diff options
author | Mike Krüger <mkrueger@xamarin.com> | 2011-10-31 13:04:15 +0400 |
---|---|---|
committer | Mike Krüger <mkrueger@xamarin.com> | 2011-10-31 13:04:15 +0400 |
commit | 1a8ebc7ac8f7d7da099fdca15695d256bc0550a0 (patch) | |
tree | a3ce7750b8b580d1f938a4477a8d4c90e9fa9c4c /main/contrib | |
parent | a16d61344ef5e1b70354f28c7399c18a25b41263 (diff) |
Fixed "Bug 1744 - [New Resolver] Issues while typing a property".
Diffstat (limited to 'main/contrib')
-rw-r--r-- | main/contrib/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs | 3773 |
1 files changed, 1907 insertions, 1866 deletions
diff --git a/main/contrib/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/main/contrib/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 69548ed5bb..c8990cb6cd 100644 --- a/main/contrib/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/main/contrib/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -1,1866 +1,1907 @@ -// -// CSharpCompletionEngine.cs -// -// Author: -// Mike Krüger <mkrueger@xamarin.com> -// -// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using ICSharpCode.NRefactory.Completion; -using ICSharpCode.NRefactory.CSharp.Refactoring; -using ICSharpCode.NRefactory.CSharp.Resolver; -using ICSharpCode.NRefactory.Editor; -using ICSharpCode.NRefactory.Semantics; -using ICSharpCode.NRefactory.TypeSystem; - -namespace ICSharpCode.NRefactory.CSharp.Completion -{ - public class CSharpCompletionEngine : CSharpCompletionEngineBase - { - internal ICompletionDataFactory factory; - - #region Additional input properties - public CSharpFormattingOptions FormattingPolicy { get; set; } - public string EolMarker { get; set; } - public string IndentString { get; set; } - #endregion - - #region Result properties - public bool AutoCompleteEmptyMatch; - public bool AutoSelect; - public string DefaultCompletionString; - #endregion - - public CSharpCompletionEngine (IDocument document, ICompletionDataFactory factory) - { - this.document = document; - this.factory = factory; - } - - public IEnumerable<ICompletionData> GetCompletionData (int offset, bool controlSpace) - { - this.AutoCompleteEmptyMatch = true; - this.AutoSelect = true; - this.DefaultCompletionString = null; - SetOffset (offset); - if (offset > 0) { - char lastChar = document.GetCharAt (offset - 1); - var result = MagicKeyCompletion (lastChar, controlSpace) ?? Enumerable.Empty<ICompletionData> (); - if (controlSpace && char.IsWhiteSpace (lastChar)) { - offset -= 2; - while (offset >= 0 && char.IsWhiteSpace (document.GetCharAt (offset))) - offset--; - if (offset > 0) { - var nonWsResult = MagicKeyCompletion (document.GetCharAt (offset), controlSpace); - if (nonWsResult != null) - result = result.Concat (nonWsResult); - } - } - - return result; - } - return Enumerable.Empty<ICompletionData> (); - } - - IEnumerable<ICompletionData> MagicKeyCompletion (char completionChar, bool controlSpace) - { - switch (completionChar) { - // Magic key completion - case ':': - case '.': - if (IsInsideComment () || IsInsideString ()) - return Enumerable.Empty<ICompletionData> (); - var expr = GetExpressionBeforeCursor (); - if (expr == null) - return null; - // do not complete <number>. (but <number>.<number>.) - if (expr.Item2 is PrimitiveExpression) { - var pexpr = (PrimitiveExpression)expr.Item2; - if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains ('.')) - return null; - } - - - var resolveResult = ResolveExpression (expr.Item1, expr.Item2, expr.Item3); - - if (resolveResult == null) - return null; - if (expr.Item2 is AstType) - return CreateTypeAndNamespaceCompletionData (location, resolveResult.Item1, expr.Item2, resolveResult.Item2); - - return CreateCompletionData (location, resolveResult.Item1, expr.Item2, resolveResult.Item2); - case '#': - if (IsInsideComment () || IsInsideString ()) - return null; - return GetDirectiveCompletionData (); - -// XML doc completion - case '<': - if (IsInsideDocComment ()) - return GetXmlDocumentationCompletionData (); - if (controlSpace) - return DefaultControlSpaceItems (); - return null; - case '>': - if (!IsInsideDocComment ()) - return null; - string lineText = document.GetText (document.GetLineByNumber (location.Line)); - int startIndex = Math.Min (location.Column - 1, lineText.Length - 1); - - while (startIndex >= 0 && lineText [startIndex] != '<') { - --startIndex; - if (lineText [startIndex] == '/') { // already closed. - startIndex = -1; - break; - } - } - - if (startIndex >= 0) { - int endIndex = startIndex; - while (endIndex <= location.Column && endIndex < lineText.Length && !Char.IsWhiteSpace (lineText [endIndex])) { - endIndex++; - } - string tag = endIndex - startIndex - 1 > 0 ? lineText.Substring (startIndex + 1, endIndex - startIndex - 2) : null; - if (!string.IsNullOrEmpty (tag) && commentTags.IndexOf (tag) >= 0) - document.Insert (offset, "</" + tag + ">"); - } - return null; - - // Parameter completion - case '(': - if (IsInsideComment () || IsInsideString ()) - return null; - var invoke = GetInvocationBeforeCursor (true); - if (invoke == null) - return null; - if (invoke.Item2 is TypeOfExpression) - return CreateTypeList (); - var invocationResult = ResolveExpression (invoke.Item1, invoke.Item2, invoke.Item3); - if (invocationResult == null) - return null; - var methodGroup = invocationResult.Item1 as MethodGroupResolveResult; - if (methodGroup != null) - return CreateParameterCompletion (methodGroup, invocationResult.Item2, invoke.Item2, 0, controlSpace); - return null; - case ',': - int cpos2; - if (!GetParameterCompletionCommandOffset (out cpos2)) - return null; - // completionContext = CompletionWidget.CreateCodeCompletionContext (cpos2); - // int currentParameter2 = MethodParameterDataProvider.GetCurrentParameterIndex (CompletionWidget, completionContext) - 1; -// return CreateParameterCompletion (CreateResolver (), location, ExpressionContext.MethodBody, provider.Methods, currentParameter); - break; - - // Completion on space: - case ' ': - if (IsInsideComment () || IsInsideString ()) - return null; - - int tokenIndex = offset; - string token = GetPreviousToken (ref tokenIndex, false); - - // check propose name, for context <variable name> <ctrl+space> (but only in control space context) - IType isAsType = null; - var isAsExpression = GetExpressionAt (offset); - if (controlSpace && isAsExpression != null && isAsExpression.Item2 is VariableDeclarationStatement && token != "new") { - var parent = isAsExpression.Item2 as VariableDeclarationStatement; - int offset1 = document.GetOffset (parent.Type.StartLocation); - int offset2 = document.GetOffset (parent.Type.EndLocation); - - string name = document.GetText (offset1, offset2 - offset1); - var names = new List<string> (); - int lastNameStart = 0; - for (int i = 1; i < name.Length; i++) { - if (Char.IsUpper (name [i])) { - names.Add (name.Substring (lastNameStart, i - lastNameStart)); - lastNameStart = i; - } - } - names.Add (name.Substring (lastNameStart, name.Length - lastNameStart)); - var proposeNameList = new CompletionDataWrapper (this); - var possibleName = new StringBuilder (); - for (int i = 0; i < names.Count; i++) { - possibleName.Length = 0; - for (int j = i; j < names.Count; j++) { - if (string.IsNullOrEmpty (names [j])) - continue; - if (j == i) - names [j] = Char.ToLower (names [j] [0]) + names [j].Substring (1); - possibleName.Append (names [j]); - } - if (possibleName.Length > 0) - proposeNameList.Result.Add (factory.CreateLiteralCompletionData (possibleName.ToString ())); - } - - AutoSelect = false; - AutoCompleteEmptyMatch = false; - return proposeNameList.Result; - } -// int tokenIndex = offset; -// string token = GetPreviousToken (ref tokenIndex, false); -// if (result.ExpressionContext == ExpressionContext.ObjectInitializer) { -// resolver = CreateResolver (); -// ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForObjectInitializer (document, resolver.Unit, Document.FileName, resolver.CallingType); -// IReturnType objectInitializer = ((ExpressionContext.TypeExpressionContext)exactContext).UnresolvedType; -// if (objectInitializer != null && objectInitializer.ArrayDimensions == 0 && objectInitializer.PointerNestingLevel == 0 && (token == "{" || token == ",")) -// return CreateCtrlSpaceCompletionData (completionContext, result); -// } - if (token == "=") { - int j = tokenIndex; - string prevToken = GetPreviousToken (ref j, false); - if (prevToken == "=" || prevToken == "+" || prevToken == "-") { - token = prevToken + token; - tokenIndex = j; - } - } - switch (token) { - case "(": - case ",": - int cpos; - if (!GetParameterCompletionCommandOffset (out cpos)) - break; - int currentParameter = GetCurrentParameterIndex (cpos, 0) - 1; - if (currentParameter < 0) - return null; - invoke = GetInvocationBeforeCursor (token == "("); - if (invoke == null) - return null; - invocationResult = ResolveExpression (invoke.Item1, invoke.Item2, invoke.Item3); - if (invocationResult == null) - return null; - methodGroup = invocationResult.Item1 as MethodGroupResolveResult; - if (methodGroup != null) - return CreateParameterCompletion (methodGroup, invocationResult.Item2, invoke.Item2, currentParameter, controlSpace); - return null; - case "=": - case "==": - GetPreviousToken (ref tokenIndex, false); - - var expressionOrVariableDeclaration = GetExpressionAt (tokenIndex); - if (expressionOrVariableDeclaration == null) - return null; - - resolveResult = ResolveExpression (expressionOrVariableDeclaration.Item1, expressionOrVariableDeclaration.Item2, expressionOrVariableDeclaration.Item3); - if (resolveResult == null) - return null; - - if (resolveResult.Item1.Type.Kind == TypeKind.Enum) { - var wrapper = new CompletionDataWrapper (this); - AddContextCompletion (wrapper, resolveResult.Item2, expressionOrVariableDeclaration.Item2); - AddEnumMembers (wrapper, resolveResult.Item1.Type, resolveResult.Item2); - AutoCompleteEmptyMatch = false; - return wrapper.Result; - } -// -// if (resolvedType.FullName == DomReturnType.Bool.FullName) { -// CompletionDataList completionList = new ProjectDomCompletionDataList (); -// CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); -// completionList.AutoCompleteEmptyMatch = false; -// cdc.Add ("true", "md-keyword"); -// cdc.Add ("false", "md-keyword"); -// resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc); -// return completionList; -// } -// if (resolvedType.ClassType == ClassType.Delegate && token == "=") { -// CompletionDataList completionList = new ProjectDomCompletionDataList (); -// string parameterDefinition = AddDelegateHandlers (completionList, resolvedType); -// string varName = GetPreviousMemberReferenceExpression (tokenIndex); -// completionList.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.CallingMember, resolvedType)); -// -// CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); -// resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc); -// foreach (var data in completionList) { -// if (data is MemberCompletionData) -// ((MemberCompletionData)data).IsDelegateExpected = true; -// } -// return completionList; -// } - return null; - case "+=": - case "-=": - GetPreviousToken (ref tokenIndex, false); - - expressionOrVariableDeclaration = GetExpressionAt (tokenIndex); - if (expressionOrVariableDeclaration == null) - return null; - - resolveResult = ResolveExpression (expressionOrVariableDeclaration.Item1, expressionOrVariableDeclaration.Item2, expressionOrVariableDeclaration.Item3); - if (resolveResult == null) - return null; - - - var mrr = resolveResult.Item1 as MemberResolveResult; - if (mrr != null) { - var evt = mrr.Member as IEvent; - if (evt == null) - return null; - var delegateType = evt.ReturnType.Resolve (ctx); - if (!delegateType.IsDelegate ()) - return null; - - var wrapper = new CompletionDataWrapper (this); - if (currentType != null) { -// bool includeProtected = DomType.IncludeProtected (dom, typeFromDatabase, resolver.CallingType); - foreach (var method in currentType.GetMethods (ctx)) { - if (MatchDelegate (delegateType, method) /*&& method.IsAccessibleFrom (dom, resolver.CallingType, resolver.CallingMember, includeProtected) &&*/) { - wrapper.AddMember (method); -// data.SetText (data.CompletionText + ";"); - } - } - } - if (token == "+=") { - string parameterDefinition = AddDelegateHandlers (wrapper, delegateType); - string varName = GetPreviousMemberReferenceExpression (tokenIndex); - wrapper.Result.Add (factory.CreateEventCreationCompletionData (varName, delegateType, evt, parameterDefinition, currentMember, currentType)); - } - - return wrapper.Result; - } - return null; - case ":": - if (currentMember == null) { - var wrapper = new CompletionDataWrapper (this); - AddTypesAndNamespaces (wrapper, GetState (), null, t => currentType != null ? !currentType.Equals (t) : true); - return wrapper.Result; - } - return null; - } - - var keywordCompletion = HandleKeywordCompletion (tokenIndex, token); - if (keywordCompletion == null && controlSpace) - goto default; - return keywordCompletion; - // Automatic completion - default: - if (IsInsideComment () || IsInsideString ()) - return null; - var identifierStart = GetExpressionAtCursor (); - if (IsInLinqContext (offset)) { - tokenIndex = offset; - token = GetPreviousToken (ref tokenIndex, false); // token last typed - if (linqKeywords.Contains (token)) { - if (token == "from") // after from no auto code completion. - return null; - return DefaultControlSpaceItems (); - } - var dataList = new CompletionDataWrapper (this); - AddKeywords (dataList, linqKeywords); - return dataList.Result; - } - - if (!(char.IsLetter (completionChar) || completionChar == '_') && (identifierStart == null || !(identifierStart.Item2 is ArrayInitializerExpression))) - return controlSpace ? DefaultControlSpaceItems () : null; - char prevCh = offset > 2 ? document.GetCharAt (offset - 2) : '\0'; - char nextCh = offset < document.TextLength ? document.GetCharAt (offset) : ' '; - const string allowedChars = ";,[(){}+-*/%^?:&|~!<>="; - if (!Char.IsWhiteSpace (nextCh) && allowedChars.IndexOf (nextCh) < 0) - return null; - if (!(Char.IsWhiteSpace (prevCh) || allowedChars.IndexOf (prevCh) >= 0)) - return null; - // Do not pop up completion on identifier identifier (should be handled by keyword completion). - tokenIndex = offset - 1; - token = GetPreviousToken (ref tokenIndex, false); - if (string.IsNullOrEmpty (token) && !(IsInsideComment (tokenIndex) || IsInsideString (tokenIndex))) { - char last = token [token.Length - 1]; - if (!char.IsLetterOrDigit (last) && last != '_') - return null; - } - - if (identifierStart == null) - return null; - - CSharpResolver csResolver; - AstNode n = identifierStart.Item2; - var contextList = new CompletionDataWrapper (this); - if (n is ArrayInitializerExpression) { - var initalizerResult = ResolveExpression (identifierStart.Item1, n.Parent, identifierStart.Item3); - if (initalizerResult != null) { - foreach (var property in initalizerResult.Item1.Type.GetProperties (ctx)) { - if (!property.IsPublic) - continue; - contextList.AddMember (property); - } - foreach (var field in initalizerResult.Item1.Type.GetProperties (ctx)){ - if (!field.IsPublic) - continue; - contextList.AddMember (field); - } - return contextList.Result; - } - return null; - } - - if (n != null/* && !(identifierStart.Item2 is TypeDeclaration)*/) { - csResolver = new CSharpResolver (ctx, System.Threading.CancellationToken.None); - var nodes = new List<AstNode> (); - nodes.Add (n); - if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) - nodes.Add (n.Parent); - var navigator = new NodeListResolveVisitorNavigator (nodes); - var visitor = new ResolveVisitor (csResolver, identifierStart.Item1, navigator); - visitor.Scan (identifierStart.Item3); - try { - csResolver = visitor.GetResolverStateBefore (n); - } catch (Exception) { - csResolver = GetState (); - } - // add attribute properties. - if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { - var resolved = visitor.GetResolveResult (n.Parent); - if (resolved != null && resolved.Type != null) { - foreach (var property in resolved.Type.GetProperties (ctx).Where (p => p.Accessibility == Accessibility.Public)) { - contextList.AddMember (property); - } - foreach (var field in resolved.Type.GetFields (ctx).Where (p => p.Accessibility == Accessibility.Public)) { - contextList.AddMember (field); - } - } - } - } else { - csResolver = GetState (); - } - - // identifier has already started with the first letter - offset--; - - AddContextCompletion (contextList, csResolver, identifierStart.Item2); - return contextList.Result; -// if (stub.Parent is BlockStatement) - -// result = FindExpression (dom, completionContext, -1); -// if (result == null) -// return null; -// else if (result.ExpressionContext != ExpressionContext.IdentifierExpected) { -// triggerWordLength = 1; -// bool autoSelect = true; -// IType returnType = null; -// if ((prevCh == ',' || prevCh == '(') && GetParameterCompletionCommandOffset (out cpos)) { -// ctx = CompletionWidget.CreateCodeCompletionContext (cpos); -// NRefactoryParameterDataProvider dataProvider = ParameterCompletionCommand (ctx) as NRefactoryParameterDataProvider; -// if (dataProvider != null) { -// int i = dataProvider.GetCurrentParameterIndex (CompletionWidget, ctx) - 1; -// foreach (var method in dataProvider.Methods) { -// if (i < method.Parameters.Count) { -// returnType = dom.GetType (method.Parameters [i].ReturnType); -// autoSelect = returnType == null || returnType.ClassType != ClassType.Delegate; -// break; -// } -// } -// } -// } -// // Bug 677531 - Auto-complete doesn't always highlight generic parameter in method signature -// //if (result.ExpressionContext == ExpressionContext.TypeName) -// // autoSelect = false; -// CompletionDataList dataList = CreateCtrlSpaceCompletionData (completionContext, result); -// AddEnumMembers (dataList, returnType); -// dataList.AutoSelect = autoSelect; -// return dataList; -// } else { -// result = FindExpression (dom, completionContext, 0); -// tokenIndex = offset; -// -// // check foreach case, unfortunately the expression finder is too dumb to handle full type names -// // should be overworked if the expression finder is replaced with a mcs ast based analyzer. -// var possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // starting letter -// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varname -// -// // read return types to '(' token -// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varType -// if (possibleForeachToken == ">") { -// while (possibleForeachToken != null && possibleForeachToken != "(") { -// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); -// } -// } else { -// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // ( -// if (possibleForeachToken == ".") -// while (possibleForeachToken != null && possibleForeachToken != "(") -// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); -// } -// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // foreach -// -// if (possibleForeachToken == "foreach") { -// result.ExpressionContext = ExpressionContext.ForeachInToken; -// } else { -// return null; -// // result.ExpressionContext = ExpressionContext.IdentifierExpected; -// } -// result.Expression = ""; -// result.Region = DomRegion.Empty; -// -// return CreateCtrlSpaceCompletionData (completionContext, result); -// } -// break; - } - return null; - } - - bool IsInLinqContext (int offset) - { - string token; - while (null != (token = GetPreviousToken (ref offset, true))) { - if (token == "from") - return true; - if (token == ";") - return false; - } - return false; - } - - IEnumerable<ICompletionData> DefaultControlSpaceItems () - { - var wrapper = new CompletionDataWrapper (this); - - var node = Unit.GetNodeAt (location); - var rr = ResolveExpression (CSharpParsedFile, node, Unit); - AddContextCompletion (wrapper, rr != null ? rr.Item2 : GetState (), node); - - return wrapper.Result; - } - - void AddContextCompletion (CompletionDataWrapper wrapper, CSharpResolver state, AstNode node) - { - if (state == null) - return; - foreach (var variable in state.LocalVariables) { - wrapper.AddVariable (variable); - } - if (state.CurrentMember is IParameterizedMember) { - var param = (IParameterizedMember)state.CurrentMember; - foreach (var p in param.Parameters) { - wrapper.AddVariable (p); - } - } - - if (state.CurrentMember is IMethod) { - var method = (IMethod)state.CurrentMember; - foreach (var p in method.TypeParameters) { - wrapper.AddTypeParameter (p); - } - } - Predicate<ITypeDefinition> typePred = null; - if (node is Attribute) { - var attribute = ctx.GetTypeDefinition ("System", "Attribute", 0, StringComparer.Ordinal); - typePred = t => t.GetAllBaseTypeDefinitions (ctx).Any (bt => bt.Equals (attribute)); - } - AddTypesAndNamespaces (wrapper, state, node, typePred); - - wrapper.Result.Add (factory.CreateLiteralCompletionData ("global")); - if (state.CurrentMember != null) { - AddKeywords (wrapper, statementStartKeywords); - AddKeywords (wrapper, expressionLevelKeywords); - } else if (state.CurrentTypeDefinition != null) { - AddKeywords (wrapper, typeLevelKeywords); - } else { - AddKeywords (wrapper, globalLevelKeywords); - } - - if (IsInSwitchContext(node)) { - wrapper.AddCustom ("case"); - wrapper.AddCustom ("default"); - } - - AddKeywords (wrapper, primitiveTypesKeywords); - wrapper.Result.AddRange (factory.CreateCodeTemplateCompletionData ()); - } - - static bool IsInSwitchContext(AstNode node) - { - var n = node; - while (n != null && !(n is MemberDeclaration)) { - Console.WriteLine (n.GetType ()); - if (n is SwitchStatement) - return true; - if (n is BlockStatement) - return false; - n = n.Parent; - } - return false; - } - - void AddTypesAndNamespaces (CompletionDataWrapper wrapper, CSharpResolver state, AstNode node, Predicate<ITypeDefinition> typePred = null, Predicate<IMember> memberPred = null) - { - var currentType = state.CurrentTypeDefinition ?? this.currentType; - var currentMember = state.CurrentMember ?? this.currentMember; - if (currentType != null) { - for (var ct = currentType; ct != null; ct = ct.DeclaringTypeDefinition) { - foreach (var nestedType in ct.NestedTypes) { - if (typePred == null || typePred (nestedType)) { - string name = nestedType.Name; - if (node is Attribute && name.EndsWith ("Attribute") && name.Length > "Attribute".Length) - name = name.Substring (0, name.Length - "Attribute".Length); - wrapper.AddType (nestedType, name); - } - } - } - if (currentMember != null) { - foreach (var member in currentType.Resolve (ctx).GetMembers (ctx)) { - if (memberPred == null || memberPred (member)) - wrapper.AddMember (member); - } - } - foreach (var p in currentType.TypeParameters) { - wrapper.AddTypeParameter (p); - } - } - - for (var n = state.CurrentUsingScope; n != null; n = n.Parent) { - foreach (var pair in n.UsingAliases) { - wrapper.AddNamespace ("", pair.Key); - } - - foreach (var u in n.Usings) { - var ns = u.ResolveNamespace (ctx); - if (ns == null) - continue; - foreach (var type in ctx.GetTypes (ns.NamespaceName, StringComparer.Ordinal)) { - if (typePred == null || typePred (type)) { - string name = type.Name; - if (node is Attribute && name.EndsWith ("Attribute") && name.Length > "Attribute".Length) - name = name.Substring (0, name.Length - "Attribute".Length); - wrapper.AddType (type, name); - } - } - } - - foreach (var type in ctx.GetTypes (n.NamespaceName, StringComparer.Ordinal)) { - if (typePred == null || typePred (type)) { - wrapper.AddType (type, type.Name); - } - } - - foreach (var curNs in ctx.GetNamespaces ().Where (sn => sn.StartsWith (n.NamespaceName) && sn != n.NamespaceName)) { - wrapper.AddNamespace (n.NamespaceName, curNs); - } - } - } - - IEnumerable<ICompletionData> HandleKeywordCompletion (int wordStart, string word) - { - if (IsInsideComment () || IsInsideString ()) - return null; - switch (word) { - case "using": - case "namespace": - if (currentType != null) - return null; - var wrapper = new CompletionDataWrapper (this); - AddTypesAndNamespaces (wrapper, GetState (), null, t => false); - return wrapper.Result; - case "case": - return CreateCaseCompletionData (location); -// case ",": -// case ":": -// if (result.ExpressionContext == ExpressionContext.InheritableType) { -// IType cls = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); -// CompletionDataList completionList = new ProjectDomCompletionDataList (); -// List<string > namespaceList = GetUsedNamespaces (); -// var col = new CSharpTextEditorCompletion.CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, null, location); -// bool isInterface = false; -// HashSet<string > baseTypeNames = new HashSet<string> (); -// if (cls != null) { -// baseTypeNames.Add (cls.Name); -// if (cls.ClassType == ClassType.Struct) -// isInterface = true; -// } -// int tokenIndex = offset; -// -// // Search base types " : [Type1, ... ,TypeN,] <Caret>" -// string token = null; -// do { -// token = GetPreviousToken (ref tokenIndex, false); -// if (string.IsNullOrEmpty (token)) -// break; -// token = token.Trim (); -// if (Char.IsLetterOrDigit (token [0]) || token [0] == '_') { -// IType baseType = dom.SearchType (Document.CompilationUnit, cls, result.Region.Start, token); -// if (baseType != null) { -// if (baseType.ClassType != ClassType.Interface) -// isInterface = true; -// baseTypeNames.Add (baseType.Name); -// } -// } -// } while (token != ":"); -// foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) { -// IType type = o as IType; -// if (type != null && (type.IsStatic || type.IsSealed || baseTypeNames.Contains (type.Name) || isInterface && type.ClassType != ClassType.Interface)) { -// continue; -// } -// if (o is Namespace && !namespaceList.Any (ns => ns.StartsWith (((Namespace)o).FullName))) -// continue; -// col.Add (o); -// } -// // Add inner classes -// Stack<IType > innerStack = new Stack<IType> (); -// innerStack.Push (cls); -// while (innerStack.Count > 0) { -// IType curType = innerStack.Pop (); -// if (curType == null) -// continue; -// foreach (IType innerType in curType.InnerTypes) { -// if (innerType != cls) -// // don't add the calling class as possible base type -// col.Add (innerType); -// } -// if (curType.DeclaringType != null) -// innerStack.Push (curType.DeclaringType); -// } -// return completionList; -// } -// break; - case "is": - case "as": - if (currentType == null) - return null; - IType isAsType = null; - var isAsExpression = GetExpressionAt (wordStart); - if (isAsExpression != null) { - var parent = isAsExpression.Item2.Parent; - if (parent is VariableInitializer) - parent = parent.Parent; - if (parent is VariableDeclarationStatement) { - var resolved = ResolveExpression (isAsExpression.Item1, parent, isAsExpression.Item3); - if (resolved != null) - isAsType = resolved.Item1.Type; - } - } - - var isAsWrapper = new CompletionDataWrapper (this); - AddTypesAndNamespaces (isAsWrapper, GetState (), null, t => isAsType == null || t.IsDerivedFrom (isAsType.GetDefinition (), ctx)); - return isAsWrapper.Result; -// { -// CompletionDataList completionList = new ProjectDomCompletionDataList (); -// ExpressionResult expressionResult = FindExpression (dom, completionContext, wordStart - document.Caret.Offset); -// NRefactoryResolver resolver = CreateResolver (); -// ResolveResult resolveResult = resolver.Resolve (expressionResult, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); -// if (resolveResult != null && resolveResult.ResolvedType != null) { -// CompletionDataCollector col = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); -// IType foundType = null; -// if (word == "as") { -// ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForAsCompletion (document, Document.CompilationUnit, Document.FileName, resolver.CallingType); -// if (exactContext is ExpressionContext.TypeExpressionContext) { -// foundType = resolver.SearchType (((ExpressionContext.TypeExpressionContext)exactContext).Type); -// AddAsCompletionData (col, foundType); -// } -// } -// -// if (foundType == null) -// foundType = resolver.SearchType (resolveResult.ResolvedType); -// -// if (foundType != null) { -// if (foundType.ClassType == ClassType.Interface) -// foundType = resolver.SearchType (DomReturnType.Object); -// -// foreach (IType type in dom.GetSubclasses (foundType)) { -// if (type.IsSpecialName || type.Name.StartsWith ("<")) -// continue; -// AddAsCompletionData (col, type); -// } -// } -// List<string > namespaceList = GetUsedNamespaces (); -// foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) { -// if (o is IType) { -// IType type = (IType)o; -// if (type.ClassType != ClassType.Interface || type.IsSpecialName || type.Name.StartsWith ("<")) -// continue; -// // if (foundType != null && !dom.GetInheritanceTree (foundType).Any (x => x.FullName == type.FullName)) -// // continue; -// AddAsCompletionData (col, type); -// continue; -// } -// if (o is Namespace) -// continue; -// col.Add (o); -// } -// return completionList; -// } -// result.ExpressionContext = ExpressionContext.TypeName; -// return CreateCtrlSpaceCompletionData (completionContext, result); -// } - case "override": - // Look for modifiers, in order to find the beginning of the declaration - int firstMod = wordStart; - int i = wordStart; - for (int n = 0; n < 3; n++) { - string mod = GetPreviousToken (ref i, true); - if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") { - firstMod = i; - } else if (mod == "static") { - // static methods are not overridable - return null; - } else - break; - } - if (!IsLineEmptyUpToEol ()) - return null; - var overrideCls = CSharpParsedFile.GetInnermostTypeDefinition (location); - - if (overrideCls != null && (overrideCls.Kind == TypeKind.Class || overrideCls.Kind == TypeKind.Struct)) { - string modifiers = document.GetText (firstMod, wordStart - firstMod); - return GetOverrideCompletionData (overrideCls, modifiers); - } - return null; -// case "partial": -// // Look for modifiers, in order to find the beginning of the declaration -// firstMod = wordStart; -// i = wordStart; -// for (int n = 0; n < 3; n++) { -// string mod = GetPreviousToken (ref i, true); -// if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") { -// firstMod = i; -// } else if (mod == "static") { -// // static methods are not overridable -// return null; -// } else -// break; -// } -// if (!IsLineEmptyUpToEol ()) -// return null; -// -// overrideCls = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); -// if (overrideCls != null && (overrideCls.ClassType == ClassType.Class || overrideCls.ClassType == ClassType.Struct)) { -// string modifiers = document.GetTextBetween (firstMod, wordStart); -// return GetPartialCompletionData (completionContext, overrideCls, modifiers); -// } -// return null; -// - case "public": - case "protected": - case "private": - case "internal": - case "sealed": - case "static": - wrapper = new CompletionDataWrapper (this); - var state = GetState (); - AddTypesAndNamespaces (wrapper, state, null, m => false); - AddKeywords (wrapper, typeLevelKeywords); - AddKeywords (wrapper, primitiveTypesKeywords); - return wrapper.Result; - case "new": - int j = offset - 4; -// string token = GetPreviousToken (ref j, true); - - IType hintType = null; - var expressionOrVariableDeclaration = GetExpressionAt (j); - if (expressionOrVariableDeclaration != null && expressionOrVariableDeclaration.Item2 is VariableDeclarationStatement) { - var varDecl = (VariableDeclarationStatement)expressionOrVariableDeclaration.Item2; - var resolved = ResolveExpression (expressionOrVariableDeclaration.Item1, varDecl.Type, expressionOrVariableDeclaration.Item3); - if (resolved != null) { - hintType = resolved.Item1.Type; - } - } - return CreateTypeCompletionData (hintType); -// IType callingType = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (document.Caret.Line, document.Caret.Column)); -// ExpressionContext newExactContext = new NewCSharpExpressionFinder (dom).FindExactContextForNewCompletion (document, Document.CompilationUnit, Document.FileName, callingType); -// if (newExactContext is ExpressionContext.TypeExpressionContext) -// return CreateTypeCompletionData (location, callingType, newExactContext, ((ExpressionContext.TypeExpressionContext)newExactContext).Type, ((ExpressionContext.TypeExpressionContext)newExactContext).UnresolvedType); -// if (newExactContext == null) { -// int j = offset - 4; -// -// string yieldToken = GetPreviousToken (ref j, true); -// if (token == "return") { -// NRefactoryResolver resolver = CreateResolver (); -// resolver.SetupResolver (new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); -// IReturnType returnType = resolver.CallingMember.ReturnType; -// if (yieldToken == "yield" && returnType.GenericArguments.Count > 0) -// returnType = returnType.GenericArguments [0]; -// if (resolver.CallingMember != null) -// return CreateTypeCompletionData (location, callingType, newExactContext, null, returnType); -// } -// } -// return CreateCtrlSpaceCompletionData (completionContext, null); -// case "if": -// case "elif": -// if (stateTracker.Engine.IsInsidePreprocessorDirective) -// return GetDefineCompletionData (); -// return null; - case "yield": - var yieldDataList = new CompletionDataWrapper (this); - DefaultCompletionString = "return"; - yieldDataList.AddCustom ("break"); - yieldDataList.AddCustom ("return"); - return yieldDataList.Result; - case "in": - var inList = new CompletionDataWrapper (this); - var node = Unit.GetNodeAt (location); - var rr = ResolveExpression (CSharpParsedFile, node, Unit); - AddContextCompletion (inList, rr != null ? rr.Item2 : GetState (), node); - return inList.Result; -// case "where": -// CompletionDataList whereDataList = new CompletionDataList (); -// NRefactoryResolver constraintResolver = CreateResolver (); -// constraintResolver.SetupResolver (new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); -// if (constraintResolver.CallingMember is IMethod) { -// foreach (ITypeParameter tp in ((IMethod)constraintResolver.CallingMember).TypeParameters) { -// whereDataList.Add (tp.Name, "md-keyword"); -// } -// } else { -// if (constraintResolver.CallingType != null) { -// foreach (ITypeParameter tp in constraintResolver.CallingType.TypeParameters) { -// whereDataList.Add (tp.Name, "md-keyword"); -// } -// } -// } -// -// return whereDataList; - } -// if (IsInLinqContext (result)) { -// if (linqKeywords.Contains (word)) { -// if (word == "from") // after from no auto code completion. -// return null; -// result.Expression = ""; -// return CreateCtrlSpaceCompletionData (completionContext, result); -// } -// CompletionDataList dataList = new ProjectDomCompletionDataList (); -// CompletionDataCollector col = new CompletionDataCollector (this, dom, dataList, Document.CompilationUnit, null, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); -// foreach (string kw in linqKeywords) { -// col.Add (kw, "md-keyword"); -// } -// return dataList; -// } - return null; - } - - bool IsLineEmptyUpToEol () - { - var line = document.GetLineByNumber (location.Line); - for (int j = offset; j < line.EndOffset; j++) { - char ch = document.GetCharAt (j); - if (!char.IsWhiteSpace (ch)) - return false; - } - return true; - } - string GetLineIndent (int lineNr) - { - var line = document.GetLineByNumber (lineNr); - for (int j = offset; j < line.EndOffset; j++) { - char ch = document.GetCharAt (j); - if (!char.IsWhiteSpace (ch)) - return document.GetText (line.Offset, j - line.Offset - 1); - } - return ""; - } - - IEnumerable<ICompletionData> CreateTypeCompletionData (IType hintType) - { - var wrapper = new CompletionDataWrapper (this); - var state = GetState (); - Predicate<ITypeDefinition> pred = null; - if (hintType != null && !hintType.Equals (SharedTypes.UnknownType)) { - var lookup = new MemberLookup (ctx, currentType, ProjectContent); - pred = t => { - // check if type is in inheritance tree. - if (hintType.GetDefinition () != null && !t.IsDerivedFrom (hintType.GetDefinition (), ctx)) - return false; - // check for valid constructors - if (t.Methods.Count (m => m.IsConstructor) == 0) - return true; - bool isProtectedAllowed = currentType != null ? currentType.IsDerivedFrom (t, ctx) : false; - return t.Methods.Any (m => m.IsConstructor && lookup.IsAccessible (m, isProtectedAllowed)); - }; - DefaultCompletionString = GetShortType (hintType, GetState ()); - wrapper.AddType (hintType, DefaultCompletionString); - } - AddTypesAndNamespaces (wrapper, state, null, pred, m => false); - AddKeywords (wrapper, primitiveTypesKeywords.Where (k => k != "void")); - AutoCompleteEmptyMatch = true; - return wrapper.Result; - } - - IEnumerable<ICompletionData> GetOverrideCompletionData (ITypeDefinition type, string modifiers) - { - var wrapper = new CompletionDataWrapper (this); - var alreadyInserted = new Dictionary<string, bool> (); - bool addedVirtuals = false; - - int declarationBegin = offset; - int j = declarationBegin; - for (int i = 0; i < 3; i++) { - switch (GetPreviousToken (ref j, true)) { - case "public": - case "protected": - case "private": - case "internal": - case "sealed": - case "override": - declarationBegin = j; - break; - case "static": - return null; // don't add override completion for static members - } - } - foreach (var baseType in type.GetAllBaseTypeDefinitions (ctx)) { - AddVirtuals (alreadyInserted, wrapper, type, modifiers, baseType, declarationBegin); - addedVirtuals = true; - } - if (!addedVirtuals) - AddVirtuals (alreadyInserted, wrapper, type, modifiers, ctx.GetTypeDefinition (typeof(object)), declarationBegin); - return wrapper.Result; - } - - static string GetNameWithParamCount (IMember member) - { - var e = member as IMethod; - if (e == null || e.TypeParameters.Count == 0) - return member.Name; - return e.Name + "`" + e.TypeParameters.Count; - } - - void AddVirtuals (Dictionary<string, bool> alreadyInserted, CompletionDataWrapper col, ITypeDefinition type, string modifiers, ITypeDefinition curType, int declarationBegin) - { - if (curType == null) - return; - foreach (var m in curType.Methods.Where (m => !m.IsConstructor && !m.IsDestructor).Cast<IMember> ().Concat (curType.Properties.Cast<IMember> ())) { - if (m.IsSynthetic || curType.Kind != TypeKind.Interface && !(m.IsVirtual || m.IsOverride || m.IsAbstract)) - continue; - // filter out the "Finalize" methods, because finalizers should be done with destructors. - if (m is IMethod && m.Name == "Finalize") - continue; - - var data = factory.CreateNewOverrideCompletionData (declarationBegin, type, m); - string text = GetNameWithParamCount (m); - - // check if the member is already implemented - bool foundMember = type.Members.Any (cm => GetNameWithParamCount (cm) == text); - if (!foundMember && !alreadyInserted.ContainsKey (text)) { - alreadyInserted [text] = true; - data.CompletionCategory = col.GetCompletionCategory (curType); - col.Add (data); - } - } - } - - static void AddKeywords (CompletionDataWrapper wrapper, IEnumerable<string> keywords) - { - foreach (string keyword in keywords) { - wrapper.AddCustom (keyword); - } - } - - public string GetPreviousMemberReferenceExpression (int tokenIndex) - { - string result = GetPreviousToken (ref tokenIndex, false); - result = GetPreviousToken (ref tokenIndex, false); - if (result != ".") { - result = null; - } else { - var names = new List<string> (); - while (result == ".") { - result = GetPreviousToken (ref tokenIndex, false); - if (result == "this") { - names.Add ("handle"); - } else if (result != null) { - string trimmedName = result.Trim (); - if (trimmedName.Length == 0) - break; - names.Insert (0, trimmedName); - } - result = GetPreviousToken (ref tokenIndex, false); - } - result = String.Join ("", names.ToArray ()); - foreach (char ch in result) { - if (!char.IsLetterOrDigit (ch) && ch != '_') { - result = ""; - break; - } - } - } - return result; - } - - bool MatchDelegate (IType delegateType, IMethod method) - { - var delegateMethod = delegateType.GetDelegateInvokeMethod (); - if (delegateMethod == null || delegateMethod.Parameters.Count != method.Parameters.Count) - return false; - - for (int i = 0; i < delegateMethod.Parameters.Count; i++) { - if (!delegateMethod.Parameters [i].Type.Resolve (ctx).Equals (method.Parameters [i].Type.Resolve (ctx))) - return false; - } - return true; - } - - string AddDelegateHandlers (CompletionDataWrapper completionList, IType delegateType, bool addSemicolon = true, bool addDefault = true) - { - IMethod delegateMethod = delegateType.GetDelegateInvokeMethod (); - var thisLineIndent = GetLineIndent (location.Line); - string delegateEndString = EolMarker + thisLineIndent + "}" + (addSemicolon ? ";" : ""); - bool containsDelegateData = completionList.Result.Any (d => d.DisplayText.StartsWith ("delegate(")); - if (addDefault) - completionList.AddCustom ("delegate", "Creates anonymous delegate.", "delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString); - var sb = new StringBuilder ("("); - var sbWithoutTypes = new StringBuilder ("("); - for (int k = 0; k < delegateMethod.Parameters.Count; k++) { - if (k > 0) { - sb.Append (", "); - sbWithoutTypes.Append (", "); - } - var parameterType = delegateMethod.Parameters [k].Type.Resolve (ctx); - sb.Append (GetShortType (parameterType, GetState ())); - sb.Append (" "); - sb.Append (delegateMethod.Parameters [k].Name); - sbWithoutTypes.Append (delegateMethod.Parameters [k].Name); - } - sb.Append (")"); - sbWithoutTypes.Append (")"); - completionList.AddCustom ("delegate" + sb, "Creates anonymous delegate.", "delegate" + sb + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString); - if (!completionList.Result.Any (data => data.DisplayText == sbWithoutTypes.ToString ())) - completionList.AddCustom (sbWithoutTypes.ToString (), "Creates lambda expression.", sbWithoutTypes + " => |" + (addSemicolon ? ";" : "")); - /* TODO:Make factory method out of it. - // It's needed to temporarly disable inserting auto matching bracket because the anonymous delegates are selectable with '(' - // otherwise we would end up with () => ) - if (!containsDelegateData) { - var savedValue = MonoDevelop.SourceEditor.DefaultSourceEditorOptions.Instance.AutoInsertMatchingBracket; - MonoDevelop.SourceEditor.DefaultSourceEditorOptions.Instance.AutoInsertMatchingBracket = false; - completionList.Result.CompletionListClosed += delegate { - MonoDevelop.SourceEditor.DefaultSourceEditorOptions.Instance.AutoInsertMatchingBracket = savedValue; - }; - }*/ - return sb.ToString (); - } - - bool IsAccessibleFrom (IEntity member, ITypeDefinition calledType, IMember currentMember, bool includeProtected) - { - if (currentMember == null) - return member.IsStatic || member.IsPublic; -// if (currentMember is MonoDevelop.Projects.Dom.BaseResolveResult.BaseMemberDecorator) -// return member.IsPublic | member.IsProtected; - // if (member.IsStatic && !IsStatic) - // return false; - if (member.IsPublic || calledType != null && calledType.Kind == TypeKind.Interface && !member.IsProtected) - return true; - if (member.DeclaringTypeDefinition != null) { - if (member.DeclaringTypeDefinition.Kind == TypeKind.Interface) - return IsAccessibleFrom (member.DeclaringTypeDefinition, calledType, currentMember, includeProtected); - - if (member.IsProtected && !(member.DeclaringTypeDefinition.IsProtectedOrInternal && !includeProtected)) - return includeProtected; - } - if (member.IsInternal || member.IsProtectedAndInternal || member.IsProtectedOrInternal) { - var type1 = member is ITypeDefinition ? (ITypeDefinition)member : member.DeclaringTypeDefinition; - var type2 = currentMember is ITypeDefinition ? (ITypeDefinition)currentMember : currentMember.DeclaringTypeDefinition; - bool result; - // easy case, projects are the same - if (type1.ProjectContent == type2.ProjectContent) { - result = true; - } else if (type1.ProjectContent != null) { - // maybe type2 hasn't project dom set (may occur in some cases), check if the file is in the project - //TODO !! -// result = type1.ProjectContent.Annotation<MonoDevelop.Projects.Project> ().GetProjectFile (type2.Region.FileName) != null; - result = false; - } else if (type2.ProjectContent != null) { - //TODO!! -// result = type2.ProjectContent.Annotation<MonoDevelop.Projects.Project> ().GetProjectFile (type1.Region.FileName) != null; - result = false; - } else { - // should never happen ! - result = true; - } - return member.IsProtectedAndInternal ? includeProtected && result : result; - } - - if (!(currentMember is IType) && (currentMember.DeclaringTypeDefinition == null || member.DeclaringTypeDefinition == null)) - return false; - - // inner class - var declaringType = currentMember.DeclaringTypeDefinition; - while (declaringType != null) { - if (declaringType.ReflectionName == currentMember.DeclaringType.ReflectionName) - return true; - declaringType = declaringType.DeclaringTypeDefinition; - } - - - return currentMember.DeclaringTypeDefinition != null && member.DeclaringTypeDefinition.FullName == currentMember.DeclaringTypeDefinition.FullName; - } - - IEnumerable<ICompletionData> CreateTypeAndNamespaceCompletionData (TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state) - { - if (resolveResult == null || resolveResult.IsError) - return null; - var result = new CompletionDataWrapper (this); - - if (resolveResult is NamespaceResolveResult) { - var nr = (NamespaceResolveResult)resolveResult; - foreach (var cl in ctx.GetTypes (nr.NamespaceName, StringComparer.Ordinal)) { - result.AddType (cl, cl.Name); - } - foreach (var ns in ctx.GetNamespaces ().Where (n => n.Length > nr.NamespaceName.Length && n.StartsWith (nr.NamespaceName))) { - result.AddNamespace (nr.NamespaceName, ns); - } - } else if (resolveResult is TypeResolveResult) { - var type = resolveResult.Type.Resolve (ctx); - foreach (var nested in type.GetNestedTypes (ctx)) { - result.AddType (nested, nested.Name); - } - } - return result.Result; - } - - IEnumerable<ICompletionData> CreateTypeList () - { - foreach (var cl in ctx.GetTypes ("", StringComparer.Ordinal)) { - yield return factory.CreateTypeCompletionData (cl, cl.Name); - } - foreach (var ns in ctx.GetNamespaces ()) { - string name = ns; - int idx = name.IndexOf ("."); - if (idx >= 0) - name = name.Substring (0, idx); - yield return factory.CreateNamespaceCompletionData (name); - } - } - - public ITypeDefinition GetTypeFromContext (ITypeDefinition type) - { - if (type == null || type.DeclaringType != null) - return type; - var result = ctx.GetTypeDefinition (type.Namespace, type.Name, type.TypeParameterCount, StringComparer.Ordinal) ?? type; - if (result.GetParts ().Count == 1) - return type; -// Console.WriteLine ("result:"+ result); - return result; - } - - IEnumerable<ICompletionData> CreateParameterCompletion (MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, int parameter, bool controlSpace) - { - var result = new CompletionDataWrapper (this); - var addedEnums = new HashSet<string> (); - var addedDelegates = new HashSet<string> (); - - foreach (var method in resolveResult.Methods) { - if (method.Parameters.Count <= parameter) - continue; - var resolvedType = method.Parameters [parameter].Type.Resolve (ctx); - if (resolvedType.Kind == TypeKind.Enum) { - if (addedEnums.Contains (resolvedType.ReflectionName)) - continue; - addedEnums.Add (resolvedType.ReflectionName); - AddEnumMembers (result, resolvedType, state); - } else if (resolvedType.Kind == TypeKind.Delegate) { -// if (addedDelegates.Contains (resolvedType.DecoratedFullName)) -// continue; -// addedDelegates.Add (resolvedType.DecoratedFullName); -// string parameterDefinition = AddDelegateHandlers (completionList, resolvedType, false, addedDelegates.Count == 1); -// string varName = "Handle" + method.Parameters [parameter].ReturnType.Name + method.Parameters [parameter].Name; -// result.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.Unit.GetMemberAt (location), resolvedType) { AddSemicolon = false }); - - } - } - if (!controlSpace) { - if (addedEnums.Count + addedDelegates.Count == 0) - return Enumerable.Empty<ICompletionData> (); - AutoCompleteEmptyMatch = false; - AutoSelect = false; - } - AddContextCompletion (result, state, invocation); - -// resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); -// if (addedDelegates.Count > 0) { -// foreach (var data in result.Result) { -// if (data is MemberCompletionData) -// ((MemberCompletionData)data).IsDelegateExpected = true; -// } -// } - return result.Result; - } - - string GetShortType (IType type, CSharpResolver state) - { - var builder = new TypeSystemAstBuilder (state); - var shortType = builder.ConvertType (type); - using (var w = new System.IO.StringWriter ()) { - var visitor = new CSharpOutputVisitor (w, FormattingPolicy); - shortType.AcceptVisitor (visitor, null); - return w.ToString (); - } - } - - void AddEnumMembers (CompletionDataWrapper completionList, IType resolvedType, CSharpResolver state) - { - if (resolvedType.Kind != TypeKind.Enum) - return; - string typeString = GetShortType (resolvedType, state); - if (typeString.Contains (".")) - completionList.AddType (resolvedType, typeString); - foreach (var field in resolvedType.GetFields (ctx)) { - if (field.IsConst || field.IsStatic) - completionList.Result.Add (factory.CreateEntityCompletionData (field, typeString + "." + field.Name)); - } - DefaultCompletionString = typeString; - } - - IEnumerable<ICompletionData> CreateCompletionData (TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state) - { - if (resolveResult == null /*|| resolveResult.IsError*/) - return null; - if (resolveResult is NamespaceResolveResult) { - var nr = (NamespaceResolveResult)resolveResult; - var namespaceContents = new CompletionDataWrapper (this); - foreach (var cl in ctx.GetTypes (nr.NamespaceName, StringComparer.Ordinal)) { - namespaceContents.AddType (cl, cl.Name); - } - foreach (var ns in ctx.GetNamespaces ().Where (n => n.Length > nr.NamespaceName.Length && n.StartsWith (nr.NamespaceName))) { - namespaceContents.AddNamespace (nr.NamespaceName, ns); - } - - return namespaceContents.Result; - } - - IType type = GetTypeFromContext (resolveResult.Type.GetDefinition ()) ?? resolveResult.Type; - var typeDef = resolveResult.Type.GetDefinition (); - var lookup = new MemberLookup (ctx, currentType, ProjectContent); - var result = new CompletionDataWrapper (this); - bool isProtectedAllowed = false; - bool includeStaticMembers = false; - - if (resolveResult is LocalResolveResult) { - isProtectedAllowed = currentType != null && typeDef != null ? typeDef.GetAllBaseTypeDefinitions (ctx).Any (bt => bt.Equals (currentType)) : false; - if (resolvedNode is IdentifierExpression) { - var mrr = (LocalResolveResult)resolveResult; - includeStaticMembers = mrr.Variable.Name == mrr.Type.Name; - } - } else { - isProtectedAllowed = currentType != null && typeDef != null ? currentType.GetAllBaseTypeDefinitions (ctx).Any (bt => bt.Equals (typeDef)) : false; - } - if (resolveResult is TypeResolveResult && type.Kind == TypeKind.Enum) { - foreach (var field in type.GetFields (ctx)) { - result.AddMember (field); - } - foreach (var m in type.GetMethods (ctx)) { - if (m.Name == "TryParse") - result.AddMember (m); - } - return result.Result; - } - - if (resolveResult is MemberResolveResult && resolvedNode is IdentifierExpression) { - var mrr = (MemberResolveResult)resolveResult; - includeStaticMembers = mrr.Member.Name == mrr.Type.Name; - } - -// Console.WriteLine ("type:" + type +"/"+type.GetType ()); -// Console.WriteLine ("IS PROT ALLOWED:" + isProtectedAllowed); -// Console.WriteLine (resolveResult); -// Console.WriteLine (currentMember != null ? currentMember.IsStatic : "currentMember == null"); - if (resolvedNode.Annotation<ObjectCreateExpression> () == null) { //tags the created expression as part of an object create expression. - foreach (var member in type.GetMembers (ctx)) { - if (!lookup.IsAccessible (member, isProtectedAllowed)) { - // Console.WriteLine ("skip access: " + member.FullName); - continue; - } - if (resolvedNode is BaseReferenceExpression && member.IsAbstract) - continue; - - if (!includeStaticMembers && member.IsStatic && !(resolveResult is TypeResolveResult)) { - // Console.WriteLine ("skip static member: " + member.FullName); - continue; - } - if (!member.IsStatic && (resolveResult is TypeResolveResult)) { - // Console.WriteLine ("skip non static member: " + member.FullName); - continue; - } - // Console.WriteLine ("add : "+ member.FullName + " --- " + member.IsStatic); - result.AddMember (member); - } - } - - if (resolveResult is TypeResolveResult || includeStaticMembers) { - foreach (var nested in type.GetNestedTypes (ctx)) { - result.AddType (nested, nested.Name); - } - - } else { - var baseTypes = new List<IType> (type.GetAllBaseTypes (ctx)); - var conv = new Conversions (ctx); - for (var n = state.CurrentUsingScope; n != null; n = n.Parent) { - AddExtensionMethods (result, conv, baseTypes, n.NamespaceName); - foreach (var u in n.Usings) { - var ns = u.ResolveNamespace (ctx); - if (ns == null) - continue; - AddExtensionMethods (result, conv, baseTypes, ns.NamespaceName); - } - } - } - -// IEnumerable<object> objects = resolveResult.CreateResolveResult (dom, resolver != null ? resolver.CallingMember : null); -// CompletionDataCollector col = new CompletionDataCollector (this, dom, result, Document.CompilationUnit, resolver != null ? resolver.CallingType : null, location); -// col.HideExtensionParameter = !resolveResult.StaticResolve; -// col.NamePrefix = expressionResult.Expression; -// bool showOnlyTypes = expressionResult.Contexts.Any (ctx => ctx == ExpressionContext.InheritableType || ctx == ExpressionContext.Constraints); -// if (objects != null) { -// foreach (object obj in objects) { -// if (expressionResult.ExpressionContext != null && expressionResult.ExpressionContext.FilterEntry (obj)) -// continue; -// if (expressionResult.ExpressionContext == ExpressionContext.NamespaceNameExcepted && !(obj is Namespace)) -// continue; -// if (showOnlyTypes && !(obj is IType)) -// continue; -// CompletionData data = col.Add (obj); -// if (data != null && expressionResult.ExpressionContext == ExpressionContext.Attribute && data.CompletionText != null && data.CompletionText.EndsWith ("Attribute")) { -// string newText = data.CompletionText.Substring (0, data.CompletionText.Length - "Attribute".Length); -// data.SetText (newText); -// } -// } -// } - - return result.Result; - } - - void AddExtensionMethods (CompletionDataWrapper result, Conversions conv, List<IType> baseTypes, string namespaceName) - { - foreach (var typeDefinition in ctx.GetTypes (namespaceName, StringComparer.Ordinal).Where (t => t.IsStatic && t.HasExtensionMethods)) { - foreach (var m in typeDefinition.Methods.Where (m => m.IsExtensionMethod )) { - var pt = m.Parameters.First ().Type.Resolve (ctx); - string reflectionName = pt is ParameterizedType ? ((ParameterizedType)pt).GetDefinition ().ReflectionName : pt.ReflectionName; - if (baseTypes.Any (bt => (bt is ParameterizedType ? ((ParameterizedType)bt).GetDefinition ().ReflectionName : bt.ReflectionName) == reflectionName)) { - result.AddMember (m); - } - } - } - } - IEnumerable<ICompletionData> CreateCaseCompletionData (TextLocation location) - { - var unit = ParseStub ("a: break;"); - if (unit == null) - return null; - var s = unit.GetNodeAt<SwitchStatement> (location); - if (s == null) - return null; - - var offset = document.GetOffset (s.Expression.StartLocation); - var expr = GetExpressionAt (offset); - if (expr == null) - return null; - - var resolveResult = ResolveExpression (expr.Item1, expr.Item2, expr.Item3); - if (resolveResult == null || resolveResult.Item1.Type.Kind != TypeKind.Enum) - return null; - var wrapper = new CompletionDataWrapper (this); - AddEnumMembers (wrapper, resolveResult.Item1.Type, resolveResult.Item2); - AutoCompleteEmptyMatch = false; - return wrapper.Result; - } - - #region Parsing methods - - - Tuple<CSharpParsedFile, AstNode, CompilationUnit> GetExpressionBeforeCursor () - { - CompilationUnit baseUnit; - if (currentMember == null) { - baseUnit = ParseStub ("st {}", false); - var type = baseUnit.GetNodeAt<MemberType> (location); - if (type == null) { - baseUnit = ParseStub ("a;", false); - type = baseUnit.GetNodeAt<MemberType> (location); - } - if (type != null) { - // insert target type into compilation unit, to respect the - var target = type.Target; - target.Remove (); - var node = Unit.GetNodeAt (location) ?? Unit; - node.AddChild (target, AstNode.Roles.Type); - return Tuple.Create (CSharpParsedFile, (AstNode)target, Unit); - } - } - - if (currentMember == null && currentType == null) { - return null; - } - baseUnit = ParseStub ("a()"); - Print (baseUnit); - var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; - var mref = baseUnit.GetNodeAt<MemberReferenceExpression> (location); - if (mref == null){ - var invoke = baseUnit.GetNodeAt<InvocationExpression> (location); - if (invoke != null) - mref = invoke.Target as MemberReferenceExpression; - } - Expression expr = null; - if (mref != null) { - expr = mref.Target.Clone (); - mref.Parent.ReplaceWith (expr); - } else { - Expression tref = baseUnit.GetNodeAt<TypeReferenceExpression> (location); - var memberType = tref != null ? ((TypeReferenceExpression)tref).Type as MemberType : null; - if (memberType == null) { - memberType = baseUnit.GetNodeAt<MemberType> (location); - if (memberType != null) { - tref = baseUnit.GetNodeAt<Expression> (location); - if (tref == null) - return null; - } - if (tref is ObjectCreateExpression) { - expr = new TypeReferenceExpression (memberType.Target.Clone ()); - expr.AddAnnotation (new ObjectCreateExpression ()); - } - } - - if (memberType == null) - return null; - if (expr == null) - expr = new TypeReferenceExpression (memberType.Target.Clone ()); - tref.ReplaceWith (expr); - } - - var member = Unit.GetNodeAt<AttributedNode> (memberLocation); - var member2 = baseUnit.GetNodeAt<AttributedNode> (memberLocation); - member2.Remove (); - member.ReplaceWith (member2); - var tsvisitor = new TypeSystemConvertVisitor (ProjectContent, this.CSharpParsedFile.FileName); - Unit.AcceptVisitor (tsvisitor, null); - return Tuple.Create (tsvisitor.ParsedFile, (AstNode)expr, Unit); - } - - Tuple<CSharpParsedFile, AstNode, CompilationUnit> GetExpressionAtCursor () - { -// if (currentMember == null && currentType == null) -// return null; - - TextLocation memberLocation; - if (currentMember != null) { - memberLocation = currentMember.Region.Begin; - } else if (currentType != null) { - memberLocation = currentType.Region.Begin; - } else { - memberLocation = location; - } - - var baseUnit = ParseStub (""); - var tmpUnit = baseUnit; - AstNode expr = baseUnit.GetNodeAt<IdentifierExpression> (location.Line, location.Column - 1); - if (expr == null) - expr = baseUnit.GetNodeAt<Attribute> (location.Line, location.Column - 1); - if (expr == null) { - baseUnit = ParseStub ("()"); - expr = baseUnit.GetNodeAt<IdentifierExpression> (location.Line, location.Column - 1); - } - - // try initializer expression - if (expr == null) { - baseUnit = ParseStub ("a = b};", false); - expr = baseUnit.GetNodeAt<ArrayInitializerExpression> (location.Line, location.Column - 1); - } - - // try statement - if (expr == null) { - expr = tmpUnit.GetNodeAt<Statement> (location.Line, location.Column - 1); - } - - if (expr == null) - return null; - var member = Unit.GetNodeAt<AttributedNode> (memberLocation); - var member2 = baseUnit.GetNodeAt<AttributedNode> (memberLocation); - if (member != null && member2 != null) { - member2.Remove (); - - if (member is TypeDeclaration) { - member.AddChild (member2, TypeDeclaration.MemberRole); - } else { - member.ReplaceWith (member2); - } - } else { - var tsvisitor2 = new TypeSystemConvertVisitor (ProjectContent, CSharpParsedFile.FileName); - Unit.AcceptVisitor (tsvisitor2, null); - return Tuple.Create (tsvisitor2.ParsedFile, expr, baseUnit); - } - var tsvisitor = new TypeSystemConvertVisitor (ProjectContent, CSharpParsedFile.FileName); - Unit.AcceptVisitor (tsvisitor, null); - return Tuple.Create (tsvisitor.ParsedFile, expr, Unit); - } - - Tuple<CSharpParsedFile, AstNode, CompilationUnit> GetExpressionAt (int offset) - { - var parser = new CSharpParser (); - string text = this.document.GetText (0, this.offset); - var sb = new StringBuilder (text); - sb.Append ("a;"); - AppendMissingClosingBrackets (sb, text, false); - var stream = new System.IO.StringReader (sb.ToString ()); - var completionUnit = parser.Parse (stream, 0); - stream.Close (); - var loc = document.GetLocation (offset); - var expr = completionUnit.GetNodeAt (loc, n => n is Expression || n is VariableDeclarationStatement); - if (expr == null) - return null; - var tsvisitor = new TypeSystemConvertVisitor (ProjectContent, CSharpParsedFile.FileName); - completionUnit.AcceptVisitor (tsvisitor, null); - - return Tuple.Create (tsvisitor.ParsedFile, expr, completionUnit); - } - - - #endregion - - #region Helper methods - string GetPreviousToken (ref int i, bool allowLineChange) - { - char c; - if (i <= 0) - return null; - - do { - c = document.GetCharAt (--i); - } while (i > 0 && char.IsWhiteSpace (c) && (allowLineChange ? true : c != '\n')); - - if (i == 0) - return null; - - if (!char.IsLetterOrDigit (c)) - return new string (c, 1); - - int endOffset = i + 1; - - do { - c = document.GetCharAt (i - 1); - if (!(char.IsLetterOrDigit (c) || c == '_')) - break; - - i--; - } while (i > 0); - - return document.GetText (i, endOffset - i); - } - - bool GetParameterCompletionCommandOffset (out int cpos) - { - // Start calculating the parameter offset from the beginning of the - // current member, instead of the beginning of the file. - cpos = offset - 1; - IMember mem = currentMember; - if (mem == null || (mem is IType)) - return false; - int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn); - int parenDepth = 0; - int chevronDepth = 0; - while (cpos > startPos) { - char c = document.GetCharAt (cpos); - if (c == ')') - parenDepth++; - if (c == '>') - chevronDepth++; - if (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<') { - int p = GetCurrentParameterIndex (cpos + 1, startPos); - if (p != -1) { - cpos++; - return true; - } else { - return false; - } - } - if (c == '(') - parenDepth--; - if (c == '<') - chevronDepth--; - cpos--; - } - return false; - } - - int GetCurrentParameterIndex (int offset, int memberStart) - { - int cursor = this.offset; - int i = offset; - - if (i > cursor) - return -1; - if (i == cursor) - return 1; // parameters are 1 based - int index = memberStart + 1; - int parentheses = 0; - int bracket = 0; - bool insideQuote = false, insideString = false, insideSingleLineComment = false, insideMultiLineComment = false; - do { - char c = document.GetCharAt (i - 1); - switch (c) { - case '\\': - if (insideString || insideQuote) - i++; - break; - case '\'': - if (!insideString && !insideSingleLineComment && !insideMultiLineComment) - insideQuote = !insideQuote; - break; - case '"': - if (!insideQuote && !insideSingleLineComment && !insideMultiLineComment) - insideString = !insideString; - break; - case '/': - if (!insideQuote && !insideString && !insideMultiLineComment) { - if (document.GetCharAt (i) == '/') - insideSingleLineComment = true; - if (document.GetCharAt (i) == '*') - insideMultiLineComment = true; - } - break; - case '*': - if (insideMultiLineComment && document.GetCharAt (i) == '/') - insideMultiLineComment = false; - break; - case '\n': - case '\r': - insideSingleLineComment = false; - break; - case '{': - if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment) - bracket++; - break; - case '}': - if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment) - bracket--; - break; - case '(': - if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment) - parentheses++; - break; - case ')': - if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment) - parentheses--; - break; - case ',': - if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment && parentheses == 1 && bracket == 0) - index++; - break; - } - i++; - } while (i <= cursor && parentheses >= 0); - - return parentheses != 1 || bracket > 0 ? -1 : index; - } - - CSharpResolver GetState () - { - var state = new CSharpResolver (ctx, System.Threading.CancellationToken.None); - - state.CurrentMember = CSharpParsedFile.GetMember (location); - state.CurrentTypeDefinition = CSharpParsedFile.GetInnermostTypeDefinition (location); - state.CurrentUsingScope = CSharpParsedFile.GetUsingScope (location); - if (state.CurrentMember != null) { - var node = Unit.GetNodeAt (location); - if (node == null) - return state; - var navigator = new NodeListResolveVisitorNavigator (new[] { node }); - var visitor = new ResolveVisitor (state, CSharpParsedFile, navigator); - Unit.AcceptVisitor (visitor, null); - try { - var newState = visitor.GetResolverStateBefore (node); - if (newState != null) - state = newState; - } catch (Exception) { - } - } - - return state; - } - #endregion - - #region Preprocessor - - IEnumerable<ICompletionData> GetDirectiveCompletionData () - { - yield return factory.CreateLiteralCompletionData ("if"); - yield return factory.CreateLiteralCompletionData ("else"); - yield return factory.CreateLiteralCompletionData ("elif"); - yield return factory.CreateLiteralCompletionData ("endif"); - yield return factory.CreateLiteralCompletionData ("define"); - yield return factory.CreateLiteralCompletionData ("undef"); - yield return factory.CreateLiteralCompletionData ("warning"); - yield return factory.CreateLiteralCompletionData ("error"); - yield return factory.CreateLiteralCompletionData ("pragma"); - yield return factory.CreateLiteralCompletionData ("line"); - yield return factory.CreateLiteralCompletionData ("line hidden"); - yield return factory.CreateLiteralCompletionData ("line default"); - yield return factory.CreateLiteralCompletionData ("region"); - yield return factory.CreateLiteralCompletionData ("endregion"); - } - #endregion - - #region Xml Comments - static readonly List<string> commentTags = new List<string> (new string[] { "c", "code", "example", "exception", "include", "list", "listheader", "item", "term", "description", "para", "param", "paramref", "permission", "remarks", "returns", "see", "seealso", "summary", "value" }); - - IEnumerable<ICompletionData> GetXmlDocumentationCompletionData () - { - yield return factory.CreateLiteralCompletionData ("c", "Set text in a code-like font"); - yield return factory.CreateLiteralCompletionData ("code", "Set one or more lines of source code or program output"); - yield return factory.CreateLiteralCompletionData ("example", "Indicate an example"); - yield return factory.CreateLiteralCompletionData ("exception", "Identifies the exceptions a method can throw", "exception cref=\"|\"></exception>"); - yield return factory.CreateLiteralCompletionData ("include", "Includes comments from a external file", "include file=\"|\" path=\"\">"); - yield return factory.CreateLiteralCompletionData ("list", "Create a list or table", "list type=\"|\">"); - yield return factory.CreateLiteralCompletionData ("listheader", "Define the heading row"); - yield return factory.CreateLiteralCompletionData ("item", "Defines list or table item"); - - yield return factory.CreateLiteralCompletionData ("term", "A term to define"); - yield return factory.CreateLiteralCompletionData ("description", "Describes a list item"); - yield return factory.CreateLiteralCompletionData ("para", "Permit structure to be added to text"); - - yield return factory.CreateLiteralCompletionData ("param", "Describe a parameter for a method or constructor", "param name=\"|\">"); - yield return factory.CreateLiteralCompletionData ("paramref", "Identify that a word is a parameter name", "paramref name=\"|\"/>"); - - yield return factory.CreateLiteralCompletionData ("permission", "Document the security accessibility of a member", "permission cref=\"|\""); - yield return factory.CreateLiteralCompletionData ("remarks", "Describe a type"); - yield return factory.CreateLiteralCompletionData ("returns", "Describe the return value of a method"); - yield return factory.CreateLiteralCompletionData ("see", "Specify a link", "see cref=\"|\"/>"); - yield return factory.CreateLiteralCompletionData ("seealso", "Generate a See Also entry", "seealso cref=\"|\"/>"); - yield return factory.CreateLiteralCompletionData ("summary", "Describe a member of a type"); - yield return factory.CreateLiteralCompletionData ("typeparam", "Describe a type parameter for a generic type or method"); - yield return factory.CreateLiteralCompletionData ("typeparamref", "Identify that a word is a type parameter name"); - yield return factory.CreateLiteralCompletionData ("value", "Describe a property"); - } - #endregion - - #region Keywords - static string[] expressionLevelKeywords = new string [] { "as", "is", "else", "out", "ref", "null", "delegate", "default"}; - - static string[] primitiveTypesKeywords = new string [] { "void", "object", "bool", "byte", "sbyte", "char", "short", "int", "long", "ushort", "uint", "ulong", "float", "double", "decimal", "string"}; - - static string[] statementStartKeywords = new string [] { "base", "new", "sizeof", "this", - "true", "false", "typeof", "checked", "unchecked", "from", "break", "checked", - "unchecked", "const", "continue", "do", "finally", "fixed", "for", "foreach", - "goto", "if", "lock", "return", "stackalloc", "switch", "throw", "try", "unsafe", - "using", "while", "yield", "dynamic", "var" }; - - static string[] globalLevelKeywords = new string [] { - "namespace", "using", "extern", "public", "internal", - "class", "interface", "struct", "enum", "delegate", - "abstract", "sealed", "static", "unsafe", "partial" - }; - - static string[] typeLevelKeywords = new string [] { - "public", "internal", "protected", "private", - "class", "interface", "struct", "enum", "delegate", - "abstract", "sealed", "static", "unsafe", "partial", - "const", "event", "extern", "fixed","new", - "operator", "explicit", "implicit", - "override", "readonly", "virtual", "volatile" - }; - static string[] linqKeywords = new string[] { "from", "where", "select", "group", "into", "orderby", "join", "let", "in", "on", "equals", "by", "ascending", "descending" }; - #endregion - } -} - +//
+// CSharpCompletionEngine.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using ICSharpCode.NRefactory.Completion;
+using ICSharpCode.NRefactory.CSharp.Refactoring;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+using ICSharpCode.NRefactory.Editor;
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.TypeSystem;
+
+namespace ICSharpCode.NRefactory.CSharp.Completion
+{
+ public class CSharpCompletionEngine : CSharpCompletionEngineBase
+ {
+ internal ICompletionDataFactory factory;
+
+ #region Additional input properties
+ public CSharpFormattingOptions FormattingPolicy { get; set; }
+ public string EolMarker { get; set; }
+ public string IndentString { get; set; }
+ #endregion
+
+ #region Result properties
+ public bool AutoCompleteEmptyMatch;
+ public bool AutoSelect;
+ public string DefaultCompletionString;
+ #endregion
+
+ public CSharpCompletionEngine (IDocument document, ICompletionDataFactory factory)
+ {
+ this.document = document;
+ this.factory = factory;
+ }
+
+ public IEnumerable<ICompletionData> GetCompletionData (int offset, bool controlSpace)
+ {
+ this.AutoCompleteEmptyMatch = true;
+ this.AutoSelect = true;
+ this.DefaultCompletionString = null;
+ SetOffset (offset);
+ if (offset > 0) {
+ char lastChar = document.GetCharAt (offset - 1);
+ var result = MagicKeyCompletion (lastChar, controlSpace) ?? Enumerable.Empty<ICompletionData> ();
+ if (controlSpace && char.IsWhiteSpace (lastChar)) {
+ offset -= 2;
+ while (offset >= 0 && char.IsWhiteSpace (document.GetCharAt (offset)))
+ offset--;
+ if (offset > 0) {
+ var nonWsResult = MagicKeyCompletion (document.GetCharAt (offset), controlSpace);
+ if (nonWsResult != null)
+ result = result.Concat (nonWsResult);
+ }
+ }
+
+ return result;
+ }
+ return Enumerable.Empty<ICompletionData> ();
+ }
+
+ IEnumerable<ICompletionData> MagicKeyCompletion (char completionChar, bool controlSpace)
+ {
+ switch (completionChar) {
+ // Magic key completion
+ case ':':
+ case '.':
+ if (IsInsideComment () || IsInsideString ())
+ return Enumerable.Empty<ICompletionData> ();
+ var expr = GetExpressionBeforeCursor ();
+ if (expr == null)
+ return null;
+ // do not complete <number>. (but <number>.<number>.)
+ if (expr.Item2 is PrimitiveExpression) {
+ var pexpr = (PrimitiveExpression)expr.Item2;
+ if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains ('.'))
+ return null;
+ }
+
+
+ var resolveResult = ResolveExpression (expr.Item1, expr.Item2, expr.Item3);
+
+ if (resolveResult == null)
+ return null;
+ if (expr.Item2 is AstType)
+ return CreateTypeAndNamespaceCompletionData (location, resolveResult.Item1, expr.Item2, resolveResult.Item2);
+
+ return CreateCompletionData (location, resolveResult.Item1, expr.Item2, resolveResult.Item2);
+ case '#':
+ if (IsInsideComment () || IsInsideString ())
+ return null;
+ return GetDirectiveCompletionData ();
+
+// XML doc completion
+ case '<':
+ if (IsInsideDocComment ())
+ return GetXmlDocumentationCompletionData ();
+ if (controlSpace)
+ return DefaultControlSpaceItems ();
+ return null;
+ case '>':
+ if (!IsInsideDocComment ())
+ return null;
+ string lineText = document.GetText (document.GetLineByNumber (location.Line));
+ int startIndex = Math.Min (location.Column - 1, lineText.Length - 1);
+
+ while (startIndex >= 0 && lineText [startIndex] != '<') {
+ --startIndex;
+ if (lineText [startIndex] == '/') { // already closed.
+ startIndex = -1;
+ break;
+ }
+ }
+
+ if (startIndex >= 0) {
+ int endIndex = startIndex;
+ while (endIndex <= location.Column && endIndex < lineText.Length && !Char.IsWhiteSpace (lineText [endIndex])) {
+ endIndex++;
+ }
+ string tag = endIndex - startIndex - 1 > 0 ? lineText.Substring (startIndex + 1, endIndex - startIndex - 2) : null;
+ if (!string.IsNullOrEmpty (tag) && commentTags.IndexOf (tag) >= 0)
+ document.Insert (offset, "</" + tag + ">");
+ }
+ return null;
+
+ // Parameter completion
+ case '(':
+ if (IsInsideComment () || IsInsideString ())
+ return null;
+ var invoke = GetInvocationBeforeCursor (true);
+ if (invoke == null)
+ return null;
+ if (invoke.Item2 is TypeOfExpression)
+ return CreateTypeList ();
+ var invocationResult = ResolveExpression (invoke.Item1, invoke.Item2, invoke.Item3);
+ if (invocationResult == null)
+ return null;
+ var methodGroup = invocationResult.Item1 as MethodGroupResolveResult;
+ if (methodGroup != null)
+ return CreateParameterCompletion (methodGroup, invocationResult.Item2, invoke.Item2, 0, controlSpace);
+ return null;
+ case ',':
+ int cpos2;
+ if (!GetParameterCompletionCommandOffset (out cpos2))
+ return null;
+ // completionContext = CompletionWidget.CreateCodeCompletionContext (cpos2);
+ // int currentParameter2 = MethodParameterDataProvider.GetCurrentParameterIndex (CompletionWidget, completionContext) - 1;
+// return CreateParameterCompletion (CreateResolver (), location, ExpressionContext.MethodBody, provider.Methods, currentParameter);
+ break;
+
+ // Completion on space:
+ case ' ':
+ if (IsInsideComment () || IsInsideString ())
+ return null;
+
+ int tokenIndex = offset;
+ string token = GetPreviousToken (ref tokenIndex, false);
+
+ // check propose name, for context <variable name> <ctrl+space> (but only in control space context)
+ IType isAsType = null;
+ var isAsExpression = GetExpressionAt (offset);
+ if (controlSpace && isAsExpression != null && isAsExpression.Item2 is VariableDeclarationStatement && token != "new") {
+ var parent = isAsExpression.Item2 as VariableDeclarationStatement;
+ int offset1 = document.GetOffset (parent.Type.StartLocation);
+ int offset2 = document.GetOffset (parent.Type.EndLocation);
+
+ string name = document.GetText (offset1, offset2 - offset1);
+ var names = new List<string> ();
+ int lastNameStart = 0;
+ for (int i = 1; i < name.Length; i++) {
+ if (Char.IsUpper (name [i])) {
+ names.Add (name.Substring (lastNameStart, i - lastNameStart));
+ lastNameStart = i;
+ }
+ }
+ names.Add (name.Substring (lastNameStart, name.Length - lastNameStart));
+ var proposeNameList = new CompletionDataWrapper (this);
+ var possibleName = new StringBuilder ();
+ for (int i = 0; i < names.Count; i++) {
+ possibleName.Length = 0;
+ for (int j = i; j < names.Count; j++) {
+ if (string.IsNullOrEmpty (names [j]))
+ continue;
+ if (j == i)
+ names [j] = Char.ToLower (names [j] [0]) + names [j].Substring (1);
+ possibleName.Append (names [j]);
+ }
+ if (possibleName.Length > 0)
+ proposeNameList.Result.Add (factory.CreateLiteralCompletionData (possibleName.ToString ()));
+ }
+
+ AutoSelect = false;
+ AutoCompleteEmptyMatch = false;
+ return proposeNameList.Result;
+ }
+// int tokenIndex = offset;
+// string token = GetPreviousToken (ref tokenIndex, false);
+// if (result.ExpressionContext == ExpressionContext.ObjectInitializer) {
+// resolver = CreateResolver ();
+// ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForObjectInitializer (document, resolver.Unit, Document.FileName, resolver.CallingType);
+// IReturnType objectInitializer = ((ExpressionContext.TypeExpressionContext)exactContext).UnresolvedType;
+// if (objectInitializer != null && objectInitializer.ArrayDimensions == 0 && objectInitializer.PointerNestingLevel == 0 && (token == "{" || token == ","))
+// return CreateCtrlSpaceCompletionData (completionContext, result);
+// }
+ if (token == "=") {
+ int j = tokenIndex;
+ string prevToken = GetPreviousToken (ref j, false);
+ if (prevToken == "=" || prevToken == "+" || prevToken == "-") {
+ token = prevToken + token;
+ tokenIndex = j;
+ }
+ }
+ switch (token) {
+ case "(":
+ case ",":
+ int cpos;
+ if (!GetParameterCompletionCommandOffset (out cpos))
+ break;
+ int currentParameter = GetCurrentParameterIndex (cpos, 0) - 1;
+ if (currentParameter < 0)
+ return null;
+ invoke = GetInvocationBeforeCursor (token == "(");
+ if (invoke == null)
+ return null;
+ invocationResult = ResolveExpression (invoke.Item1, invoke.Item2, invoke.Item3);
+ if (invocationResult == null)
+ return null;
+ methodGroup = invocationResult.Item1 as MethodGroupResolveResult;
+ if (methodGroup != null)
+ return CreateParameterCompletion (methodGroup, invocationResult.Item2, invoke.Item2, currentParameter, controlSpace);
+ return null;
+ case "=":
+ case "==":
+ GetPreviousToken (ref tokenIndex, false);
+
+ var expressionOrVariableDeclaration = GetExpressionAt (tokenIndex);
+ if (expressionOrVariableDeclaration == null)
+ return null;
+
+ resolveResult = ResolveExpression (expressionOrVariableDeclaration.Item1, expressionOrVariableDeclaration.Item2, expressionOrVariableDeclaration.Item3);
+ if (resolveResult == null)
+ return null;
+
+ if (resolveResult.Item1.Type.Kind == TypeKind.Enum) {
+ var wrapper = new CompletionDataWrapper (this);
+ AddContextCompletion (wrapper, resolveResult.Item2, expressionOrVariableDeclaration.Item2);
+ AddEnumMembers (wrapper, resolveResult.Item1.Type, resolveResult.Item2);
+ AutoCompleteEmptyMatch = false;
+ return wrapper.Result;
+ }
+//
+// if (resolvedType.FullName == DomReturnType.Bool.FullName) {
+// CompletionDataList completionList = new ProjectDomCompletionDataList ();
+// CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location);
+// completionList.AutoCompleteEmptyMatch = false;
+// cdc.Add ("true", "md-keyword");
+// cdc.Add ("false", "md-keyword");
+// resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc);
+// return completionList;
+// }
+// if (resolvedType.ClassType == ClassType.Delegate && token == "=") {
+// CompletionDataList completionList = new ProjectDomCompletionDataList ();
+// string parameterDefinition = AddDelegateHandlers (completionList, resolvedType);
+// string varName = GetPreviousMemberReferenceExpression (tokenIndex);
+// completionList.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.CallingMember, resolvedType));
+//
+// CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location);
+// resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc);
+// foreach (var data in completionList) {
+// if (data is MemberCompletionData)
+// ((MemberCompletionData)data).IsDelegateExpected = true;
+// }
+// return completionList;
+// }
+ return null;
+ case "+=":
+ case "-=":
+ GetPreviousToken (ref tokenIndex, false);
+
+ expressionOrVariableDeclaration = GetExpressionAt (tokenIndex);
+ if (expressionOrVariableDeclaration == null)
+ return null;
+
+ resolveResult = ResolveExpression (expressionOrVariableDeclaration.Item1, expressionOrVariableDeclaration.Item2, expressionOrVariableDeclaration.Item3);
+ if (resolveResult == null)
+ return null;
+
+
+ var mrr = resolveResult.Item1 as MemberResolveResult;
+ if (mrr != null) {
+ var evt = mrr.Member as IEvent;
+ if (evt == null)
+ return null;
+ var delegateType = evt.ReturnType.Resolve (ctx);
+ if (!delegateType.IsDelegate ())
+ return null;
+
+ var wrapper = new CompletionDataWrapper (this);
+ if (currentType != null) {
+// bool includeProtected = DomType.IncludeProtected (dom, typeFromDatabase, resolver.CallingType);
+ foreach (var method in currentType.GetMethods (ctx)) {
+ if (MatchDelegate (delegateType, method) /*&& method.IsAccessibleFrom (dom, resolver.CallingType, resolver.CallingMember, includeProtected) &&*/) {
+ wrapper.AddMember (method);
+// data.SetText (data.CompletionText + ";");
+ }
+ }
+ }
+ if (token == "+=") {
+ string parameterDefinition = AddDelegateHandlers (wrapper, delegateType);
+ string varName = GetPreviousMemberReferenceExpression (tokenIndex);
+ wrapper.Result.Add (factory.CreateEventCreationCompletionData (varName, delegateType, evt, parameterDefinition, currentMember, currentType));
+ }
+
+ return wrapper.Result;
+ }
+ return null;
+ case ":":
+ if (currentMember == null) {
+ var wrapper = new CompletionDataWrapper (this);
+ AddTypesAndNamespaces (wrapper, GetState (), null, t => currentType != null ? !currentType.Equals (t) : true);
+ return wrapper.Result;
+ }
+ return null;
+ }
+
+ var keywordCompletion = HandleKeywordCompletion (tokenIndex, token);
+ if (keywordCompletion == null && controlSpace)
+ goto default;
+ return keywordCompletion;
+ // Automatic completion
+ default:
+ if (IsInsideComment () || IsInsideString ())
+ return null;
+ var identifierStart = GetExpressionAtCursor ();
+ if (IsInLinqContext (offset)) {
+ tokenIndex = offset;
+ token = GetPreviousToken (ref tokenIndex, false); // token last typed
+ if (linqKeywords.Contains (token)) {
+ if (token == "from") // after from no auto code completion.
+ return null;
+ return DefaultControlSpaceItems ();
+ }
+ var dataList = new CompletionDataWrapper (this);
+ AddKeywords (dataList, linqKeywords);
+ return dataList.Result;
+ }
+
+ var contextList = new CompletionDataWrapper (this);
+ if (identifierStart == null) {
+ var unit = ParseStub ("get; }", false);
+ var node = unit.GetNodeAt (location, cn => !(cn is CSharpTokenNode));
+ if (node is Accessor)
+ node = node.Parent;
+ if (node is PropertyDeclaration) {
+ contextList.AddCustom ("get");
+ contextList.AddCustom ("set");
+ AddKeywords (contextList, accessorModifierKeywords);
+ } else if (node is CustomEventDeclaration) {
+ contextList.AddCustom ("add");
+ contextList.AddCustom ("remove");
+ } else {
+ AddContextCompletion (contextList, GetState (), null);
+ }
+
+ return contextList.Result;
+ }
+
+ if (!(char.IsLetter (completionChar) || completionChar == '_') && (identifierStart == null || !(identifierStart.Item2 is ArrayInitializerExpression)))
+ return controlSpace ? DefaultControlSpaceItems () : null;
+ char prevCh = offset > 2 ? document.GetCharAt (offset - 2) : '\0';
+ char nextCh = offset < document.TextLength ? document.GetCharAt (offset) : ' ';
+ const string allowedChars = ";,[(){}+-*/%^?:&|~!<>=";
+ if (!Char.IsWhiteSpace (nextCh) && allowedChars.IndexOf (nextCh) < 0)
+ return null;
+ if (!(Char.IsWhiteSpace (prevCh) || allowedChars.IndexOf (prevCh) >= 0))
+ return null;
+ // Do not pop up completion on identifier identifier (should be handled by keyword completion).
+ tokenIndex = offset - 1;
+ token = GetPreviousToken (ref tokenIndex, false);
+ if (string.IsNullOrEmpty (token) && !(IsInsideComment (tokenIndex) || IsInsideString (tokenIndex))) {
+ char last = token [token.Length - 1];
+ if (!char.IsLetterOrDigit (last) && last != '_')
+ return null;
+ }
+ CSharpResolver csResolver;
+ AstNode n = identifierStart.Item2;
+ if (n is ArrayInitializerExpression) {
+ var initalizerResult = ResolveExpression (identifierStart.Item1, n.Parent, identifierStart.Item3);
+ if (initalizerResult != null) {
+ foreach (var property in initalizerResult.Item1.Type.GetProperties (ctx)) {
+ if (!property.IsPublic)
+ continue;
+ contextList.AddMember (property);
+ }
+ foreach (var field in initalizerResult.Item1.Type.GetProperties (ctx)){
+ if (!field.IsPublic)
+ continue;
+ contextList.AddMember (field);
+ }
+ return contextList.Result;
+ }
+ return null;
+ }
+
+ if (n != null/* && !(identifierStart.Item2 is TypeDeclaration)*/) {
+ csResolver = new CSharpResolver (ctx, System.Threading.CancellationToken.None);
+ var nodes = new List<AstNode> ();
+ nodes.Add (n);
+ if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute)
+ nodes.Add (n.Parent);
+ var navigator = new NodeListResolveVisitorNavigator (nodes);
+ var visitor = new ResolveVisitor (csResolver, identifierStart.Item1, navigator);
+ visitor.Scan (identifierStart.Item3);
+ try {
+ csResolver = visitor.GetResolverStateBefore (n);
+ } catch (Exception) {
+ csResolver = GetState ();
+ }
+ // add attribute properties.
+ if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) {
+ var resolved = visitor.GetResolveResult (n.Parent);
+ if (resolved != null && resolved.Type != null) {
+ foreach (var property in resolved.Type.GetProperties (ctx).Where (p => p.Accessibility == Accessibility.Public)) {
+ contextList.AddMember (property);
+ }
+ foreach (var field in resolved.Type.GetFields (ctx).Where (p => p.Accessibility == Accessibility.Public)) {
+ contextList.AddMember (field);
+ }
+ }
+ }
+ } else {
+ csResolver = GetState ();
+ }
+
+ // identifier has already started with the first letter
+ offset--;
+
+ AddContextCompletion (contextList, csResolver, identifierStart.Item2);
+ return contextList.Result;
+// if (stub.Parent is BlockStatement)
+
+// result = FindExpression (dom, completionContext, -1);
+// if (result == null)
+// return null;
+// else if (result.ExpressionContext != ExpressionContext.IdentifierExpected) {
+// triggerWordLength = 1;
+// bool autoSelect = true;
+// IType returnType = null;
+// if ((prevCh == ',' || prevCh == '(') && GetParameterCompletionCommandOffset (out cpos)) {
+// ctx = CompletionWidget.CreateCodeCompletionContext (cpos);
+// NRefactoryParameterDataProvider dataProvider = ParameterCompletionCommand (ctx) as NRefactoryParameterDataProvider;
+// if (dataProvider != null) {
+// int i = dataProvider.GetCurrentParameterIndex (CompletionWidget, ctx) - 1;
+// foreach (var method in dataProvider.Methods) {
+// if (i < method.Parameters.Count) {
+// returnType = dom.GetType (method.Parameters [i].ReturnType);
+// autoSelect = returnType == null || returnType.ClassType != ClassType.Delegate;
+// break;
+// }
+// }
+// }
+// }
+// // Bug 677531 - Auto-complete doesn't always highlight generic parameter in method signature
+// //if (result.ExpressionContext == ExpressionContext.TypeName)
+// // autoSelect = false;
+// CompletionDataList dataList = CreateCtrlSpaceCompletionData (completionContext, result);
+// AddEnumMembers (dataList, returnType);
+// dataList.AutoSelect = autoSelect;
+// return dataList;
+// } else {
+// result = FindExpression (dom, completionContext, 0);
+// tokenIndex = offset;
+//
+// // check foreach case, unfortunately the expression finder is too dumb to handle full type names
+// // should be overworked if the expression finder is replaced with a mcs ast based analyzer.
+// var possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // starting letter
+// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varname
+//
+// // read return types to '(' token
+// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varType
+// if (possibleForeachToken == ">") {
+// while (possibleForeachToken != null && possibleForeachToken != "(") {
+// possibleForeachToken = GetPreviousToken (ref tokenIndex, false);
+// }
+// } else {
+// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // (
+// if (possibleForeachToken == ".")
+// while (possibleForeachToken != null && possibleForeachToken != "(")
+// possibleForeachToken = GetPreviousToken (ref tokenIndex, false);
+// }
+// possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // foreach
+//
+// if (possibleForeachToken == "foreach") {
+// result.ExpressionContext = ExpressionContext.ForeachInToken;
+// } else {
+// return null;
+// // result.ExpressionContext = ExpressionContext.IdentifierExpected;
+// }
+// result.Expression = "";
+// result.Region = DomRegion.Empty;
+//
+// return CreateCtrlSpaceCompletionData (completionContext, result);
+// }
+// break;
+ }
+ return null;
+ }
+
+ bool IsInLinqContext (int offset)
+ {
+ string token;
+ while (null != (token = GetPreviousToken (ref offset, true))) {
+ if (token == "from")
+ return true;
+ if (token == ";")
+ return false;
+ }
+ return false;
+ }
+
+ IEnumerable<ICompletionData> DefaultControlSpaceItems ()
+ {
+ var wrapper = new CompletionDataWrapper (this);
+ while (offset > 0 && char.IsWhiteSpace (document.GetCharAt (offset))) {
+ offset--;
+ }
+ location = document.GetLocation (offset);
+ var xp = GetExpressionAtCursor ();
+
+ AstNode node;
+ Tuple<ResolveResult, CSharpResolver> rr;
+ if (xp != null) {
+ node = xp.Item2;
+ rr = ResolveExpression (xp.Item1, node, xp.Item3);
+ } else {
+ node = Unit.GetNodeAt (location);
+ rr = ResolveExpression (CSharpParsedFile, node, Unit);
+ }
+
+ AddContextCompletion (wrapper, rr != null ? rr.Item2 : GetState (), node);
+
+ return wrapper.Result;
+ }
+
+ void AddContextCompletion (CompletionDataWrapper wrapper, CSharpResolver state, AstNode node)
+ {
+ if (state == null)
+ return;
+ foreach (var variable in state.LocalVariables) {
+ wrapper.AddVariable (variable);
+ }
+ if (state.CurrentMember is IParameterizedMember) {
+ var param = (IParameterizedMember)state.CurrentMember;
+ foreach (var p in param.Parameters) {
+ wrapper.AddVariable (p);
+ }
+ }
+
+ if (state.CurrentMember is IMethod) {
+ var method = (IMethod)state.CurrentMember;
+ foreach (var p in method.TypeParameters) {
+ wrapper.AddTypeParameter (p);
+ }
+ }
+ Predicate<ITypeDefinition> typePred = null;
+ if (node is Attribute) {
+ var attribute = ctx.GetTypeDefinition ("System", "Attribute", 0, StringComparer.Ordinal);
+ typePred = t => t.GetAllBaseTypeDefinitions (ctx).Any (bt => bt.Equals (attribute));
+ }
+ AddTypesAndNamespaces (wrapper, state, node, typePred);
+
+ wrapper.Result.Add (factory.CreateLiteralCompletionData ("global"));
+ if (state.CurrentMember != null) {
+ AddKeywords (wrapper, statementStartKeywords);
+ AddKeywords (wrapper, expressionLevelKeywords);
+ } else if (state.CurrentTypeDefinition != null) {
+ AddKeywords (wrapper, typeLevelKeywords);
+ } else {
+ AddKeywords (wrapper, globalLevelKeywords);
+ }
+
+ if (IsInSwitchContext(node)) {
+ wrapper.AddCustom ("case");
+ wrapper.AddCustom ("default");
+ }
+
+ AddKeywords (wrapper, primitiveTypesKeywords);
+ wrapper.Result.AddRange (factory.CreateCodeTemplateCompletionData ());
+ }
+
+ static bool IsInSwitchContext(AstNode node)
+ {
+ var n = node;
+ while (n != null && !(n is MemberDeclaration)) {
+ if (n is SwitchStatement)
+ return true;
+ if (n is BlockStatement)
+ return false;
+ n = n.Parent;
+ }
+ return false;
+ }
+
+ void AddTypesAndNamespaces (CompletionDataWrapper wrapper, CSharpResolver state, AstNode node, Predicate<ITypeDefinition> typePred = null, Predicate<IMember> memberPred = null)
+ {
+ var currentType = state.CurrentTypeDefinition ?? this.currentType;
+ var currentMember = state.CurrentMember ?? this.currentMember;
+ if (currentType != null) {
+ for (var ct = currentType; ct != null; ct = ct.DeclaringTypeDefinition) {
+ foreach (var nestedType in ct.NestedTypes) {
+ if (typePred == null || typePred (nestedType)) {
+ string name = nestedType.Name;
+ if (node is Attribute && name.EndsWith ("Attribute") && name.Length > "Attribute".Length)
+ name = name.Substring (0, name.Length - "Attribute".Length);
+ wrapper.AddType (nestedType, name);
+ }
+ }
+ }
+ if (currentMember != null) {
+ foreach (var member in currentType.Resolve (ctx).GetMembers (ctx)) {
+ if (memberPred == null || memberPred (member))
+ wrapper.AddMember (member);
+ }
+ }
+ foreach (var p in currentType.TypeParameters) {
+ wrapper.AddTypeParameter (p);
+ }
+ }
+
+ for (var n = state.CurrentUsingScope; n != null; n = n.Parent) {
+ foreach (var pair in n.UsingAliases) {
+ wrapper.AddNamespace ("", pair.Key);
+ }
+
+ foreach (var u in n.Usings) {
+ var ns = u.ResolveNamespace (ctx);
+ if (ns == null)
+ continue;
+ foreach (var type in ctx.GetTypes (ns.NamespaceName, StringComparer.Ordinal)) {
+ if (typePred == null || typePred (type)) {
+ string name = type.Name;
+ if (node is Attribute && name.EndsWith ("Attribute") && name.Length > "Attribute".Length)
+ name = name.Substring (0, name.Length - "Attribute".Length);
+ wrapper.AddType (type, name);
+ }
+ }
+ }
+
+ foreach (var type in ctx.GetTypes (n.NamespaceName, StringComparer.Ordinal)) {
+ if (typePred == null || typePred (type)) {
+ wrapper.AddType (type, type.Name);
+ }
+ }
+
+ foreach (var curNs in ctx.GetNamespaces ().Where (sn => sn.StartsWith (n.NamespaceName) && sn != n.NamespaceName)) {
+ wrapper.AddNamespace (n.NamespaceName, curNs);
+ }
+ }
+ }
+
+ IEnumerable<ICompletionData> HandleKeywordCompletion (int wordStart, string word)
+ {
+ if (IsInsideComment () || IsInsideString ())
+ return null;
+ switch (word) {
+ case "using":
+ case "namespace":
+ if (currentType != null)
+ return null;
+ var wrapper = new CompletionDataWrapper (this);
+ AddTypesAndNamespaces (wrapper, GetState (), null, t => false);
+ return wrapper.Result;
+ case "case":
+ return CreateCaseCompletionData (location);
+// case ",":
+// case ":":
+// if (result.ExpressionContext == ExpressionContext.InheritableType) {
+// IType cls = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset));
+// CompletionDataList completionList = new ProjectDomCompletionDataList ();
+// List<string > namespaceList = GetUsedNamespaces ();
+// var col = new CSharpTextEditorCompletion.CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, null, location);
+// bool isInterface = false;
+// HashSet<string > baseTypeNames = new HashSet<string> ();
+// if (cls != null) {
+// baseTypeNames.Add (cls.Name);
+// if (cls.ClassType == ClassType.Struct)
+// isInterface = true;
+// }
+// int tokenIndex = offset;
+//
+// // Search base types " : [Type1, ... ,TypeN,] <Caret>"
+// string token = null;
+// do {
+// token = GetPreviousToken (ref tokenIndex, false);
+// if (string.IsNullOrEmpty (token))
+// break;
+// token = token.Trim ();
+// if (Char.IsLetterOrDigit (token [0]) || token [0] == '_') {
+// IType baseType = dom.SearchType (Document.CompilationUnit, cls, result.Region.Start, token);
+// if (baseType != null) {
+// if (baseType.ClassType != ClassType.Interface)
+// isInterface = true;
+// baseTypeNames.Add (baseType.Name);
+// }
+// }
+// } while (token != ":");
+// foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) {
+// IType type = o as IType;
+// if (type != null && (type.IsStatic || type.IsSealed || baseTypeNames.Contains (type.Name) || isInterface && type.ClassType != ClassType.Interface)) {
+// continue;
+// }
+// if (o is Namespace && !namespaceList.Any (ns => ns.StartsWith (((Namespace)o).FullName)))
+// continue;
+// col.Add (o);
+// }
+// // Add inner classes
+// Stack<IType > innerStack = new Stack<IType> ();
+// innerStack.Push (cls);
+// while (innerStack.Count > 0) {
+// IType curType = innerStack.Pop ();
+// if (curType == null)
+// continue;
+// foreach (IType innerType in curType.InnerTypes) {
+// if (innerType != cls)
+// // don't add the calling class as possible base type
+// col.Add (innerType);
+// }
+// if (curType.DeclaringType != null)
+// innerStack.Push (curType.DeclaringType);
+// }
+// return completionList;
+// }
+// break;
+ case "is":
+ case "as":
+ if (currentType == null)
+ return null;
+ IType isAsType = null;
+ var isAsExpression = GetExpressionAt (wordStart);
+ if (isAsExpression != null) {
+ var parent = isAsExpression.Item2.Parent;
+ if (parent is VariableInitializer)
+ parent = parent.Parent;
+ if (parent is VariableDeclarationStatement) {
+ var resolved = ResolveExpression (isAsExpression.Item1, parent, isAsExpression.Item3);
+ if (resolved != null)
+ isAsType = resolved.Item1.Type;
+ }
+ }
+
+ var isAsWrapper = new CompletionDataWrapper (this);
+ AddTypesAndNamespaces (isAsWrapper, GetState (), null, t => isAsType == null || t.IsDerivedFrom (isAsType.GetDefinition (), ctx));
+ return isAsWrapper.Result;
+// {
+// CompletionDataList completionList = new ProjectDomCompletionDataList ();
+// ExpressionResult expressionResult = FindExpression (dom, completionContext, wordStart - document.Caret.Offset);
+// NRefactoryResolver resolver = CreateResolver ();
+// ResolveResult resolveResult = resolver.Resolve (expressionResult, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset));
+// if (resolveResult != null && resolveResult.ResolvedType != null) {
+// CompletionDataCollector col = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location);
+// IType foundType = null;
+// if (word == "as") {
+// ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForAsCompletion (document, Document.CompilationUnit, Document.FileName, resolver.CallingType);
+// if (exactContext is ExpressionContext.TypeExpressionContext) {
+// foundType = resolver.SearchType (((ExpressionContext.TypeExpressionContext)exactContext).Type);
+// AddAsCompletionData (col, foundType);
+// }
+// }
+//
+// if (foundType == null)
+// foundType = resolver.SearchType (resolveResult.ResolvedType);
+//
+// if (foundType != null) {
+// if (foundType.ClassType == ClassType.Interface)
+// foundType = resolver.SearchType (DomReturnType.Object);
+//
+// foreach (IType type in dom.GetSubclasses (foundType)) {
+// if (type.IsSpecialName || type.Name.StartsWith ("<"))
+// continue;
+// AddAsCompletionData (col, type);
+// }
+// }
+// List<string > namespaceList = GetUsedNamespaces ();
+// foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) {
+// if (o is IType) {
+// IType type = (IType)o;
+// if (type.ClassType != ClassType.Interface || type.IsSpecialName || type.Name.StartsWith ("<"))
+// continue;
+// // if (foundType != null && !dom.GetInheritanceTree (foundType).Any (x => x.FullName == type.FullName))
+// // continue;
+// AddAsCompletionData (col, type);
+// continue;
+// }
+// if (o is Namespace)
+// continue;
+// col.Add (o);
+// }
+// return completionList;
+// }
+// result.ExpressionContext = ExpressionContext.TypeName;
+// return CreateCtrlSpaceCompletionData (completionContext, result);
+// }
+ case "override":
+ // Look for modifiers, in order to find the beginning of the declaration
+ int firstMod = wordStart;
+ int i = wordStart;
+ for (int n = 0; n < 3; n++) {
+ string mod = GetPreviousToken (ref i, true);
+ if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") {
+ firstMod = i;
+ } else if (mod == "static") {
+ // static methods are not overridable
+ return null;
+ } else
+ break;
+ }
+ if (!IsLineEmptyUpToEol ())
+ return null;
+ var overrideCls = CSharpParsedFile.GetInnermostTypeDefinition (location);
+
+ if (overrideCls != null && (overrideCls.Kind == TypeKind.Class || overrideCls.Kind == TypeKind.Struct)) {
+ string modifiers = document.GetText (firstMod, wordStart - firstMod);
+ return GetOverrideCompletionData (overrideCls, modifiers);
+ }
+ return null;
+// case "partial":
+// // Look for modifiers, in order to find the beginning of the declaration
+// firstMod = wordStart;
+// i = wordStart;
+// for (int n = 0; n < 3; n++) {
+// string mod = GetPreviousToken (ref i, true);
+// if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") {
+// firstMod = i;
+// } else if (mod == "static") {
+// // static methods are not overridable
+// return null;
+// } else
+// break;
+// }
+// if (!IsLineEmptyUpToEol ())
+// return null;
+//
+// overrideCls = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset));
+// if (overrideCls != null && (overrideCls.ClassType == ClassType.Class || overrideCls.ClassType == ClassType.Struct)) {
+// string modifiers = document.GetTextBetween (firstMod, wordStart);
+// return GetPartialCompletionData (completionContext, overrideCls, modifiers);
+// }
+// return null;
+//
+ case "public":
+ case "protected":
+ case "private":
+ case "internal":
+ case "sealed":
+ case "static":
+ wrapper = new CompletionDataWrapper (this);
+ var state = GetState ();
+ AddTypesAndNamespaces (wrapper, state, null, null, m => false);
+ AddKeywords (wrapper, typeLevelKeywords);
+ AddKeywords (wrapper, primitiveTypesKeywords);
+ return wrapper.Result;
+ case "new":
+ int j = offset - 4;
+// string token = GetPreviousToken (ref j, true);
+
+ IType hintType = null;
+ var expressionOrVariableDeclaration = GetExpressionAt (j);
+ if (expressionOrVariableDeclaration != null && expressionOrVariableDeclaration.Item2 is VariableDeclarationStatement) {
+ var varDecl = (VariableDeclarationStatement)expressionOrVariableDeclaration.Item2;
+ var resolved = ResolveExpression (expressionOrVariableDeclaration.Item1, varDecl.Type, expressionOrVariableDeclaration.Item3);
+ if (resolved != null) {
+ hintType = resolved.Item1.Type;
+ }
+ }
+ return CreateTypeCompletionData (hintType);
+// IType callingType = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (document.Caret.Line, document.Caret.Column));
+// ExpressionContext newExactContext = new NewCSharpExpressionFinder (dom).FindExactContextForNewCompletion (document, Document.CompilationUnit, Document.FileName, callingType);
+// if (newExactContext is ExpressionContext.TypeExpressionContext)
+// return CreateTypeCompletionData (location, callingType, newExactContext, ((ExpressionContext.TypeExpressionContext)newExactContext).Type, ((ExpressionContext.TypeExpressionContext)newExactContext).UnresolvedType);
+// if (newExactContext == null) {
+// int j = offset - 4;
+//
+// string yieldToken = GetPreviousToken (ref j, true);
+// if (token == "return") {
+// NRefactoryResolver resolver = CreateResolver ();
+// resolver.SetupResolver (new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset));
+// IReturnType returnType = resolver.CallingMember.ReturnType;
+// if (yieldToken == "yield" && returnType.GenericArguments.Count > 0)
+// returnType = returnType.GenericArguments [0];
+// if (resolver.CallingMember != null)
+// return CreateTypeCompletionData (location, callingType, newExactContext, null, returnType);
+// }
+// }
+// return CreateCtrlSpaceCompletionData (completionContext, null);
+// case "if":
+// case "elif":
+// if (stateTracker.Engine.IsInsidePreprocessorDirective)
+// return GetDefineCompletionData ();
+// return null;
+ case "yield":
+ var yieldDataList = new CompletionDataWrapper (this);
+ DefaultCompletionString = "return";
+ yieldDataList.AddCustom ("break");
+ yieldDataList.AddCustom ("return");
+ return yieldDataList.Result;
+ case "in":
+ var inList = new CompletionDataWrapper (this);
+ var node = Unit.GetNodeAt (location);
+ var rr = ResolveExpression (CSharpParsedFile, node, Unit);
+ AddContextCompletion (inList, rr != null ? rr.Item2 : GetState (), node);
+ return inList.Result;
+// case "where":
+// CompletionDataList whereDataList = new CompletionDataList ();
+// NRefactoryResolver constraintResolver = CreateResolver ();
+// constraintResolver.SetupResolver (new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset));
+// if (constraintResolver.CallingMember is IMethod) {
+// foreach (ITypeParameter tp in ((IMethod)constraintResolver.CallingMember).TypeParameters) {
+// whereDataList.Add (tp.Name, "md-keyword");
+// }
+// } else {
+// if (constraintResolver.CallingType != null) {
+// foreach (ITypeParameter tp in constraintResolver.CallingType.TypeParameters) {
+// whereDataList.Add (tp.Name, "md-keyword");
+// }
+// }
+// }
+//
+// return whereDataList;
+ }
+// if (IsInLinqContext (result)) {
+// if (linqKeywords.Contains (word)) {
+// if (word == "from") // after from no auto code completion.
+// return null;
+// result.Expression = "";
+// return CreateCtrlSpaceCompletionData (completionContext, result);
+// }
+// CompletionDataList dataList = new ProjectDomCompletionDataList ();
+// CompletionDataCollector col = new CompletionDataCollector (this, dom, dataList, Document.CompilationUnit, null, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset));
+// foreach (string kw in linqKeywords) {
+// col.Add (kw, "md-keyword");
+// }
+// return dataList;
+// }
+ return null;
+ }
+
+ bool IsLineEmptyUpToEol ()
+ {
+ var line = document.GetLineByNumber (location.Line);
+ for (int j = offset; j < line.EndOffset; j++) {
+ char ch = document.GetCharAt (j);
+ if (!char.IsWhiteSpace (ch))
+ return false;
+ }
+ return true;
+ }
+ string GetLineIndent (int lineNr)
+ {
+ var line = document.GetLineByNumber (lineNr);
+ for (int j = offset; j < line.EndOffset; j++) {
+ char ch = document.GetCharAt (j);
+ if (!char.IsWhiteSpace (ch))
+ return document.GetText (line.Offset, j - line.Offset - 1);
+ }
+ return "";
+ }
+
+ IEnumerable<ICompletionData> CreateTypeCompletionData (IType hintType)
+ {
+ var wrapper = new CompletionDataWrapper (this);
+ var state = GetState ();
+ Predicate<ITypeDefinition> pred = null;
+ if (hintType != null && !hintType.Equals (SharedTypes.UnknownType)) {
+ var lookup = new MemberLookup (ctx, currentType, ProjectContent);
+ pred = t => {
+ // check if type is in inheritance tree.
+ if (hintType.GetDefinition () != null && !t.IsDerivedFrom (hintType.GetDefinition (), ctx))
+ return false;
+ // check for valid constructors
+ if (t.Methods.Count (m => m.IsConstructor) == 0)
+ return true;
+ bool isProtectedAllowed = currentType != null ? currentType.IsDerivedFrom (t, ctx) : false;
+ return t.Methods.Any (m => m.IsConstructor && lookup.IsAccessible (m, isProtectedAllowed));
+ };
+ DefaultCompletionString = GetShortType (hintType, GetState ());
+ wrapper.AddType (hintType, DefaultCompletionString);
+ }
+ AddTypesAndNamespaces (wrapper, state, null, pred, m => false);
+ AddKeywords (wrapper, primitiveTypesKeywords.Where (k => k != "void"));
+ AutoCompleteEmptyMatch = true;
+ return wrapper.Result;
+ }
+
+ IEnumerable<ICompletionData> GetOverrideCompletionData (ITypeDefinition type, string modifiers)
+ {
+ var wrapper = new CompletionDataWrapper (this);
+ var alreadyInserted = new Dictionary<string, bool> ();
+ bool addedVirtuals = false;
+
+ int declarationBegin = offset;
+ int j = declarationBegin;
+ for (int i = 0; i < 3; i++) {
+ switch (GetPreviousToken (ref j, true)) {
+ case "public":
+ case "protected":
+ case "private":
+ case "internal":
+ case "sealed":
+ case "override":
+ declarationBegin = j;
+ break;
+ case "static":
+ return null; // don't add override completion for static members
+ }
+ }
+ foreach (var baseType in type.GetAllBaseTypeDefinitions (ctx)) {
+ AddVirtuals (alreadyInserted, wrapper, type, modifiers, baseType, declarationBegin);
+ addedVirtuals = true;
+ }
+ if (!addedVirtuals)
+ AddVirtuals (alreadyInserted, wrapper, type, modifiers, ctx.GetTypeDefinition (typeof(object)), declarationBegin);
+ return wrapper.Result;
+ }
+
+ static string GetNameWithParamCount (IMember member)
+ {
+ var e = member as IMethod;
+ if (e == null || e.TypeParameters.Count == 0)
+ return member.Name;
+ return e.Name + "`" + e.TypeParameters.Count;
+ }
+
+ void AddVirtuals (Dictionary<string, bool> alreadyInserted, CompletionDataWrapper col, ITypeDefinition type, string modifiers, ITypeDefinition curType, int declarationBegin)
+ {
+ if (curType == null)
+ return;
+ foreach (var m in curType.Methods.Where (m => !m.IsConstructor && !m.IsDestructor).Cast<IMember> ().Concat (curType.Properties.Cast<IMember> ())) {
+ if (m.IsSynthetic || curType.Kind != TypeKind.Interface && !(m.IsVirtual || m.IsOverride || m.IsAbstract))
+ continue;
+ // filter out the "Finalize" methods, because finalizers should be done with destructors.
+ if (m is IMethod && m.Name == "Finalize")
+ continue;
+
+ var data = factory.CreateNewOverrideCompletionData (declarationBegin, type, m);
+ string text = GetNameWithParamCount (m);
+
+ // check if the member is already implemented
+ bool foundMember = type.Members.Any (cm => GetNameWithParamCount (cm) == text);
+ if (!foundMember && !alreadyInserted.ContainsKey (text)) {
+ alreadyInserted [text] = true;
+ data.CompletionCategory = col.GetCompletionCategory (curType);
+ col.Add (data);
+ }
+ }
+ }
+
+ static void AddKeywords (CompletionDataWrapper wrapper, IEnumerable<string> keywords)
+ {
+ foreach (string keyword in keywords) {
+ wrapper.AddCustom (keyword);
+ }
+ }
+
+ public string GetPreviousMemberReferenceExpression (int tokenIndex)
+ {
+ string result = GetPreviousToken (ref tokenIndex, false);
+ result = GetPreviousToken (ref tokenIndex, false);
+ if (result != ".") {
+ result = null;
+ } else {
+ var names = new List<string> ();
+ while (result == ".") {
+ result = GetPreviousToken (ref tokenIndex, false);
+ if (result == "this") {
+ names.Add ("handle");
+ } else if (result != null) {
+ string trimmedName = result.Trim ();
+ if (trimmedName.Length == 0)
+ break;
+ names.Insert (0, trimmedName);
+ }
+ result = GetPreviousToken (ref tokenIndex, false);
+ }
+ result = String.Join ("", names.ToArray ());
+ foreach (char ch in result) {
+ if (!char.IsLetterOrDigit (ch) && ch != '_') {
+ result = "";
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ bool MatchDelegate (IType delegateType, IMethod method)
+ {
+ var delegateMethod = delegateType.GetDelegateInvokeMethod ();
+ if (delegateMethod == null || delegateMethod.Parameters.Count != method.Parameters.Count)
+ return false;
+
+ for (int i = 0; i < delegateMethod.Parameters.Count; i++) {
+ if (!delegateMethod.Parameters [i].Type.Resolve (ctx).Equals (method.Parameters [i].Type.Resolve (ctx)))
+ return false;
+ }
+ return true;
+ }
+
+ string AddDelegateHandlers (CompletionDataWrapper completionList, IType delegateType, bool addSemicolon = true, bool addDefault = true)
+ {
+ IMethod delegateMethod = delegateType.GetDelegateInvokeMethod ();
+ var thisLineIndent = GetLineIndent (location.Line);
+ string delegateEndString = EolMarker + thisLineIndent + "}" + (addSemicolon ? ";" : "");
+ bool containsDelegateData = completionList.Result.Any (d => d.DisplayText.StartsWith ("delegate("));
+ if (addDefault)
+ completionList.AddCustom ("delegate", "Creates anonymous delegate.", "delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString);
+ var sb = new StringBuilder ("(");
+ var sbWithoutTypes = new StringBuilder ("(");
+ for (int k = 0; k < delegateMethod.Parameters.Count; k++) {
+ if (k > 0) {
+ sb.Append (", ");
+ sbWithoutTypes.Append (", ");
+ }
+ var parameterType = delegateMethod.Parameters [k].Type.Resolve (ctx);
+ sb.Append (GetShortType (parameterType, GetState ()));
+ sb.Append (" ");
+ sb.Append (delegateMethod.Parameters [k].Name);
+ sbWithoutTypes.Append (delegateMethod.Parameters [k].Name);
+ }
+ sb.Append (")");
+ sbWithoutTypes.Append (")");
+ completionList.AddCustom ("delegate" + sb, "Creates anonymous delegate.", "delegate" + sb + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString);
+ if (!completionList.Result.Any (data => data.DisplayText == sbWithoutTypes.ToString ()))
+ completionList.AddCustom (sbWithoutTypes.ToString (), "Creates lambda expression.", sbWithoutTypes + " => |" + (addSemicolon ? ";" : ""));
+ /* TODO:Make factory method out of it.
+ // It's needed to temporarly disable inserting auto matching bracket because the anonymous delegates are selectable with '('
+ // otherwise we would end up with () => )
+ if (!containsDelegateData) {
+ var savedValue = MonoDevelop.SourceEditor.DefaultSourceEditorOptions.Instance.AutoInsertMatchingBracket;
+ MonoDevelop.SourceEditor.DefaultSourceEditorOptions.Instance.AutoInsertMatchingBracket = false;
+ completionList.Result.CompletionListClosed += delegate {
+ MonoDevelop.SourceEditor.DefaultSourceEditorOptions.Instance.AutoInsertMatchingBracket = savedValue;
+ };
+ }*/
+ return sb.ToString ();
+ }
+
+ bool IsAccessibleFrom (IEntity member, ITypeDefinition calledType, IMember currentMember, bool includeProtected)
+ {
+ if (currentMember == null)
+ return member.IsStatic || member.IsPublic;
+// if (currentMember is MonoDevelop.Projects.Dom.BaseResolveResult.BaseMemberDecorator)
+// return member.IsPublic | member.IsProtected;
+ // if (member.IsStatic && !IsStatic)
+ // return false;
+ if (member.IsPublic || calledType != null && calledType.Kind == TypeKind.Interface && !member.IsProtected)
+ return true;
+ if (member.DeclaringTypeDefinition != null) {
+ if (member.DeclaringTypeDefinition.Kind == TypeKind.Interface)
+ return IsAccessibleFrom (member.DeclaringTypeDefinition, calledType, currentMember, includeProtected);
+
+ if (member.IsProtected && !(member.DeclaringTypeDefinition.IsProtectedOrInternal && !includeProtected))
+ return includeProtected;
+ }
+ if (member.IsInternal || member.IsProtectedAndInternal || member.IsProtectedOrInternal) {
+ var type1 = member is ITypeDefinition ? (ITypeDefinition)member : member.DeclaringTypeDefinition;
+ var type2 = currentMember is ITypeDefinition ? (ITypeDefinition)currentMember : currentMember.DeclaringTypeDefinition;
+ bool result;
+ // easy case, projects are the same
+ if (type1.ProjectContent == type2.ProjectContent) {
+ result = true;
+ } else if (type1.ProjectContent != null) {
+ // maybe type2 hasn't project dom set (may occur in some cases), check if the file is in the project
+ //TODO !!
+// result = type1.ProjectContent.Annotation<MonoDevelop.Projects.Project> ().GetProjectFile (type2.Region.FileName) != null;
+ result = false;
+ } else if (type2.ProjectContent != null) {
+ //TODO!!
+// result = type2.ProjectContent.Annotation<MonoDevelop.Projects.Project> ().GetProjectFile (type1.Region.FileName) != null;
+ result = false;
+ } else {
+ // should never happen !
+ result = true;
+ }
+ return member.IsProtectedAndInternal ? includeProtected && result : result;
+ }
+
+ if (!(currentMember is IType) && (currentMember.DeclaringTypeDefinition == null || member.DeclaringTypeDefinition == null))
+ return false;
+
+ // inner class
+ var declaringType = currentMember.DeclaringTypeDefinition;
+ while (declaringType != null) {
+ if (declaringType.ReflectionName == currentMember.DeclaringType.ReflectionName)
+ return true;
+ declaringType = declaringType.DeclaringTypeDefinition;
+ }
+
+
+ return currentMember.DeclaringTypeDefinition != null && member.DeclaringTypeDefinition.FullName == currentMember.DeclaringTypeDefinition.FullName;
+ }
+
+ IEnumerable<ICompletionData> CreateTypeAndNamespaceCompletionData (TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state)
+ {
+ if (resolveResult == null || resolveResult.IsError)
+ return null;
+ var result = new CompletionDataWrapper (this);
+
+ if (resolveResult is NamespaceResolveResult) {
+ var nr = (NamespaceResolveResult)resolveResult;
+ foreach (var cl in ctx.GetTypes (nr.NamespaceName, StringComparer.Ordinal)) {
+ result.AddType (cl, cl.Name);
+ }
+ foreach (var ns in ctx.GetNamespaces ().Where (n => n.Length > nr.NamespaceName.Length && n.StartsWith (nr.NamespaceName))) {
+ result.AddNamespace (nr.NamespaceName, ns);
+ }
+ } else if (resolveResult is TypeResolveResult) {
+ var type = resolveResult.Type.Resolve (ctx);
+ foreach (var nested in type.GetNestedTypes (ctx)) {
+ result.AddType (nested, nested.Name);
+ }
+ }
+ return result.Result;
+ }
+
+ IEnumerable<ICompletionData> CreateTypeList ()
+ {
+ foreach (var cl in ctx.GetTypes ("", StringComparer.Ordinal)) {
+ yield return factory.CreateTypeCompletionData (cl, cl.Name);
+ }
+ foreach (var ns in ctx.GetNamespaces ()) {
+ string name = ns;
+ int idx = name.IndexOf (".");
+ if (idx >= 0)
+ name = name.Substring (0, idx);
+ yield return factory.CreateNamespaceCompletionData (name);
+ }
+ }
+
+ public ITypeDefinition GetTypeFromContext (ITypeDefinition type)
+ {
+ if (type == null || type.DeclaringType != null)
+ return type;
+ var result = ctx.GetTypeDefinition (type.Namespace, type.Name, type.TypeParameterCount, StringComparer.Ordinal) ?? type;
+ if (result.GetParts ().Count == 1)
+ return type;
+// Console.WriteLine ("result:"+ result);
+ return result;
+ }
+
+ IEnumerable<ICompletionData> CreateParameterCompletion (MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, int parameter, bool controlSpace)
+ {
+ var result = new CompletionDataWrapper (this);
+ var addedEnums = new HashSet<string> ();
+ var addedDelegates = new HashSet<string> ();
+
+ foreach (var method in resolveResult.Methods) {
+ if (method.Parameters.Count <= parameter)
+ continue;
+ var resolvedType = method.Parameters [parameter].Type.Resolve (ctx);
+ if (resolvedType.Kind == TypeKind.Enum) {
+ if (addedEnums.Contains (resolvedType.ReflectionName))
+ continue;
+ addedEnums.Add (resolvedType.ReflectionName);
+ AddEnumMembers (result, resolvedType, state);
+ } else if (resolvedType.Kind == TypeKind.Delegate) {
+// if (addedDelegates.Contains (resolvedType.DecoratedFullName))
+// continue;
+// addedDelegates.Add (resolvedType.DecoratedFullName);
+// string parameterDefinition = AddDelegateHandlers (completionList, resolvedType, false, addedDelegates.Count == 1);
+// string varName = "Handle" + method.Parameters [parameter].ReturnType.Name + method.Parameters [parameter].Name;
+// result.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.Unit.GetMemberAt (location), resolvedType) { AddSemicolon = false });
+
+ }
+ }
+ if (!controlSpace) {
+ if (addedEnums.Count + addedDelegates.Count == 0)
+ return Enumerable.Empty<ICompletionData> ();
+ AutoCompleteEmptyMatch = false;
+ AutoSelect = false;
+ }
+ AddContextCompletion (result, state, invocation);
+
+// resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc);
+// if (addedDelegates.Count > 0) {
+// foreach (var data in result.Result) {
+// if (data is MemberCompletionData)
+// ((MemberCompletionData)data).IsDelegateExpected = true;
+// }
+// }
+ return result.Result;
+ }
+
+ string GetShortType (IType type, CSharpResolver state)
+ {
+ var builder = new TypeSystemAstBuilder (state);
+ var shortType = builder.ConvertType (type);
+ using (var w = new System.IO.StringWriter ()) {
+ var visitor = new CSharpOutputVisitor (w, FormattingPolicy);
+ shortType.AcceptVisitor (visitor, null);
+ return w.ToString ();
+ }
+ }
+
+ void AddEnumMembers (CompletionDataWrapper completionList, IType resolvedType, CSharpResolver state)
+ {
+ if (resolvedType.Kind != TypeKind.Enum)
+ return;
+ string typeString = GetShortType (resolvedType, state);
+ if (typeString.Contains ("."))
+ completionList.AddType (resolvedType, typeString);
+ foreach (var field in resolvedType.GetFields (ctx)) {
+ if (field.IsConst || field.IsStatic)
+ completionList.Result.Add (factory.CreateEntityCompletionData (field, typeString + "." + field.Name));
+ }
+ DefaultCompletionString = typeString;
+ }
+
+ IEnumerable<ICompletionData> CreateCompletionData (TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state)
+ {
+ if (resolveResult == null /*|| resolveResult.IsError*/)
+ return null;
+ if (resolveResult is NamespaceResolveResult) {
+ var nr = (NamespaceResolveResult)resolveResult;
+ var namespaceContents = new CompletionDataWrapper (this);
+ foreach (var cl in ctx.GetTypes (nr.NamespaceName, StringComparer.Ordinal)) {
+ namespaceContents.AddType (cl, cl.Name);
+ }
+ foreach (var ns in ctx.GetNamespaces ().Where (n => n.Length > nr.NamespaceName.Length && n.StartsWith (nr.NamespaceName))) {
+ namespaceContents.AddNamespace (nr.NamespaceName, ns);
+ }
+
+ return namespaceContents.Result;
+ }
+
+ IType type = GetTypeFromContext (resolveResult.Type.GetDefinition ()) ?? resolveResult.Type;
+ var typeDef = resolveResult.Type.GetDefinition ();
+ var lookup = new MemberLookup (ctx, currentType, ProjectContent);
+ var result = new CompletionDataWrapper (this);
+ bool isProtectedAllowed = false;
+ bool includeStaticMembers = false;
+
+ if (resolveResult is LocalResolveResult) {
+ isProtectedAllowed = currentType != null && typeDef != null ? typeDef.GetAllBaseTypeDefinitions (ctx).Any (bt => bt.Equals (currentType)) : false;
+ if (resolvedNode is IdentifierExpression) {
+ var mrr = (LocalResolveResult)resolveResult;
+ includeStaticMembers = mrr.Variable.Name == mrr.Type.Name;
+ }
+ } else {
+ isProtectedAllowed = currentType != null && typeDef != null ? currentType.GetAllBaseTypeDefinitions (ctx).Any (bt => bt.Equals (typeDef)) : false;
+ }
+ if (resolveResult is TypeResolveResult && type.Kind == TypeKind.Enum) {
+ foreach (var field in type.GetFields (ctx)) {
+ result.AddMember (field);
+ }
+ foreach (var m in type.GetMethods (ctx)) {
+ if (m.Name == "TryParse")
+ result.AddMember (m);
+ }
+ return result.Result;
+ }
+
+ if (resolveResult is MemberResolveResult && resolvedNode is IdentifierExpression) {
+ var mrr = (MemberResolveResult)resolveResult;
+ includeStaticMembers = mrr.Member.Name == mrr.Type.Name;
+ }
+
+// Console.WriteLine ("type:" + type +"/"+type.GetType ());
+// Console.WriteLine ("IS PROT ALLOWED:" + isProtectedAllowed);
+// Console.WriteLine (resolveResult);
+// Console.WriteLine (currentMember != null ? currentMember.IsStatic : "currentMember == null");
+ if (resolvedNode.Annotation<ObjectCreateExpression> () == null) { //tags the created expression as part of an object create expression.
+ foreach (var member in type.GetMembers (ctx)) {
+ if (!lookup.IsAccessible (member, isProtectedAllowed)) {
+ // Console.WriteLine ("skip access: " + member.FullName);
+ continue;
+ }
+ if (resolvedNode is BaseReferenceExpression && member.IsAbstract)
+ continue;
+
+ if (!includeStaticMembers && member.IsStatic && !(resolveResult is TypeResolveResult)) {
+ // Console.WriteLine ("skip static member: " + member.FullName);
+ continue;
+ }
+ if (!member.IsStatic && (resolveResult is TypeResolveResult)) {
+ // Console.WriteLine ("skip non static member: " + member.FullName);
+ continue;
+ }
+ // Console.WriteLine ("add : "+ member.FullName + " --- " + member.IsStatic);
+ result.AddMember (member);
+ }
+ }
+
+ if (resolveResult is TypeResolveResult || includeStaticMembers) {
+ foreach (var nested in type.GetNestedTypes (ctx)) {
+ result.AddType (nested, nested.Name);
+ }
+
+ } else {
+ var baseTypes = new List<IType> (type.GetAllBaseTypes (ctx));
+ var conv = new Conversions (ctx);
+ for (var n = state.CurrentUsingScope; n != null; n = n.Parent) {
+ AddExtensionMethods (result, conv, baseTypes, n.NamespaceName);
+ foreach (var u in n.Usings) {
+ var ns = u.ResolveNamespace (ctx);
+ if (ns == null)
+ continue;
+ AddExtensionMethods (result, conv, baseTypes, ns.NamespaceName);
+ }
+ }
+ }
+
+// IEnumerable<object> objects = resolveResult.CreateResolveResult (dom, resolver != null ? resolver.CallingMember : null);
+// CompletionDataCollector col = new CompletionDataCollector (this, dom, result, Document.CompilationUnit, resolver != null ? resolver.CallingType : null, location);
+// col.HideExtensionParameter = !resolveResult.StaticResolve;
+// col.NamePrefix = expressionResult.Expression;
+// bool showOnlyTypes = expressionResult.Contexts.Any (ctx => ctx == ExpressionContext.InheritableType || ctx == ExpressionContext.Constraints);
+// if (objects != null) {
+// foreach (object obj in objects) {
+// if (expressionResult.ExpressionContext != null && expressionResult.ExpressionContext.FilterEntry (obj))
+// continue;
+// if (expressionResult.ExpressionContext == ExpressionContext.NamespaceNameExcepted && !(obj is Namespace))
+// continue;
+// if (showOnlyTypes && !(obj is IType))
+// continue;
+// CompletionData data = col.Add (obj);
+// if (data != null && expressionResult.ExpressionContext == ExpressionContext.Attribute && data.CompletionText != null && data.CompletionText.EndsWith ("Attribute")) {
+// string newText = data.CompletionText.Substring (0, data.CompletionText.Length - "Attribute".Length);
+// data.SetText (newText);
+// }
+// }
+// }
+
+ return result.Result;
+ }
+
+ void AddExtensionMethods (CompletionDataWrapper result, Conversions conv, List<IType> baseTypes, string namespaceName)
+ {
+ foreach (var typeDefinition in ctx.GetTypes (namespaceName, StringComparer.Ordinal).Where (t => t.IsStatic && t.HasExtensionMethods)) {
+ foreach (var m in typeDefinition.Methods.Where (m => m.IsExtensionMethod )) {
+ var pt = m.Parameters.First ().Type.Resolve (ctx);
+ string reflectionName = pt is ParameterizedType ? ((ParameterizedType)pt).GetDefinition ().ReflectionName : pt.ReflectionName;
+ if (baseTypes.Any (bt => (bt is ParameterizedType ? ((ParameterizedType)bt).GetDefinition ().ReflectionName : bt.ReflectionName) == reflectionName)) {
+ result.AddMember (m);
+ }
+ }
+ }
+ }
+ IEnumerable<ICompletionData> CreateCaseCompletionData (TextLocation location)
+ {
+ var unit = ParseStub ("a: break;");
+ if (unit == null)
+ return null;
+ var s = unit.GetNodeAt<SwitchStatement> (location);
+ if (s == null)
+ return null;
+
+ var offset = document.GetOffset (s.Expression.StartLocation);
+ var expr = GetExpressionAt (offset);
+ if (expr == null)
+ return null;
+
+ var resolveResult = ResolveExpression (expr.Item1, expr.Item2, expr.Item3);
+ if (resolveResult == null || resolveResult.Item1.Type.Kind != TypeKind.Enum)
+ return null;
+ var wrapper = new CompletionDataWrapper (this);
+ AddEnumMembers (wrapper, resolveResult.Item1.Type, resolveResult.Item2);
+ AutoCompleteEmptyMatch = false;
+ return wrapper.Result;
+ }
+
+ #region Parsing methods
+ Tuple<CSharpParsedFile, AstNode, CompilationUnit> GetExpressionBeforeCursor ()
+ {
+ CompilationUnit baseUnit;
+ if (currentMember == null) {
+ baseUnit = ParseStub ("st {}", false);
+ var type = baseUnit.GetNodeAt<MemberType> (location);
+ if (type == null) {
+ baseUnit = ParseStub ("a;", false);
+ type = baseUnit.GetNodeAt<MemberType> (location);
+ }
+ if (type != null) {
+ // insert target type into compilation unit, to respect the
+ var target = type.Target;
+ target.Remove ();
+ var node = Unit.GetNodeAt (location) ?? Unit;
+ node.AddChild (target, AstNode.Roles.Type);
+ return Tuple.Create (CSharpParsedFile, (AstNode)target, Unit);
+ }
+ }
+
+ if (currentMember == null && currentType == null) {
+ return null;
+ }
+ baseUnit = ParseStub ("a()");
+ Print (baseUnit);
+ var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin;
+ var mref = baseUnit.GetNodeAt<MemberReferenceExpression> (location);
+ if (mref == null){
+ var invoke = baseUnit.GetNodeAt<InvocationExpression> (location);
+ if (invoke != null)
+ mref = invoke.Target as MemberReferenceExpression;
+ }
+ Expression expr = null;
+ if (mref != null) {
+ expr = mref.Target.Clone ();
+ mref.Parent.ReplaceWith (expr);
+ } else {
+ Expression tref = baseUnit.GetNodeAt<TypeReferenceExpression> (location);
+ var memberType = tref != null ? ((TypeReferenceExpression)tref).Type as MemberType : null;
+ if (memberType == null) {
+ memberType = baseUnit.GetNodeAt<MemberType> (location);
+ if (memberType != null) {
+ tref = baseUnit.GetNodeAt<Expression> (location);
+ if (tref == null)
+ return null;
+ }
+ if (tref is ObjectCreateExpression) {
+ expr = new TypeReferenceExpression (memberType.Target.Clone ());
+ expr.AddAnnotation (new ObjectCreateExpression ());
+ }
+ }
+
+ if (memberType == null)
+ return null;
+ if (expr == null)
+ expr = new TypeReferenceExpression (memberType.Target.Clone ());
+ tref.ReplaceWith (expr);
+ }
+
+ var member = Unit.GetNodeAt<AttributedNode> (memberLocation);
+ var member2 = baseUnit.GetNodeAt<AttributedNode> (memberLocation);
+ member2.Remove ();
+ member.ReplaceWith (member2);
+ var tsvisitor = new TypeSystemConvertVisitor (ProjectContent, this.CSharpParsedFile.FileName);
+ Unit.AcceptVisitor (tsvisitor, null);
+ return Tuple.Create (tsvisitor.ParsedFile, (AstNode)expr, Unit);
+ }
+
+ Tuple<CSharpParsedFile, AstNode, CompilationUnit> GetExpressionAtCursor ()
+ {
+// if (currentMember == null && currentType == null)
+// return null;
+
+ TextLocation memberLocation;
+ if (currentMember != null) {
+ memberLocation = currentMember.Region.Begin;
+ } else if (currentType != null) {
+ memberLocation = currentType.Region.Begin;
+ } else {
+ memberLocation = location;
+ }
+
+ var baseUnit = ParseStub ("");
+ var tmpUnit = baseUnit;
+ AstNode expr = baseUnit.GetNodeAt<IdentifierExpression> (location.Line, location.Column - 1);
+ if (expr == null)
+ expr = baseUnit.GetNodeAt<Attribute> (location.Line, location.Column - 1);
+ if (expr == null) {
+ baseUnit = ParseStub ("()");
+ expr = baseUnit.GetNodeAt<IdentifierExpression> (location.Line, location.Column - 1);
+ }
+
+ // try initializer expression
+ if (expr == null) {
+ baseUnit = ParseStub ("a = b};", false);
+ expr = baseUnit.GetNodeAt<ArrayInitializerExpression> (location.Line, location.Column - 1);
+ }
+
+ // try statement
+ if (expr == null) {
+ expr = tmpUnit.GetNodeAt<SwitchStatement> (location.Line, location.Column - 1);
+ baseUnit = tmpUnit;
+ }
+
+ if (expr == null) {
+ var forStmt = tmpUnit.GetNodeAt<ForStatement> (location.Line, location.Column - 3);
+ expr = forStmt;
+ if (forStmt != null && forStmt.EmbeddedStatement.IsNull) {
+ var id = new IdentifierExpression ("stub");
+ forStmt.EmbeddedStatement = new BlockStatement () { Statements = { new ExpressionStatement (id) }};
+ expr = id;
+ }
+ baseUnit = tmpUnit;
+ }
+
+ if (expr == null)
+ return null;
+ var member = Unit.GetNodeAt<AttributedNode> (memberLocation);
+ var member2 = baseUnit.GetNodeAt<AttributedNode> (memberLocation);
+ if (member != null && member2 != null) {
+ member2.Remove ();
+
+ if (member is TypeDeclaration) {
+ member.AddChild (member2, TypeDeclaration.MemberRole);
+ } else {
+ member.ReplaceWith (member2);
+ }
+ } else {
+ var tsvisitor2 = new TypeSystemConvertVisitor (ProjectContent, CSharpParsedFile.FileName);
+ Unit.AcceptVisitor (tsvisitor2, null);
+ return Tuple.Create (tsvisitor2.ParsedFile, expr, baseUnit);
+ }
+ var tsvisitor = new TypeSystemConvertVisitor (ProjectContent, CSharpParsedFile.FileName);
+ Unit.AcceptVisitor (tsvisitor, null);
+ return Tuple.Create (tsvisitor.ParsedFile, expr, Unit);
+ }
+
+ Tuple<CSharpParsedFile, AstNode, CompilationUnit> GetExpressionAt (int offset)
+ {
+ var parser = new CSharpParser ();
+ string text = this.document.GetText (0, this.offset);
+ var sb = new StringBuilder (text);
+ sb.Append ("a;");
+ AppendMissingClosingBrackets (sb, text, false);
+ var stream = new System.IO.StringReader (sb.ToString ());
+ var completionUnit = parser.Parse (stream, 0);
+ stream.Close ();
+ var loc = document.GetLocation (offset);
+ var expr = completionUnit.GetNodeAt (loc, n => n is Expression || n is VariableDeclarationStatement);
+ if (expr == null)
+ return null;
+ var tsvisitor = new TypeSystemConvertVisitor (ProjectContent, CSharpParsedFile.FileName);
+ completionUnit.AcceptVisitor (tsvisitor, null);
+
+ return Tuple.Create (tsvisitor.ParsedFile, expr, completionUnit);
+ }
+
+
+ #endregion
+
+ #region Helper methods
+ string GetPreviousToken (ref int i, bool allowLineChange)
+ {
+ char c;
+ if (i <= 0)
+ return null;
+
+ do {
+ c = document.GetCharAt (--i);
+ } while (i > 0 && char.IsWhiteSpace (c) && (allowLineChange ? true : c != '\n'));
+
+ if (i == 0)
+ return null;
+
+ if (!char.IsLetterOrDigit (c))
+ return new string (c, 1);
+
+ int endOffset = i + 1;
+
+ do {
+ c = document.GetCharAt (i - 1);
+ if (!(char.IsLetterOrDigit (c) || c == '_'))
+ break;
+
+ i--;
+ } while (i > 0);
+
+ return document.GetText (i, endOffset - i);
+ }
+
+ bool GetParameterCompletionCommandOffset (out int cpos)
+ {
+ // Start calculating the parameter offset from the beginning of the
+ // current member, instead of the beginning of the file.
+ cpos = offset - 1;
+ IMember mem = currentMember;
+ if (mem == null || (mem is IType))
+ return false;
+ int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn);
+ int parenDepth = 0;
+ int chevronDepth = 0;
+ while (cpos > startPos) {
+ char c = document.GetCharAt (cpos);
+ if (c == ')')
+ parenDepth++;
+ if (c == '>')
+ chevronDepth++;
+ if (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<') {
+ int p = GetCurrentParameterIndex (cpos + 1, startPos);
+ if (p != -1) {
+ cpos++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ if (c == '(')
+ parenDepth--;
+ if (c == '<')
+ chevronDepth--;
+ cpos--;
+ }
+ return false;
+ }
+
+ int GetCurrentParameterIndex (int offset, int memberStart)
+ {
+ int cursor = this.offset;
+ int i = offset;
+
+ if (i > cursor)
+ return -1;
+ if (i == cursor)
+ return 1; // parameters are 1 based
+ int index = memberStart + 1;
+ int parentheses = 0;
+ int bracket = 0;
+ bool insideQuote = false, insideString = false, insideSingleLineComment = false, insideMultiLineComment = false;
+ do {
+ char c = document.GetCharAt (i - 1);
+ switch (c) {
+ case '\\':
+ if (insideString || insideQuote)
+ i++;
+ break;
+ case '\'':
+ if (!insideString && !insideSingleLineComment && !insideMultiLineComment)
+ insideQuote = !insideQuote;
+ break;
+ case '"':
+ if (!insideQuote && !insideSingleLineComment && !insideMultiLineComment)
+ insideString = !insideString;
+ break;
+ case '/':
+ if (!insideQuote && !insideString && !insideMultiLineComment) {
+ if (document.GetCharAt (i) == '/')
+ insideSingleLineComment = true;
+ if (document.GetCharAt (i) == '*')
+ insideMultiLineComment = true;
+ }
+ break;
+ case '*':
+ if (insideMultiLineComment && document.GetCharAt (i) == '/')
+ insideMultiLineComment = false;
+ break;
+ case '\n':
+ case '\r':
+ insideSingleLineComment = false;
+ break;
+ case '{':
+ if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment)
+ bracket++;
+ break;
+ case '}':
+ if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment)
+ bracket--;
+ break;
+ case '(':
+ if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment)
+ parentheses++;
+ break;
+ case ')':
+ if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment)
+ parentheses--;
+ break;
+ case ',':
+ if (!insideQuote && !insideString && !insideSingleLineComment && !insideMultiLineComment && parentheses == 1 && bracket == 0)
+ index++;
+ break;
+ }
+ i++;
+ } while (i <= cursor && parentheses >= 0);
+
+ return parentheses != 1 || bracket > 0 ? -1 : index;
+ }
+
+ CSharpResolver GetState ()
+ {
+ var state = new CSharpResolver (ctx, System.Threading.CancellationToken.None);
+
+ state.CurrentMember = CSharpParsedFile.GetMember (location);
+ state.CurrentTypeDefinition = CSharpParsedFile.GetInnermostTypeDefinition (location);
+ state.CurrentUsingScope = CSharpParsedFile.GetUsingScope (location);
+ if (state.CurrentMember != null) {
+ var node = Unit.GetNodeAt (location);
+ if (node == null)
+ return state;
+ var navigator = new NodeListResolveVisitorNavigator (new[] { node });
+ var visitor = new ResolveVisitor (state, CSharpParsedFile, navigator);
+ Unit.AcceptVisitor (visitor, null);
+ try {
+ var newState = visitor.GetResolverStateBefore (node);
+ if (newState != null)
+ state = newState;
+ } catch (Exception) {
+ }
+ }
+
+ return state;
+ }
+ #endregion
+
+ #region Preprocessor
+
+ IEnumerable<ICompletionData> GetDirectiveCompletionData ()
+ {
+ yield return factory.CreateLiteralCompletionData ("if");
+ yield return factory.CreateLiteralCompletionData ("else");
+ yield return factory.CreateLiteralCompletionData ("elif");
+ yield return factory.CreateLiteralCompletionData ("endif");
+ yield return factory.CreateLiteralCompletionData ("define");
+ yield return factory.CreateLiteralCompletionData ("undef");
+ yield return factory.CreateLiteralCompletionData ("warning");
+ yield return factory.CreateLiteralCompletionData ("error");
+ yield return factory.CreateLiteralCompletionData ("pragma");
+ yield return factory.CreateLiteralCompletionData ("line");
+ yield return factory.CreateLiteralCompletionData ("line hidden");
+ yield return factory.CreateLiteralCompletionData ("line default");
+ yield return factory.CreateLiteralCompletionData ("region");
+ yield return factory.CreateLiteralCompletionData ("endregion");
+ }
+ #endregion
+
+ #region Xml Comments
+ static readonly List<string> commentTags = new List<string> (new string[] { "c", "code", "example", "exception", "include", "list", "listheader", "item", "term", "description", "para", "param", "paramref", "permission", "remarks", "returns", "see", "seealso", "summary", "value" });
+
+ IEnumerable<ICompletionData> GetXmlDocumentationCompletionData ()
+ {
+ yield return factory.CreateLiteralCompletionData ("c", "Set text in a code-like font");
+ yield return factory.CreateLiteralCompletionData ("code", "Set one or more lines of source code or program output");
+ yield return factory.CreateLiteralCompletionData ("example", "Indicate an example");
+ yield return factory.CreateLiteralCompletionData ("exception", "Identifies the exceptions a method can throw", "exception cref=\"|\"></exception>");
+ yield return factory.CreateLiteralCompletionData ("include", "Includes comments from a external file", "include file=\"|\" path=\"\">");
+ yield return factory.CreateLiteralCompletionData ("list", "Create a list or table", "list type=\"|\">");
+ yield return factory.CreateLiteralCompletionData ("listheader", "Define the heading row");
+ yield return factory.CreateLiteralCompletionData ("item", "Defines list or table item");
+
+ yield return factory.CreateLiteralCompletionData ("term", "A term to define");
+ yield return factory.CreateLiteralCompletionData ("description", "Describes a list item");
+ yield return factory.CreateLiteralCompletionData ("para", "Permit structure to be added to text");
+
+ yield return factory.CreateLiteralCompletionData ("param", "Describe a parameter for a method or constructor", "param name=\"|\">");
+ yield return factory.CreateLiteralCompletionData ("paramref", "Identify that a word is a parameter name", "paramref name=\"|\"/>");
+
+ yield return factory.CreateLiteralCompletionData ("permission", "Document the security accessibility of a member", "permission cref=\"|\"");
+ yield return factory.CreateLiteralCompletionData ("remarks", "Describe a type");
+ yield return factory.CreateLiteralCompletionData ("returns", "Describe the return value of a method");
+ yield return factory.CreateLiteralCompletionData ("see", "Specify a link", "see cref=\"|\"/>");
+ yield return factory.CreateLiteralCompletionData ("seealso", "Generate a See Also entry", "seealso cref=\"|\"/>");
+ yield return factory.CreateLiteralCompletionData ("summary", "Describe a member of a type");
+ yield return factory.CreateLiteralCompletionData ("typeparam", "Describe a type parameter for a generic type or method");
+ yield return factory.CreateLiteralCompletionData ("typeparamref", "Identify that a word is a type parameter name");
+ yield return factory.CreateLiteralCompletionData ("value", "Describe a property");
+ }
+ #endregion
+
+ #region Keywords
+ static string[] expressionLevelKeywords = new string [] { "as", "is", "else", "out", "ref", "null", "delegate", "default"};
+
+ static string[] primitiveTypesKeywords = new string [] { "void", "object", "bool", "byte", "sbyte", "char", "short", "int", "long", "ushort", "uint", "ulong", "float", "double", "decimal", "string"};
+
+ static string[] statementStartKeywords = new string [] { "base", "new", "sizeof", "this",
+ "true", "false", "typeof", "checked", "unchecked", "from", "break", "checked",
+ "unchecked", "const", "continue", "do", "finally", "fixed", "for", "foreach",
+ "goto", "if", "lock", "return", "stackalloc", "switch", "throw", "try", "unsafe",
+ "using", "while", "yield", "dynamic", "var" };
+
+ static string[] globalLevelKeywords = new string [] {
+ "namespace", "using", "extern", "public", "internal",
+ "class", "interface", "struct", "enum", "delegate",
+ "abstract", "sealed", "static", "unsafe", "partial"
+ };
+
+ static string[] accessorModifierKeywords = new string [] {
+ "public", "internal", "protected", "private"
+ };
+
+ static string[] typeLevelKeywords = new string [] {
+ "public", "internal", "protected", "private",
+ "class", "interface", "struct", "enum", "delegate",
+ "abstract", "sealed", "static", "unsafe", "partial",
+ "const", "event", "extern", "fixed","new",
+ "operator", "explicit", "implicit",
+ "override", "readonly", "virtual", "volatile"
+ };
+ static string[] linqKeywords = new string[] { "from", "where", "select", "group", "into", "orderby", "join", "let", "in", "on", "equals", "by", "ascending", "descending" };
+ #endregion
+ }
+}
+
|