From d4619878398a2935ca56d089dbef4ae89b755859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Wed, 2 Nov 2011 08:06:47 +0100 Subject: [PATCH] Improved comment & string context recognition. --- .../Completion/CSharpCompletionEngine.cs | 12 +-- .../Completion/CSharpCompletionEngineBase.cs | 90 +++++++++++++++++-- .../CSharpParameterCompletionEngine.cs | 2 +- 3 files changed, 88 insertions(+), 16 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index bb439b8c16..21479c399c 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -150,7 +150,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion // Magic key completion case ':': case '.': - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return Enumerable.Empty (); var expr = GetExpressionBeforeCursor (); if (expr == null) @@ -172,7 +172,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return CreateCompletionData (location, resolveResult.Item1, expr.Item2, resolveResult.Item2); case '#': - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return null; return GetDirectiveCompletionData (); @@ -210,7 +210,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion // Parameter completion case '(': - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return null; var invoke = GetInvocationBeforeCursor (true); if (invoke == null) @@ -237,7 +237,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion // Completion on space: case ' ': - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return null; int tokenIndex = offset; @@ -395,7 +395,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return keywordCompletion; // Automatic completion default: - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return null; if (IsInLinqContext (offset)) { tokenIndex = offset; @@ -741,7 +741,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion IEnumerable HandleKeywordCompletion (int wordStart, string word) { - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return null; switch (word) { case "using": diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs index cfc619a87e..767826ba8a 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs @@ -57,6 +57,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion protected void SetOffset (int offset) { + Reset (); + this.offset = offset; this.location = document.GetLocation (offset); @@ -65,9 +67,74 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } #region Context helper methods - protected bool IsInsideComment () + protected bool IsInsideCommentOrString () { - return IsInsideComment (offset); + var text = GetMemberTextToCaret (); + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + + for (int i = 0; i < text.Item1.Length - 1; i++) { + char ch = text.Item1[i]; + char nextCh = text.Item1[i + 1]; + + switch (ch) { + case '/': + if (inString || inChar || inVerbatimString) + break; + if (nextCh == '/') { + i++; + inSingleComment = true; + } + if (nextCh == '*') + inMultiLineComment = true; + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) + break; + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\n': + case '\r': + inSingleComment = false; + inString = false; + inChar = false; + break; + case '\\': + if (inString || inChar) + i++; + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) + break; + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) + break; + inChar = !inChar; + break; + } + } + + return inSingleComment || inString || inVerbatimString || inChar || inMultiLineComment; } protected bool IsInsideComment (int offset) @@ -83,11 +150,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return cmt != null && cmt.CommentType == CommentType.Documentation; } - protected bool IsInsideString () - { - return IsInsideString (offset); - } - protected bool IsInsideString (int offset) { @@ -240,6 +302,13 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } + string cachedText = null; + + protected virtual void Reset () + { + cachedText = null; + } + protected Tuple GetMemberTextToCaret () { int startOffset; @@ -250,9 +319,12 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } else { startOffset = 0; } - return Tuple.Create (document.GetText (startOffset, offset - startOffset), startOffset != 0); + if (cachedText == null) + cachedText = document.GetText (startOffset, offset - startOffset); + + return Tuple.Create (cachedText, startOffset != 0); } - + protected Tuple GetInvocationBeforeCursor (bool afterBracket) { CompilationUnit baseUnit; diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs index 634d758e73..6aaa6716ea 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -77,7 +77,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion char completionChar = document.GetCharAt (offset - 1); if (completionChar != '(' && completionChar != '<' && completionChar != '[') return null; - if (IsInsideComment () || IsInsideString ()) + if (IsInsideCommentOrString ()) return null; var invoke = GetInvocationBeforeCursor (true) ?? GetIndexerBeforeCursor ();