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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Parser.VB; using ICSharpCode.NRefactory.Parser.VB;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
@ -21,9 +22,9 @@ namespace ICSharpCode.VBNetBinding
{ {
public static class CompletionDataHelper 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); IResolver resolver = ParserService.CreateResolver(editor.FileName);
ParseInformation info = ParserService.GetParseInformation(editor.FileName); ParseInformation info = ParserService.GetParseInformation(editor.FileName);
@ -75,17 +76,16 @@ namespace ICSharpCode.VBNetBinding
addedKeywords = true; addedKeywords = true;
} }
result = CodeCompletionItemProvider.ConvertCompletionData(result, data, expressionResult.Context); CodeCompletionItemProvider.ConvertCompletionData(result, data, expressionResult.Context);
if (addedKeywords) if (addedKeywords)
AddTemplates(editor, result); AddTemplates(editor, result);
string word = editor.GetWordBeforeCaret().Trim(); string word = editor.GetWordBeforeCaret().Trim();
IClass c; IClass c;
IMember m = GetCurrentMember(editor);
if (contextCompletion && pressedKey == ' ') { if (contextCompletion && pressedKey == ' ') {
IMember m = GetCurrentMember(editor);
if (word.Equals("return", StringComparison.InvariantCultureIgnoreCase) && m != null) { if (word.Equals("return", StringComparison.InvariantCultureIgnoreCase) && m != null) {
c = m.ReturnType != null ? m.ReturnType.GetUnderlyingClass() : null; c = m.ReturnType != null ? m.ReturnType.GetUnderlyingClass() : null;
if (c != null) { if (c != null) {
@ -103,7 +103,16 @@ namespace ICSharpCode.VBNetBinding
c = GetCurrentClass(editor); c = GetCurrentClass(editor);
if (word.Equals("overrides", StringComparison.InvariantCultureIgnoreCase) && pressedKey == ' ' && c != null) { 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 if (pressedKey == '\0') { // ctrl+space
@ -114,6 +123,9 @@ namespace ICSharpCode.VBNetBinding
result.PreselectionLength = word.Length; result.PreselectionLength = word.Length;
} }
result.SortItems();
return result; return result;
} }
@ -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 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.VBNetBinding namespace ICSharpCode.VBNetBinding
{ {
@ -27,5 +28,17 @@ namespace ICSharpCode.VBNetBinding
return default(T); 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;
using ICSharpCode.NRefactory.Parser.VB; using ICSharpCode.NRefactory.Parser.VB;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.Refactoring;
using ICSharpCode.SharpDevelop.Dom.VBNet; using ICSharpCode.SharpDevelop.Dom.VBNet;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
@ -51,6 +52,7 @@ namespace ICSharpCode.VBNetBinding
VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName)); VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName));
ExpressionResult result; ExpressionResult result;
VBNetCompletionItemList list;
switch (ch) { switch (ch) {
case '(': case '(':
@ -61,10 +63,15 @@ namespace ICSharpCode.VBNetBinding
return CodeCompletionKeyPressResult.Completed; return CodeCompletionKeyPressResult.Completed;
} }
break; break;
case '\n':
TryDeclarationTypeInference(editor, editor.Document.GetLineForOffset(editor.Caret.Offset));
break;
case '.': case '.':
result = ef.FindExpression(editor.Document.Text, editor.Caret.Offset); result = ef.FindExpression(editor.Document.Text, editor.Caret.Offset);
LoggingService.Debug("CC: After dot, result=" + result + ", context=" + result.Context); 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; return CodeCompletionKeyPressResult.Completed;
case ' ': case ' ':
editor.Document.Insert(editor.Caret.Offset, " "); editor.Document.Insert(editor.Caret.Offset, " ");
@ -73,7 +80,9 @@ namespace ICSharpCode.VBNetBinding
string word = editor.GetWordBeforeCaret().Trim(); 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) { 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); 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; return CodeCompletionKeyPressResult.EatKey;
default: default:
@ -91,7 +100,9 @@ namespace ICSharpCode.VBNetBinding
if ((result.Context != ExpressionContext.IdentifierExpected && char.IsLetter(ch)) && if ((result.Context != ExpressionContext.IdentifierExpected && char.IsLetter(ch)) &&
(!char.IsLetterOrDigit(prevChar) && prevChar != '.')) { (!char.IsLetterOrDigit(prevChar) && prevChar != '.')) {
LoggingService.Debug("CC: Beginning to type a word, result=" + result + ", context=" + result.Context); 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; return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion;
} }
} }
@ -101,6 +112,7 @@ namespace ICSharpCode.VBNetBinding
return CodeCompletionKeyPressResult.None; return CodeCompletionKeyPressResult.None;
} }
#region Helpers
bool OperatorMayFollow(BitArray array) bool OperatorMayFollow(BitArray array)
{ {
if (array == null) if (array == null)
@ -186,6 +198,7 @@ namespace ICSharpCode.VBNetBinding
GetCommentOrStringState(editor, out inString, out inComment); GetCommentOrStringState(editor, out inString, out inComment);
return inComment; return inComment;
} }
#endregion
public bool CtrlSpace(ITextEditor editor) public bool CtrlSpace(ITextEditor editor)
{ {
@ -214,8 +227,51 @@ namespace ICSharpCode.VBNetBinding
VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName)); VBNetExpressionFinder ef = new VBNetExpressionFinder(ParserService.GetParseInformation(editor.FileName));
ExpressionResult result = ef.FindExpression(editor.Document.Text, cursor); ExpressionResult result = ef.FindExpression(editor.Document.Text, cursor);
LoggingService.Debug("CC: Beginning to type a word, result=" + result + ", context=" + result.Context); 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; 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 =
MemberVariableOrConstantDeclarator = MemberVariableOrConstantDeclarator =
[ "Const" ] [ "Const" ]
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) IdentifierForFieldDeclaration (. PopContext(); .) (. 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 ] [ "=" Expression ]
. .
@ -751,7 +751,9 @@ ExpressionRangeVariable =
. .
CollectionRangeVariable = 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 */ /* semantic action will be inserted on all paths that possibly lead to XmlLiteral */
@ -790,7 +792,7 @@ PrimitiveTypeName =
"Object" "Object"
. .
TypeName = ( "Global" | Identifier | PrimitiveTypeName ) { TypeSuffix } { "." IdentifierOrKeyword { TypeSuffix } } . TypeName = ( "Global" | Identifier | PrimitiveTypeName | "?" /* used for ? = completion */ ) { TypeSuffix } { "." IdentifierOrKeyword { TypeSuffix } } .
TypeSuffix = "(" ( "Of" [ TypeName ] { "," [ TypeName ] } | [ ArgumentList ] ) ")" . TypeSuffix = "(" ( "Of" [ TypeName ] { "," [ TypeName ] } | [ ArgumentList ] ) ")" .
@ -1024,7 +1026,7 @@ VariableDeclarationStatement =
(. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .) (. PushContext(Context.Identifier, la, t); .) (.OnEachPossiblePath: SetIdentifierExpected(la); .)
Identifier (. PopContext(); .) [ "?" ] [ ( "(" { "," } ")" ) ] Identifier (. PopContext(); .) [ "?" ] [ ( "(" { "," } ")" ) ]
} }
[ "As" ( NewExpression | (. PushContext(Context.Type, la, t); .) TypeName (. PopContext(); .) ) ] [ "As" (. PushContext(Context.Type, la, t); .) ( NewExpression | TypeName ) (. PopContext(); .) ]
[ "=" Expression ] [ "=" Expression ]
. .
@ -1114,7 +1116,9 @@ ForEachLoopStatement =
. .
ForLoopVariable = 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 = ErrorHandlingStatement =

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

@ -761,6 +761,8 @@ namespace ICSharpCode.NRefactory.Parser.VB
{ {
Location start = new Location(Col - 1, Line); Location start = new Location(Col - 1, Line);
string directive = ReadIdent('#'); string directive = ReadIdent('#');
// TODO : expression parser for PP directives
// needed for proper conversion to e. g. C#
string argument = ReadToEndOfLine(); string argument = ReadToEndOfLine();
this.specialTracker.AddPreprocessingDirective(new PreprocessingDirective(directive, argument.Trim(), start, new Location(start.Column + directive.Length + argument.Length, start.Line))); 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
End Class End Class
"; ";
const string program4 = @"
Class MainClass
Sub A
Dim a " + @"
End Sub
End Class
";
VBNetExpressionFinder ef; VBNetExpressionFinder ef;
[SetUp] [SetUp]
@ -160,34 +152,61 @@ End Class", "Test2", 5, "Test2", ExpressionContext.Default);
#endregion #endregion
#region Context Tests #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"); 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()); Assert.AreEqual(context.ToString(), er.Context.ToString());
} }
[Test] [Test]
public void ContextAfterDimIdentifierSpace() 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] [Test]
public void ContextAfterDim() 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] [Test]
public void ContextInModule() public void ContextInModule()
{ {
ContextTest(@"Module Test string prg = @"Module Test
|
End Module", @"Module Test End Module";
", @"Module Test
".Length, ExpressionContext.TypeDeclaration); ContextTest(prg, ExpressionContext.TypeDeclaration);
} }
#endregion #endregion

Loading…
Cancel
Save