Browse Source

- reimplemented TryTypeInference feature as CC item "? ="

- fixed bugs with expressions expecting Type context.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/vbnet@6184 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Siegfried Pammer 16 years ago
parent
commit
0d04541240
  1. 39
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/CompletionDataHelper.cs
  2. 13
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/Extensions.cs
  3. 64
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs
  4. 14
      src/Libraries/NRefactory/Project/Src/Lexer/VBNet/ExpressionFinder.atg
  5. 2
      src/Libraries/NRefactory/Project/Src/Lexer/VBNet/Lexer.cs
  6. 2518
      src/Libraries/NRefactory/Project/Src/Lexer/VBNet/Parser.cs
  7. 55
      src/Main/Base/Test/VBExpressionFinderTests.cs

39
src/AddIns/BackendBindings/VBNetBinding/Project/Src/CompletionDataHelper.cs

@ -10,6 +10,7 @@ using System.Collections; @@ -10,6 +10,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Parser.VB;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
@ -21,9 +22,9 @@ namespace ICSharpCode.VBNetBinding @@ -21,9 +22,9 @@ namespace ICSharpCode.VBNetBinding
{
public static class CompletionDataHelper
{
public static ICompletionItemList GenerateCompletionData(this ExpressionResult expressionResult, ITextEditor editor, char pressedKey)
public static VBNetCompletionItemList GenerateCompletionData(this ExpressionResult expressionResult, ITextEditor editor, char pressedKey)
{
DefaultCompletionItemList result = new NRefactoryCompletionItemList();
VBNetCompletionItemList result = new VBNetCompletionItemList();
IResolver resolver = ParserService.CreateResolver(editor.FileName);
ParseInformation info = ParserService.GetParseInformation(editor.FileName);
@ -75,17 +76,16 @@ namespace ICSharpCode.VBNetBinding @@ -75,17 +76,16 @@ namespace ICSharpCode.VBNetBinding
addedKeywords = true;
}
result = CodeCompletionItemProvider.ConvertCompletionData(result, data, expressionResult.Context);
CodeCompletionItemProvider.ConvertCompletionData(result, data, expressionResult.Context);
if (addedKeywords)
AddTemplates(editor, result);
string word = editor.GetWordBeforeCaret().Trim();
IClass c;
IMember m = GetCurrentMember(editor);
if (contextCompletion && pressedKey == ' ') {
IMember m = GetCurrentMember(editor);
if (word.Equals("return", StringComparison.InvariantCultureIgnoreCase) && m != null) {
c = m.ReturnType != null ? m.ReturnType.GetUnderlyingClass() : null;
if (c != null) {
@ -103,7 +103,16 @@ namespace ICSharpCode.VBNetBinding @@ -103,7 +103,16 @@ namespace ICSharpCode.VBNetBinding
c = GetCurrentClass(editor);
if (word.Equals("overrides", StringComparison.InvariantCultureIgnoreCase) && pressedKey == ' ' && c != null) {
return new OverrideCompletionItemProvider().GenerateCompletionList(editor);
return new OverrideCompletionItemProvider().GenerateCompletionList(editor).ToVBCCList();;
}
if (expressionResult.Context == ExpressionContext.Type && m != null && m.BodyRegion.IsInside(editor.Caret.Line, editor.Caret.Column)) {
result.Items.Add(
new DefaultCompletionItem("? =") {
Image = ClassBrowserIconService.GotoArrow,
Description = StringParser.Parse("${res:AddIns.VBNetBinding.CodeCompletion.QuestionmarkEqualsItem.Description}")
}
);
}
if (pressedKey == '\0') { // ctrl+space
@ -114,6 +123,9 @@ namespace ICSharpCode.VBNetBinding @@ -114,6 +123,9 @@ namespace ICSharpCode.VBNetBinding
result.PreselectionLength = word.Length;
}
result.SortItems();
return result;
}
@ -158,4 +170,19 @@ namespace ICSharpCode.VBNetBinding @@ -158,4 +170,19 @@ namespace ICSharpCode.VBNetBinding
}
}
}
public class VBNetCompletionItemList : NRefactoryCompletionItemList
{
public ITextEditor Editor { get; set; }
public ICompletionListWindow Window { get; set; }
public override CompletionItemListKeyResult ProcessInput(char key)
{
if (key == '?' && string.IsNullOrWhiteSpace(Editor.Document.GetText(Window.StartOffset, Window.EndOffset - Window.StartOffset)))
return CompletionItemListKeyResult.NormalKey;
return base.ProcessInput(key);
}
}
}

13
src/AddIns/BackendBindings/VBNetBinding/Project/Src/Extensions.cs

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.VBNetBinding
{
@ -27,5 +28,17 @@ namespace ICSharpCode.VBNetBinding @@ -27,5 +28,17 @@ namespace ICSharpCode.VBNetBinding
return default(T);
}
internal static VBNetCompletionItemList ToVBCCList(this ICompletionItemList list)
{
var result = new VBNetCompletionItemList() {
SuggestedItem = list.SuggestedItem,
PreselectionLength = list.PreselectionLength
};
result.Items.AddRange(list.Items);
return result;
}
}
}

64
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs

@ -15,6 +15,7 @@ using ICSharpCode.NRefactory.Parser; @@ -15,6 +15,7 @@ using ICSharpCode.NRefactory.Parser;
using ICSharpCode.NRefactory.Parser.VB;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.Refactoring;
using ICSharpCode.SharpDevelop.Dom.VBNet;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
@ -51,6 +52,7 @@ namespace ICSharpCode.VBNetBinding @@ -51,6 +52,7 @@ namespace ICSharpCode.VBNetBinding
VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName));
ExpressionResult result;
VBNetCompletionItemList list;
switch (ch) {
case '(':
@ -61,10 +63,15 @@ namespace ICSharpCode.VBNetBinding @@ -61,10 +63,15 @@ namespace ICSharpCode.VBNetBinding
return CodeCompletionKeyPressResult.Completed;
}
break;
case '\n':
TryDeclarationTypeInference(editor, editor.Document.GetLineForOffset(editor.Caret.Offset));
break;
case '.':
result = ef.FindExpression(editor.Document.Text, editor.Caret.Offset);
LoggingService.Debug("CC: After dot, result=" + result + ", context=" + result.Context);
editor.ShowCompletionWindow(CompletionDataHelper.GenerateCompletionData(result, editor, ch));
list = CompletionDataHelper.GenerateCompletionData(result, editor, ch);
list.Editor = editor;
list.Window = editor.ShowCompletionWindow(list);
return CodeCompletionKeyPressResult.Completed;
case ' ':
editor.Document.Insert(editor.Caret.Offset, " ");
@ -73,7 +80,9 @@ namespace ICSharpCode.VBNetBinding @@ -73,7 +80,9 @@ namespace ICSharpCode.VBNetBinding
string word = editor.GetWordBeforeCaret().Trim();
if (word.Equals("overrides", StringComparison.InvariantCultureIgnoreCase) || word.Equals("return", StringComparison.InvariantCultureIgnoreCase) || !LiteralMayFollow((BitArray)result.Tag) && !OperatorMayFollow((BitArray)result.Tag) && ExpressionContext.IdentifierExpected != result.Context) {
LoggingService.Debug("CC: After space, result=" + result + ", context=" + result.Context);
editor.ShowCompletionWindow(CompletionDataHelper.GenerateCompletionData(result, editor, ch));
list = CompletionDataHelper.GenerateCompletionData(result, editor, ch);
list.Editor = editor;
list.Window = editor.ShowCompletionWindow(list);
}
return CodeCompletionKeyPressResult.EatKey;
default:
@ -91,7 +100,9 @@ namespace ICSharpCode.VBNetBinding @@ -91,7 +100,9 @@ namespace ICSharpCode.VBNetBinding
if ((result.Context != ExpressionContext.IdentifierExpected && char.IsLetter(ch)) &&
(!char.IsLetterOrDigit(prevChar) && prevChar != '.')) {
LoggingService.Debug("CC: Beginning to type a word, result=" + result + ", context=" + result.Context);
editor.ShowCompletionWindow(CompletionDataHelper.GenerateCompletionData(result, editor, ch));
list = CompletionDataHelper.GenerateCompletionData(result, editor, ch);
list.Editor = editor;
list.Window = editor.ShowCompletionWindow(list);
return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion;
}
}
@ -101,6 +112,7 @@ namespace ICSharpCode.VBNetBinding @@ -101,6 +112,7 @@ namespace ICSharpCode.VBNetBinding
return CodeCompletionKeyPressResult.None;
}
#region Helpers
bool OperatorMayFollow(BitArray array)
{
if (array == null)
@ -186,6 +198,7 @@ namespace ICSharpCode.VBNetBinding @@ -186,6 +198,7 @@ namespace ICSharpCode.VBNetBinding
GetCommentOrStringState(editor, out inString, out inComment);
return inComment;
}
#endregion
public bool CtrlSpace(ITextEditor editor)
{
@ -214,8 +227,51 @@ namespace ICSharpCode.VBNetBinding @@ -214,8 +227,51 @@ namespace ICSharpCode.VBNetBinding
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);
editor.ShowCompletionWindow(CompletionDataHelper.GenerateCompletionData(result, editor, '\0'));
var list = CompletionDataHelper.GenerateCompletionData(result, editor, '\0');
list.Editor = editor;
list.Window = editor.ShowCompletionWindow(list);
return true;
}
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;
}
}
}

14
src/Libraries/NRefactory/Project/Src/Lexer/VBNet/ExpressionFinder.atg

@ -470,7 +470,7 @@ MemberVariableOrConstantDeclaration = @@ -470,7 +470,7 @@ MemberVariableOrConstantDeclaration =
MemberVariableOrConstantDeclarator =
[ "Const" ]
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) IdentifierForFieldDeclaration (. PopContext(); .)
[ "As" ( NewExpression | (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ) ]
[ "As" (. PushContext(Context.Type, la, t); .) ( NewExpression | TypeName ) (. PopContext(); .) ]
[ "=" Expression ]
.
@ -751,7 +751,9 @@ ExpressionRangeVariable = @@ -751,7 +751,9 @@ ExpressionRangeVariable =
.
CollectionRangeVariable =
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) Identifier (. PopContext(); .) [ "As" (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ] "In" Expression
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) Identifier (. PopContext(); .)
[ "As" (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ]
"In" Expression
.
/* semantic action will be inserted on all paths that possibly lead to XmlLiteral */
@ -790,7 +792,7 @@ PrimitiveTypeName = @@ -790,7 +792,7 @@ PrimitiveTypeName =
"Object"
.
TypeName = ( "Global" | Identifier | PrimitiveTypeName ) { TypeSuffix } { "." IdentifierOrKeyword { TypeSuffix } } .
TypeName = ( "Global" | Identifier | PrimitiveTypeName | "?" /* used for ? = completion */ ) { TypeSuffix } { "." IdentifierOrKeyword { TypeSuffix } } .
TypeSuffix = "(" ( "Of" [ TypeName ] { "," [ TypeName ] } | [ ArgumentList ] ) ")" .
@ -1024,7 +1026,7 @@ VariableDeclarationStatement = @@ -1024,7 +1026,7 @@ VariableDeclarationStatement =
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .)
Identifier (. PopContext(); .) [ "?" ] [ ( "(" { "," } ")" ) ]
}
[ "As" ( NewExpression | (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ) ]
[ "As" (. PushContext(Context.Type, la, t); .) ( NewExpression | TypeName ) (. PopContext(); .) ]
[ "=" Expression ]
.
@ -1114,7 +1116,9 @@ ForEachLoopStatement = @@ -1114,7 +1116,9 @@ ForEachLoopStatement =
.
ForLoopVariable =
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) SimpleExpression (. PopContext(); .) [ "?" ] { ExpressionSuffix } [ "As" (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ]
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) SimpleExpression (. PopContext(); .)
[ "?" ] { ExpressionSuffix }
[ "As" (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ]
.
ErrorHandlingStatement =

2
src/Libraries/NRefactory/Project/Src/Lexer/VBNet/Lexer.cs

@ -761,6 +761,8 @@ namespace ICSharpCode.NRefactory.Parser.VB @@ -761,6 +761,8 @@ namespace ICSharpCode.NRefactory.Parser.VB
{
Location start = new Location(Col - 1, Line);
string directive = ReadIdent('#');
// TODO : expression parser for PP directives
// needed for proper conversion to e. g. C#
string argument = ReadToEndOfLine();
this.specialTracker.AddPreprocessingDirective(new PreprocessingDirective(directive, argument.Trim(), start, new Location(start.Column + directive.Length + argument.Length, start.Line)));
}

2518
src/Libraries/NRefactory/Project/Src/Lexer/VBNet/Parser.cs

File diff suppressed because it is too large Load Diff

55
src/Main/Base/Test/VBExpressionFinderTests.cs

@ -49,14 +49,6 @@ Class MainClass @@ -49,14 +49,6 @@ Class MainClass
End Class
";
const string program4 = @"
Class MainClass
Sub A
Dim a " + @"
End Sub
End Class
";
VBNetExpressionFinder ef;
[SetUp]
@ -160,34 +152,61 @@ End Class", "Test2", 5, "Test2", ExpressionContext.Default); @@ -160,34 +152,61 @@ End Class", "Test2", 5, "Test2", ExpressionContext.Default);
#endregion
#region Context Tests
void ContextTest(string program, string location, int offset, ExpressionContext context)
void ContextTest(string program, ExpressionContext context)
{
int pos = program.IndexOf(location);
int pos = program.IndexOf("|");
if (pos < 0) Assert.Fail("location not found in program");
ExpressionResult er = ef.FindExpression(program, pos + offset);
program = program.Remove(pos, 1);
ExpressionResult er = ef.FindExpression(program, pos);
Assert.AreEqual(context.ToString(), er.Context.ToString());
}
[Test]
public void ContextAfterDimIdentifierSpace()
{
ContextTest(program4, " a ", 3, ExpressionContext.MethodBody);
string program4 = @"
Class MainClass
Sub A
Dim a |
End Sub
End Class
";
ContextTest(program4, ExpressionContext.MethodBody);
}
[Test]
public void ContextAfterDimIdentifierAs()
{
string prg = @"Module Test
Sub Test()
Dim x As |
End Sub
End Module";
ContextTest(prg, ExpressionContext.Type);
}
[Test]
public void ContextAfterDim()
{
ContextTest(program4, "Dim ", "Dim".Length, ExpressionContext.MethodBody);
string program4 = @"
Class MainClass
Sub A
Dim |
End Sub
End Class
";
ContextTest(program4, ExpressionContext.IdentifierExpected);
}
[Test]
public void ContextInModule()
{
ContextTest(@"Module Test
End Module", @"Module Test
", @"Module Test
".Length, ExpressionContext.TypeDeclaration);
string prg = @"Module Test
|
End Module";
ContextTest(prg, ExpressionContext.TypeDeclaration);
}
#endregion

Loading…
Cancel
Save