diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs index b218cc68db..646e50e686 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs @@ -16,6 +16,7 @@ using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; using ICSharpCode.NRefactory.Parser; +using AST = ICSharpCode.NRefactory.Parser.AST; using CSTokens = ICSharpCode.NRefactory.Parser.CSharp.Tokens; namespace CSharpBinding @@ -115,6 +116,17 @@ namespace CSharpBinding } } } + } else if (position > 0) { + ExpressionResult result = ef.FindFullExpression(documentText, position); + + if(result.Expression != null) { + ResolveResult resolveResult = ParserService.Resolve(result, editor.ActiveTextAreaControl.Caret.Line + 1, editor.ActiveTextAreaControl.Caret.Column + 1, editor.FileName, documentText); + if (resolveResult != null && resolveResult.ResolvedType != null) { + if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) { + return true; + } + } + } } } else if (ch == ';') { LineSegment curLine = editor.Document.GetLineSegmentForOffset(cursor); @@ -157,6 +169,7 @@ namespace CSharpBinding return false; } + #region Re-show insight window void ShowInsight(SharpDevelopTextAreaControl editor, MethodInsightDataProvider dp, Stack parameters, char charTyped) { int paramCount = parameters.Count; @@ -202,10 +215,10 @@ namespace CSharpBinding } } - void ProvideContextCompletion(SharpDevelopTextAreaControl editor, IReturnType expected, char charTyped) + bool ProvideContextCompletion(SharpDevelopTextAreaControl editor, IReturnType expected, char charTyped) { IClass c = expected.GetUnderlyingClass(); - if (c == null) return; + if (c == null) return false; if (c.ClassType == ClassType.Enum) { CtrlSpaceCompletionDataProvider cdp = new CtrlSpaceCompletionDataProvider(); cdp.ForceNewExpression = true; @@ -223,11 +236,14 @@ namespace CSharpBinding } } if (cache.DefaultIndex >= 0) { - cache.InsertSpace = true; + if (charTyped != ' ') cache.InsertSpace = true; editor.ShowCompletionWindow(cache, charTyped); + return true; } } + return false; } + #endregion bool IsInComment(SharpDevelopTextAreaControl editor) { @@ -258,6 +274,8 @@ namespace CSharpBinding return true; case "new": return ShowNewCompletion(editor); + case "case": + return DoCaseCompletion(editor); default: return base.HandleKeyword(editor, word); } @@ -274,5 +292,47 @@ namespace CSharpBinding } return false; } + + #region "case"-keyword completion + bool DoCaseCompletion(SharpDevelopTextAreaControl editor) + { + ICSharpCode.TextEditor.Caret caret = editor.ActiveTextAreaControl.Caret; + NRefactoryResolver r = new NRefactoryResolver(SupportedLanguage.CSharp); + if (r.Initialize(editor.FileName, caret.Line + 1, caret.Column + 1)) { + AST.INode currentMember = r.ParseCurrentMember(editor.Text); + if (currentMember != null) { + CaseCompletionSwitchFinder ccsf = new CaseCompletionSwitchFinder(caret.Line + 1, caret.Column + 1); + currentMember.AcceptVisitor(ccsf, null); + if (ccsf.bestStatement != null) { + r.RunLookupTableVisitor(currentMember); + ResolveResult rr = r.ResolveInternal(ccsf.bestStatement.SwitchExpression, ExpressionContext.Default); + if (rr != null && rr.ResolvedType != null) { + return ProvideContextCompletion(editor, rr.ResolvedType, ' '); + } + } + } + } + return false; + } + + private class CaseCompletionSwitchFinder : AbstractAstVisitor + { + Location caretLocation; + internal AST.SwitchStatement bestStatement; + + public CaseCompletionSwitchFinder(int caretLine, int caretColumn) + { + caretLocation = new Location(caretColumn, caretLine); + } + + public override object Visit(AST.SwitchStatement switchStatement, object data) + { + if (switchStatement.StartLocation < caretLocation && caretLocation < switchStatement.EndLocation) { + bestStatement = switchStatement; + } + return base.Visit(switchStatement, data); + } + } + #endregion } } diff --git a/src/Main/Base/Project/Src/Dom/NRefactoryResolver/NRefactoryResolver.cs b/src/Main/Base/Project/Src/Dom/NRefactoryResolver/NRefactoryResolver.cs index a23bad0c33..c43edf9c48 100644 --- a/src/Main/Base/Project/Src/Dom/NRefactoryResolver/NRefactoryResolver.cs +++ b/src/Main/Base/Project/Src/Dom/NRefactoryResolver/NRefactoryResolver.cs @@ -8,6 +8,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using ICSharpCode.Core; using ICSharpCode.NRefactory.Parser; @@ -141,20 +142,14 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver return null; } - public ResolveResult Resolve(ExpressionResult expressionResult, - int caretLineNumber, - int caretColumn, - string fileName, - string fileContent) + public bool Initialize(string fileName, int caretLineNumber, int caretColumn) { - string expression = GetFixedExpression(expressionResult); - this.caretLine = caretLineNumber; this.caretColumn = caretColumn; ParseInformation parseInfo = ParserService.GetParseInformation(fileName); if (parseInfo == null) { - return null; + return false; } cu = parseInfo.MostRecentCompilationUnit; @@ -162,6 +157,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver if (cu != null) { callingClass = cu.GetInnermostClass(caretLine, caretColumn); } + return true; + } + + public ResolveResult Resolve(ExpressionResult expressionResult, + int caretLineNumber, + int caretColumn, + string fileName, + string fileContent) + { + string expression = GetFixedExpression(expressionResult); + + if (!Initialize(fileName, caretLineNumber, caretColumn)) + return null; Expression expr = null; if (language == SupportedLanguage.VBNet) { @@ -245,21 +253,49 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver } } + public INode ParseCurrentMember(string fileContent) + { + CompilationUnit cu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (cu != null && cu.Children.Count > 0) { + TypeDeclaration td = cu.Children[0] as TypeDeclaration; + if (td != null && td.Children.Count > 0) { + return td.Children[0]; + } + } + return null; + } + + public CompilationUnit ParseCurrentMemberAsCompilationUnit(string fileContent) + { + System.IO.TextReader content = ExtractCurrentMethod(fileContent); + if (content != null) { + ICSharpCode.NRefactory.Parser.IParser p = ParserFactory.CreateParser(language, content); + p.Parse(); + return p.CompilationUnit; + } else { + return null; + } + } + void RunLookupTableVisitor(string fileContent) { lookupTableVisitor = new LookupTableVisitor(languageProperties.NameComparer); callingMember = GetCurrentMember(); if (callingMember != null) { - System.IO.TextReader content = ExtractMethod(fileContent, callingMember); - if (content != null) { - ICSharpCode.NRefactory.Parser.IParser p = ParserFactory.CreateParser(language, content); - p.Parse(); - lookupTableVisitor.Visit(p.CompilationUnit, null); + CompilationUnit cu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (cu != null) { + lookupTableVisitor.Visit(cu, null); } } } + public void RunLookupTableVisitor(INode currentMemberNode) + { + lookupTableVisitor = new LookupTableVisitor(languageProperties.NameComparer); + currentMemberNode.AcceptVisitor(lookupTableVisitor, null); + } + string GetAttributeName(Expression expr) { if (expr is IdentifierExpression) { @@ -310,7 +346,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver return null; } - ResolveResult ResolveInternal(Expression expr, ExpressionContext context) + public ResolveResult ResolveInternal(Expression expr, ExpressionContext context) { TypeVisitor typeVisitor = new TypeVisitor(this); IReturnType type; @@ -459,12 +495,22 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver return ResolveMethod(type, fieldReferenceExpression.FieldName); } + public TextReader ExtractCurrentMethod(string fileContent) + { + if (callingMember == null) + callingMember = GetCurrentMember(); + if (callingMember == null) + return null; + return ExtractMethod(fileContent, callingMember, language, caretLine); + } + /// /// Creates a new class containing only the specified member. /// This is useful because we only want to parse current method for local variables, /// as all fields etc. are already prepared in the AST. /// - System.IO.TextReader ExtractMethod(string fileContent, IMember member) + public static TextReader ExtractMethod(string fileContent, IMember member, + SupportedLanguage language, int caretLine) { // As the parse information is always some seconds old, the end line could be wrong // if the user just inserted a line in the method. @@ -998,11 +1044,9 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver callingMember = GetCurrentMember(); if (callingMember != null) { - System.IO.TextReader content = ExtractMethod(fileContent, callingMember); - if (content != null) { - ICSharpCode.NRefactory.Parser.IParser p = ParserFactory.CreateParser(language, content); - p.Parse(); - lookupTableVisitor.Visit(p.CompilationUnit, null); + CompilationUnit parsedCu = ParseCurrentMemberAsCompilationUnit(fileContent); + if (cu != null) { + lookupTableVisitor.Visit(parsedCu, null); } }