You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
11 KiB
314 lines
11 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using System.Linq; |
|
|
|
using ICSharpCode.Core; |
|
using ICSharpCode.NRefactory; |
|
using ICSharpCode.NRefactory.Parser; |
|
using ICSharpCode.NRefactory.Parser.VB; |
|
using ICSharpCode.SharpDevelop; |
|
using ICSharpCode.SharpDevelop.Dom; |
|
using Dom = ICSharpCode.SharpDevelop.Dom; |
|
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; |
|
using ICSharpCode.SharpDevelop.Dom.Refactoring; |
|
using ICSharpCode.SharpDevelop.Dom.VBNet; |
|
using ICSharpCode.SharpDevelop.Editor; |
|
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; |
|
|
|
namespace ICSharpCode.VBNetBinding |
|
{ |
|
public class VBNetCompletionBinding : ICodeCompletionBinding |
|
{ |
|
static VBNetCompletionBinding instance; |
|
|
|
public static VBNetCompletionBinding Instance { |
|
get { |
|
if (instance == null) |
|
instance = new VBNetCompletionBinding(); |
|
return instance; |
|
} |
|
} |
|
|
|
NRefactoryInsightWindowHandler insightHandler = new NRefactoryInsightWindowHandler(SupportedLanguage.VBNet); |
|
|
|
public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) |
|
{ |
|
if (LanguageUtils.IsInsideDocumentationComment(editor) && ch == '<') { |
|
new CommentCompletionItemProvider().ShowCompletion(editor); |
|
return CodeCompletionKeyPressResult.Completed; |
|
} |
|
|
|
if (IsInComment(editor) || IsInString(editor)) |
|
return CodeCompletionKeyPressResult.None; |
|
|
|
if (editor.SelectionLength > 0) { |
|
// allow code completion when overwriting an identifier |
|
int endOffset = editor.SelectionStart + editor.SelectionLength; |
|
// but block code completion when overwriting only part of an identifier |
|
if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset))) |
|
return CodeCompletionKeyPressResult.None; |
|
|
|
editor.Document.Remove(editor.SelectionStart, editor.SelectionLength); |
|
} |
|
|
|
VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName)); |
|
|
|
ExpressionResult result; |
|
|
|
switch (ch) { |
|
case '(': |
|
if (CodeCompletionOptions.InsightEnabled) { |
|
IInsightWindow insightWindow = editor.ShowInsightWindow(new MethodInsightProvider().ProvideInsight(editor)); |
|
if (insightWindow != null) { |
|
insightHandler.InitializeOpenedInsightWindow(editor, insightWindow); |
|
insightHandler.HighlightParameter(insightWindow, 0); |
|
} |
|
return CodeCompletionKeyPressResult.Completed; |
|
} |
|
break; |
|
case ',': |
|
if (CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { |
|
IInsightWindow insightWindow; |
|
if (insightHandler.InsightRefreshOnComma(editor, ch, out insightWindow)) |
|
return CodeCompletionKeyPressResult.Completed; |
|
} |
|
break; |
|
case '\n': |
|
TryDeclarationTypeInference(editor, editor.Document.GetLineForOffset(editor.Caret.Offset)); |
|
break; |
|
case '.': |
|
string w = editor.GetWordBeforeCaret(); int index = w.IndexOf('.'); |
|
|
|
if (index > -1 && w.Length - index == 2) |
|
index = editor.Caret.Offset - 2; |
|
else |
|
index = editor.Caret.Offset; |
|
|
|
result = ef.FindExpression(editor.Document.Text, index); |
|
LoggingService.Debug("CC: After dot, result=" + result + ", context=" + result.Context); |
|
if (ShowCompletion(result, editor, ch)) |
|
return CodeCompletionKeyPressResult.Completed; |
|
else |
|
return CodeCompletionKeyPressResult.None; |
|
case '@': |
|
if (editor.Caret.Offset > 0 && editor.Document.GetCharAt(editor.Caret.Offset - 1) == '.') |
|
return CodeCompletionKeyPressResult.None; |
|
goto default; |
|
case ' ': |
|
editor.Document.Insert(editor.Caret.Offset, " "); |
|
result = ef.FindExpression(editor.Document.Text, editor.Caret.Offset); |
|
|
|
string word = editor.GetWordBeforeCaret().Trim(); |
|
if (word.Equals("overrides", StringComparison.OrdinalIgnoreCase) || word.Equals("return", StringComparison.OrdinalIgnoreCase) || !LiteralMayFollow((BitArray)result.Tag) && !OperatorMayFollow((BitArray)result.Tag) && ExpressionContext.IdentifierExpected != result.Context) { |
|
LoggingService.Debug("CC: After space, result=" + result + ", context=" + result.Context); |
|
ShowCompletion(result, editor, ch); |
|
} |
|
return CodeCompletionKeyPressResult.EatKey; |
|
default: |
|
if (CodeCompletionOptions.CompleteWhenTyping) { |
|
int cursor = editor.Caret.Offset; |
|
char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; |
|
char ppChar = cursor > 2 ? editor.Document.GetCharAt(cursor - 2) : ' '; |
|
|
|
result = ef.FindExpression(editor.Document.Text, cursor); |
|
|
|
if ((result.Context != ExpressionContext.IdentifierExpected && char.IsLetter(ch)) && |
|
(!char.IsLetterOrDigit(prevChar) && prevChar != '.')) { |
|
if (prevChar == '@' && ppChar == '.') |
|
return CodeCompletionKeyPressResult.None; |
|
if (IsTypeCharacter(ch, prevChar)) |
|
return CodeCompletionKeyPressResult.None; |
|
if (prevChar == '_') { |
|
result.Expression = '_' + result.Expression; |
|
result.Region = new DomRegion(result.Region.BeginLine, result.Region.BeginColumn - 1, result.Region.EndLine, result.Region.EndColumn); |
|
} |
|
LoggingService.Debug("CC: Beginning to type a word, result=" + result + ", context=" + result.Context); |
|
ShowCompletion(result, editor, ch); |
|
return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion; |
|
} |
|
} |
|
break; |
|
} |
|
|
|
return CodeCompletionKeyPressResult.None; |
|
} |
|
|
|
static bool IsTypeCharacter(char ch, char prevChar) |
|
{ |
|
ch = char.ToUpperInvariant(ch); |
|
|
|
// char type character |
|
if (ch == 'C' && prevChar == '"') |
|
return true; |
|
|
|
// start of hex or octal literal |
|
if (prevChar == '&' && (ch == 'H' || ch == 'O')) |
|
return true; |
|
|
|
if (char.IsDigit(prevChar)) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
static bool ShowCompletion(ExpressionResult result, ITextEditor editor, char ch) |
|
{ |
|
VBNetCompletionItemList list = CompletionDataHelper.GenerateCompletionData(result, editor, ch); |
|
list.Editor = editor; |
|
list.Window = editor.ShowCompletionWindow(list); |
|
return list.Items.Any(); |
|
} |
|
|
|
#region Helpers |
|
static bool OperatorMayFollow(BitArray array) |
|
{ |
|
if (array == null) |
|
return false; |
|
|
|
return array[Tokens.Xor]; |
|
} |
|
|
|
static bool LiteralMayFollow(BitArray array) |
|
{ |
|
if (array == null) |
|
return false; |
|
|
|
for (int i = 0; i < array.Length; i++) { |
|
if (array[i] && i >= Tokens.LiteralString && i <= Tokens.LiteralDate) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
static void GetCommentOrStringState(ITextEditor editor, out bool inString, out bool inComment) |
|
{ |
|
ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, editor.Document.CreateReader()); |
|
|
|
Token t = lexer.NextToken(); |
|
bool inXml = false; |
|
|
|
inString = false; |
|
inComment = false; |
|
|
|
while (t.Location < editor.Caret.Position) { |
|
t = lexer.NextToken(); |
|
|
|
if (inXml && (t.Kind != Tokens.Identifier && t.Kind != Tokens.LiteralString && !(t.Kind > Tokens.LiteralDate && t.Kind < Tokens.Colon))) { |
|
inXml = false; |
|
continue; |
|
} |
|
|
|
if (t.Kind > Tokens.LiteralDate && t.Kind < Tokens.Assign) |
|
inXml = true; |
|
} |
|
|
|
if (inXml) { |
|
// TODO |
|
} else { |
|
string lineText = editor.Document.GetLine(editor.Caret.Line).Text; |
|
|
|
for (int i = 0; i < editor.Caret.Column - 1; i++) { |
|
char ch = lineText[i]; |
|
|
|
if (!inComment && ch == '"') |
|
inString = !inString; |
|
if (!inString && ch == '\'') |
|
inComment = true; |
|
} |
|
} |
|
} |
|
|
|
static bool IsInString(ITextEditor editor) |
|
{ |
|
bool inString, inComment; |
|
GetCommentOrStringState(editor, out inString, out inComment); |
|
return inString; |
|
} |
|
|
|
static bool IsInComment(ITextEditor editor) |
|
{ |
|
bool inString, inComment; |
|
GetCommentOrStringState(editor, out inString, out inComment); |
|
return inComment; |
|
} |
|
#endregion |
|
|
|
public bool CtrlSpace(ITextEditor editor) |
|
{ |
|
if (IsInComment(editor)) |
|
return false; |
|
|
|
if (editor.SelectionLength > 0) { |
|
// allow code completion when overwriting an identifier |
|
int endOffset = editor.SelectionStart + editor.SelectionLength; |
|
// but block code completion when overwriting only part of an identifier |
|
if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset))) |
|
return false; |
|
|
|
editor.Document.Remove(editor.SelectionStart, editor.SelectionLength); |
|
} |
|
|
|
int cursor = editor.Caret.Offset; |
|
char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; |
|
bool afterUnderscore = prevChar == '_'; |
|
|
|
if (afterUnderscore) { |
|
cursor--; |
|
prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; |
|
} |
|
|
|
VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName)); |
|
ExpressionResult result = ef.FindExpression(editor.Document.Text, cursor); |
|
LoggingService.Debug("CC: Beginning to type a word, result=" + result + ", context=" + result.Context); |
|
ShowCompletion(result, editor, '\0'); |
|
return true; |
|
} |
|
|
|
static bool TryDeclarationTypeInference(ITextEditor editor, IDocumentLine curLine) |
|
{ |
|
string lineText = editor.Document.GetText(curLine.Offset, curLine.Length); |
|
ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new System.IO.StringReader(lineText)); |
|
if (lexer.NextToken().Kind != Tokens.Dim) |
|
return false; |
|
if (lexer.NextToken().Kind != Tokens.Identifier) |
|
return false; |
|
if (lexer.NextToken().Kind != Tokens.As) |
|
return false; |
|
Token t1 = lexer.NextToken(); |
|
if (t1.Kind != Tokens.QuestionMark) |
|
return false; |
|
Token t2 = lexer.NextToken(); |
|
if (t2.Kind != Tokens.Assign) |
|
return false; |
|
string expr = lineText.Substring(t2.Location.Column); |
|
LoggingService.Debug("DeclarationTypeInference: >" + expr + "<"); |
|
ResolveResult rr = ParserService.Resolve(new ExpressionResult(expr), |
|
editor.Caret.Line, |
|
t2.Location.Column, editor.FileName, |
|
editor.Document.Text); |
|
if (rr != null && rr.ResolvedType != null) { |
|
ClassFinder context = new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.Caret.Line, t1.Location.Column); |
|
VBNetAmbience ambience = new VBNetAmbience(); |
|
if (CodeGenerator.CanUseShortTypeName(rr.ResolvedType, context)) |
|
ambience.ConversionFlags = ConversionFlags.None; |
|
else |
|
ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedTypeNames; |
|
string typeName = ambience.Convert(rr.ResolvedType); |
|
using (editor.Document.OpenUndoGroup()) { |
|
int offset = curLine.Offset + t1.Location.Column - 1; |
|
editor.Document.Remove(offset, 1); |
|
editor.Document.Insert(offset, typeName); |
|
} |
|
editor.Caret.Column += typeName.Length - 1; |
|
return true; |
|
} |
|
return false; |
|
} |
|
} |
|
}
|
|
|