// // CSharpCompletionEngine.cs // // Author: // Mike Krüger // // 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; using ICSharpCode.NRefactory.CSharp.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; public bool CloseOnSquareBrackets; #endregion public CSharpCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, ICompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base (content, completionContextProvider, ctx) { if (document == null) { throw new ArgumentNullException("document"); } if (factory == null) { throw new ArgumentNullException("factory"); } this.document = document; this.factory = factory; // Set defaults for additional input properties this.FormattingPolicy = FormattingOptionsFactory.CreateMono(); this.EolMarker = Environment.NewLine; this.IndentString = "\t"; } public bool TryGetCompletionWord(int offset, out int startPos, out int wordLength) { startPos = wordLength = 0; int pos = offset - 1; while (pos >= 0) { char c = document.GetCharAt(pos); if (!char.IsLetterOrDigit(c) && c != '_') break; pos--; } if (pos == -1) return false; pos++; startPos = pos; while (pos < document.TextLength) { char c = document.GetCharAt(pos); if (!char.IsLetterOrDigit(c) && c != '_') break; pos++; } wordLength = pos - startPos; return true; } public IEnumerable 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(); 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) { var text = new HashSet(result.Select(r => r.CompletionText)); result = result.Concat(nonWsResult.Where(r => !text.Contains(r.CompletionText))); } } } return result; } return Enumerable.Empty(); } IEnumerable GenerateNameProposals(AstType type) { if (type is PrimitiveType) { var pt = (PrimitiveType)type; switch (pt.Keyword) { case "object": yield return "o"; yield return "obj"; break; case "bool": yield return "b"; yield return "pred"; break; case "double": case "float": case "decimal": yield return "d"; yield return "f"; yield return "m"; break; default: yield return "i"; yield return "j"; yield return "k"; break; } yield break; } string name; if (type is SimpleType) { name = ((SimpleType)type).Identifier; } else if (type is MemberType) { name = ((SimpleType)type).Identifier; } else { yield break; } var names = WordParser.BreakWords(name); 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]); } yield return possibleName.ToString(); } } IEnumerable HandleMemberReferenceCompletion(ExpressionResult expr) { if (expr == null) return null; // do not complete . (but ..) if (expr.Node is PrimitiveExpression) { var pexpr = (PrimitiveExpression)expr.Node; if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains('.')) { return null; } } var resolveResult = ResolveExpression(expr); if (resolveResult == null) { return null; } if (expr.Node is AstType) { // need to look at paren.parent because of "catch (.A" expression if (expr.Node.Parent != null && expr.Node.Parent.Parent is CatchClause) return HandleCatchClauseType(expr); return CreateTypeAndNamespaceCompletionData( location, resolveResult.Item1, expr.Node, resolveResult.Item2 ); } return CreateCompletionData( location, resolveResult.Item1, expr.Node, resolveResult.Item2 ); } bool IsInPreprocessorDirective() { var text = GetMemberTextToCaret().Item1; var miniLexer = new MiniLexer(text); miniLexer.Parse(); return miniLexer.IsInPreprocessorDirective; } IEnumerable HandleObjectInitializer(CompilationUnit unit, AstNode n) { var p = n.Parent; while (p != null && !(p is ObjectCreateExpression)) { p = p.Parent; } var parent = (ArrayInitializerExpression)n.Parent; if (parent.IsSingleElement) parent = (ArrayInitializerExpression)parent.Parent; if (p != null) { var contextList = new CompletionDataWrapper(this); var initializerResult = ResolveExpression(p); if (initializerResult != null && initializerResult.Item1.Type.Kind != TypeKind.Unknown) { // check 3 cases: // 1) New initalizer { xpr // 2) Object initializer { prop = val1, field = val2, xpr // 3) Array initializer { new Foo (), a, xpr // in case 1 all object/array initializer options should be given - in the others not. AstNode prev = null; if (parent.Elements.Count > 1) { prev = parent.Elements.First(); if (prev is ArrayInitializerExpression && ((ArrayInitializerExpression)prev).IsSingleElement) prev = ((ArrayInitializerExpression)prev).Elements.FirstOrDefault(); } if (prev != null && !(prev is NamedExpression)) { AddContextCompletion(contextList, GetState(), n); // case 3) return contextList.Result; } foreach (var m in initializerResult.Item1.Type.GetMembers (m => m.IsPublic && (m.EntityType == EntityType.Property || m.EntityType == EntityType.Field))) { contextList.AddMember(m); } if (prev != null && (prev is NamedExpression)) { // case 2) return contextList.Result; } // case 1) // check if the object is a list, if not only provide object initalizers var list = typeof(System.Collections.IList).ToTypeReference().Resolve(Compilation); if (initializerResult.Item1.Type.Kind != TypeKind.Array && list != null) { var def = initializerResult.Item1.Type.GetDefinition(); if (def != null && !def.IsDerivedFrom(list.GetDefinition())) return contextList.Result; } AddContextCompletion(contextList, GetState(), n); return contextList.Result; } } return null; } IEnumerable MagicKeyCompletion(char completionChar, bool controlSpace) { Tuple resolveResult; switch (completionChar) { // Magic key completion case ':': case '.': if (IsInsideCommentStringOrDirective()) { return Enumerable.Empty(); } return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); case '#': if (!IsInPreprocessorDirective()) return null; return GetDirectiveCompletionData(); // XML doc completion case '<': if (IsInsideDocComment()) { return GetXmlDocumentationCompletionData(); } if (controlSpace) { return DefaultControlSpaceItems(); } return null; case '>': if (!IsInsideDocComment()) { if (offset > 2 && document.GetCharAt(offset - 2) == '-' && !IsInsideCommentStringOrDirective()) { return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); } 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, "", AnchorMovementType.BeforeInsertion); } } return null; // Parameter completion case '(': if (IsInsideCommentStringOrDirective()) { return null; } var invoke = GetInvocationBeforeCursor(true); if (invoke == null) { if (controlSpace) return DefaultControlSpaceItems(invoke); return null; } if (invoke.Node is TypeOfExpression) { return CreateTypeList(); } var invocationResult = ResolveExpression(invoke); if (invocationResult == null) { return null; } var methodGroup = invocationResult.Item1 as MethodGroupResolveResult; if (methodGroup != null) { return CreateParameterCompletion( methodGroup, invocationResult.Item2, invoke.Node, invoke.Unit, 0, controlSpace ); } if (controlSpace) { return DefaultControlSpaceItems(invoke); } return null; case '=': return controlSpace ? DefaultControlSpaceItems() : 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 ' ': int tokenIndex = offset; string token = GetPreviousToken(ref tokenIndex, false); if (IsInsideCommentStringOrDirective()) { if (IsInPreprocessorDirective()) return HandleKeywordCompletion(tokenIndex, token); return null; } // check propose name, for context (but only in control space context) //IType isAsType = null; var isAsExpression = GetExpressionAt(offset); if (controlSpace && isAsExpression != null && isAsExpression.Node is VariableDeclarationStatement && token != "new") { var parent = isAsExpression.Node as VariableDeclarationStatement; var proposeNameList = new CompletionDataWrapper(this); if (parent.Variables.Count != 1) return DefaultControlSpaceItems(isAsExpression, controlSpace); foreach (var possibleName in GenerateNameProposals (parent.Type)) { 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 - 1, this.offset) - 1; if (currentParameter < 0) { return null; } invoke = GetInvocationBeforeCursor(token == "("); if (invoke == null) { return null; } invocationResult = ResolveExpression(invoke); if (invocationResult == null) { return null; } methodGroup = invocationResult.Item1 as MethodGroupResolveResult; if (methodGroup != null) { return CreateParameterCompletion( methodGroup, invocationResult.Item2, invoke.Node, invoke.Unit, currentParameter, controlSpace); } return null; case "=": case "==": GetPreviousToken(ref tokenIndex, false); var expressionOrVariableDeclaration = GetExpressionAt(tokenIndex); if (expressionOrVariableDeclaration == null) { return null; } resolveResult = ResolveExpression(expressionOrVariableDeclaration); if (resolveResult == null) { return null; } if (resolveResult.Item1.Type.Kind == TypeKind.Enum) { var wrapper = new CompletionDataWrapper(this); AddContextCompletion( wrapper, resolveResult.Item2, expressionOrVariableDeclaration.Node); 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); 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; if (delegateType.Kind != TypeKind.Delegate) { return null; } var wrapper = new CompletionDataWrapper(this); if (currentType != null) { // bool includeProtected = DomType.IncludeProtected (dom, typeFromDatabase, resolver.CallingType); foreach (var method in currentType.Methods) { 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) { token = GetPreviousToken(ref tokenIndex, false); token = GetPreviousToken(ref tokenIndex, false); if (token == "enum") return HandleEnumContext(); var wrapper = new CompletionDataWrapper(this); AddTypesAndNamespaces( wrapper, GetState(), null, t => currentType != null && !currentType.ReflectionName.Equals(t.ReflectionName) ? t : null ); return wrapper.Result; } return null; } var keywordCompletion = HandleKeywordCompletion(tokenIndex, token); if (keywordCompletion == null && controlSpace) { goto default; } return keywordCompletion; // Automatic completion default: if (IsInsideCommentStringOrDirective()) { return null; } if (IsInLinqContext(offset)) { if (!controlSpace && !(char.IsLetter(completionChar) || completionChar == '_')) { return null; } tokenIndex = offset; token = GetPreviousToken(ref tokenIndex, false); // token last typed if (!char.IsWhiteSpace(completionChar) && !linqKeywords.Contains(token)) { 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 (currentType != null && currentType.Kind == TypeKind.Enum) { return HandleEnumContext(); } var contextList = new CompletionDataWrapper(this); var identifierStart = GetExpressionAtCursor(); if (identifierStart != null) { if (identifierStart.Node is TypeParameterDeclaration) { return null; } if (identifierStart.Node is MemberReferenceExpression) { return HandleMemberReferenceCompletion( new ExpressionResult( ((MemberReferenceExpression)identifierStart.Node).Target, identifierStart.Unit ) ); } if (identifierStart.Node is Identifier) { // May happen in variable names return controlSpace ? DefaultControlSpaceItems(identifierStart) : null; } if (identifierStart.Node is VariableInitializer && location <= ((VariableInitializer)identifierStart.Node).NameToken.EndLocation) { return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(identifierStart) : null; } if (identifierStart.Node is CatchClause) { if (((CatchClause)identifierStart.Node).VariableNameToken.Contains(location)) { return null; } return HandleCatchClauseType(identifierStart); } } if (!(char.IsLetter(completionChar) || completionChar == '_') && (!controlSpace || identifierStart == null || !(identifierStart.Node.Parent is ArrayInitializerExpression))) { return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(identifierStart) : null; } char prevCh = offset > 2 ? document.GetCharAt(offset - 2) : ';'; 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 (token == "class" || token == "interface" || token == "struct" || token == "enum" || token == "namespace") { // after these always follows a name return null; } var keywordresult = HandleKeywordCompletion(tokenIndex, token); if (keywordresult != null) { return keywordresult; } int prevTokenIndex = tokenIndex; var prevToken2 = GetPreviousToken(ref prevTokenIndex, false); if (prevToken2 == "delegate") { // after these always follows a name return null; } if (identifierStart == null && !string.IsNullOrEmpty(token) && !IsInsideCommentStringOrDirective() && (prevToken2 == ";" || prevToken2 == "{" || prevToken2 == "}")) { char last = token [token.Length - 1]; if (char.IsLetterOrDigit(last) || last == '_' || token == ">") { return HandleKeywordCompletion(tokenIndex, token); } } if (identifierStart == null) { var accCtx = HandleAccessorContext(); if (accCtx != null) { return accCtx; } return DefaultControlSpaceItems(null, controlSpace); } CSharpResolver csResolver; AstNode n = identifierStart.Node; if (n != null && n.Parent is AnonymousTypeCreateExpression) { AutoSelect = false; } // Handle foreach (type name _ if (n is IdentifierExpression) { var prev = n.GetPrevNode() as ForeachStatement; if (prev != null && prev.InExpression.IsNull) { if (controlSpace) { contextList.AddCustom("in"); return contextList.Result; } return null; } // var astResolver = new CSharpAstResolver( // GetState(), // identifierStart.Unit, // CSharpParsedFile // ); // // foreach (var type in CreateFieldAction.GetValidTypes(astResolver, (Expression)n)) { // if (type.Kind == TypeKind.Delegate) { // AddDelegateHandlers(contextList, type, false, false); // AutoSelect = false; // AutoCompleteEmptyMatch = false; // } // } } // Handle object/enumerable initialzer expressions: "new O () { P$" if (n is IdentifierExpression && n.Parent is ArrayInitializerExpression) { var result = HandleObjectInitializer(identifierStart.Unit, n); if (result != null) return result; } if (n != null && n.Parent is InvocationExpression) { var invokeParent = (InvocationExpression)n.Parent; var invokeResult = ResolveExpression( invokeParent.Target ); var mgr = invokeResult != null ? invokeResult.Item1 as MethodGroupResolveResult : null; if (mgr != null) { int idx = 0; foreach (var arg in invokeParent.Arguments) { if (arg == n) { break; } idx++; } foreach (var method in mgr.Methods) { if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { AutoSelect = false; AutoCompleteEmptyMatch = false; } foreach (var p in method.Parameters) { contextList.AddNamedParameterVariable(p); } } idx++; foreach (var list in mgr.GetEligibleExtensionMethods (true)) { foreach (var method in list) { if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { AutoSelect = false; AutoCompleteEmptyMatch = false; } } } } } if (n != null && n.Parent is ObjectCreateExpression) { var invokeResult = ResolveExpression(n.Parent); var mgr = invokeResult != null ? invokeResult.Item1 as ResolveResult : null; if (mgr != null) { foreach (var constructor in mgr.Type.GetConstructors ()) { foreach (var p in constructor.Parameters) { contextList.AddVariable(p); } } } } if (n is IdentifierExpression) { var bop = n.Parent as BinaryOperatorExpression; Expression evaluationExpr = null; if (bop != null && bop.Right == n && (bop.Operator == BinaryOperatorType.Equality || bop.Operator == BinaryOperatorType.InEquality)) { evaluationExpr = bop.Left; } // check for compare to enum case if (evaluationExpr != null) { resolveResult = ResolveExpression(evaluationExpr); if (resolveResult != null && resolveResult.Item1.Type.Kind == TypeKind.Enum) { var wrapper = new CompletionDataWrapper(this); AddContextCompletion( wrapper, resolveResult.Item2, evaluationExpr ); AddEnumMembers(wrapper, resolveResult.Item1.Type, resolveResult.Item2); AutoCompleteEmptyMatch = false; return wrapper.Result; } } } if (n is Identifier && n.Parent is ForeachStatement) { if (controlSpace) { return DefaultControlSpaceItems(); } return null; } if (n is ArrayInitializerExpression) { // check for new [] {...} expression -> no need to resolve the type there var parent = n.Parent as ArrayCreateExpression; if (parent != null && parent.Type.IsNull) { return DefaultControlSpaceItems(); } var initalizerResult = ResolveExpression(n.Parent); var concreteNode = identifierStart.Unit.GetNodeAt(location); // check if we're on the right side of an initializer expression if (concreteNode != null && concreteNode.Parent != null && concreteNode.Parent.Parent != null && concreteNode.Identifier != "a" && concreteNode.Parent.Parent is NamedExpression) { return DefaultControlSpaceItems(); } if (initalizerResult != null && initalizerResult.Item1.Type.Kind != TypeKind.Unknown) { foreach (var property in initalizerResult.Item1.Type.GetProperties ()) { if (!property.IsPublic) { continue; } contextList.AddMember(property); } foreach (var field in initalizerResult.Item1.Type.GetFields ()) { if (!field.IsPublic) { continue; } contextList.AddMember(field); } return contextList.Result; } return DefaultControlSpaceItems(); } if (IsAttributeContext(n)) { // add attribute targets if (currentType == null) { contextList.AddCustom("assembly"); contextList.AddCustom("module"); contextList.AddCustom("type"); } else { contextList.AddCustom("param"); contextList.AddCustom("field"); contextList.AddCustom("property"); contextList.AddCustom("method"); contextList.AddCustom("event"); } contextList.AddCustom("return"); } if (n is MemberType) { resolveResult = ResolveExpression( ((MemberType)n).Target ); return CreateTypeAndNamespaceCompletionData( location, resolveResult.Item1, ((MemberType)n).Target, resolveResult.Item2 ); } if (n != null/* && !(identifierStart.Item2 is TypeDeclaration)*/) { csResolver = new CSharpResolver(ctx); var nodes = new List(); nodes.Add(n); if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { nodes.Add(n.Parent); } var astResolver = CompletionContextProvider.GetResolver(csResolver, identifierStart.Unit); astResolver.ApplyNavigator(new NodeListResolveVisitorNavigator(nodes)); try { csResolver = astResolver.GetResolverStateBefore(n); } catch (Exception) { csResolver = GetState(); } // add attribute properties. if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { var resolved = astResolver.Resolve(n.Parent); if (resolved != null && resolved.Type != null) { foreach (var property in resolved.Type.GetProperties (p => p.Accessibility == Accessibility.Public)) { contextList.AddMember(property); } foreach (var field in resolved.Type.GetFields (p => p.Accessibility == Accessibility.Public)) { contextList.AddMember(field); } } } } else { csResolver = GetState(); } // identifier has already started with the first letter offset--; AddContextCompletion( contextList, csResolver, identifierStart.Node ); 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; } IEnumerable HandleCatchClauseType(ExpressionResult identifierStart) { Func typePred = delegate (IType type) { if (type.GetAllBaseTypes().Any(t => t.ReflectionName == "System.Exception")) return type; return null; }; if (identifierStart.Node is CatchClause) { var wrapper = new CompletionDataWrapper(this); AddTypesAndNamespaces( wrapper, GetState(), identifierStart.Node, typePred, m => false ); return wrapper.Result; } var resolveResult = ResolveExpression(identifierStart); return CreateCompletionData( location, resolveResult.Item1, identifierStart.Node, resolveResult.Item2, typePred ); } string[] validEnumBaseTypes = { "byte", "sbyte", "short", "int", "long", "ushort", "uint", "ulong" }; IEnumerable HandleEnumContext() { var cu = ParseStub("a", false); if (cu == null) { return null; } var curType = cu.GetNodeAt(location); if (curType == null || curType.ClassType != ClassType.Enum) { cu = ParseStub("a {}", false); var node = cu.GetNodeAt(location); if (node != null) { var wrapper = new CompletionDataWrapper(this); AddKeywords(wrapper, validEnumBaseTypes); return wrapper.Result; } } var member = cu.GetNodeAt(location); if (member != null && member.NameToken.EndLocation < location) { return DefaultControlSpaceItems(); } return null; } bool IsInLinqContext(int offset) { string token; while (null != (token = GetPreviousToken (ref offset, true)) && !IsInsideCommentStringOrDirective ()) { if (token == "from") { return !IsInsideCommentStringOrDirective(offset); } if (token == ";" || token == "{") { return false; } } return false; } IEnumerable HandleAccessorContext() { var unit = ParseStub("get; }", false); var node = unit.GetNodeAt(location, cn => !(cn is CSharpTokenNode)); if (node is Accessor) { node = node.Parent; } var contextList = new CompletionDataWrapper(this); 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 { return null; } return contextList.Result; } IEnumerable DefaultControlSpaceItems(ExpressionResult xp = null, bool controlSpace = true) { var wrapper = new CompletionDataWrapper(this); if (offset >= document.TextLength) { offset = document.TextLength - 1; } while (offset > 1 && char.IsWhiteSpace (document.GetCharAt (offset))) { offset--; } location = document.GetLocation(offset); if (xp == null) { xp = GetExpressionAtCursor(); } AstNode node; CompilationUnit unit; Tuple rr; if (xp != null) { node = xp.Node; rr = ResolveExpression(node); unit = xp.Unit; } else { unit = ParseStub("foo", false); node = unit.GetNodeAt( location.Line, location.Column + 2, n => n is Expression || n is AstType ); rr = ResolveExpression(node); } if (node is Identifier && node.Parent is ForeachStatement) { var foreachStmt = (ForeachStatement)node.Parent; foreach (var possibleName in GenerateNameProposals (foreachStmt.VariableType)) { if (possibleName.Length > 0) { wrapper.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); } } AutoSelect = false; AutoCompleteEmptyMatch = false; return wrapper.Result; } if (node is Identifier && node.Parent is ParameterDeclaration) { if (!controlSpace) { return null; } // Try Parameter name case var param = node.Parent as ParameterDeclaration; if (param != null) { foreach (var possibleName in GenerateNameProposals (param.Type)) { if (possibleName.Length > 0) { wrapper.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); } } AutoSelect = false; AutoCompleteEmptyMatch = false; return wrapper.Result; } } /* if (Unit != null && (node == null || node is TypeDeclaration)) { var constructor = Unit.GetNodeAt( location.Line, location.Column - 3 ); if (constructor != null && !constructor.ColonToken.IsNull && constructor.Initializer.IsNull) { wrapper.AddCustom("this"); wrapper.AddCustom("base"); return wrapper.Result; } }*/ var initializer = node != null ? node.Parent as ArrayInitializerExpression : null; if (initializer != null) { var result = HandleObjectInitializer(unit, initializer); if (result != null) return result; } CSharpResolver csResolver = null; if (rr != null) { csResolver = rr.Item2; } if (csResolver == null) { if (node != null) { csResolver = GetState(); //var astResolver = new CSharpAstResolver (csResolver, node, xp != null ? xp.Item1 : CSharpParsedFile); try { //csResolver = astResolver.GetResolverStateBefore (node); Console.WriteLine(csResolver.LocalVariables.Count()); } catch (Exception e) { Console.WriteLine("E!!!" + e); } } else { csResolver = GetState(); } } AddContextCompletion(wrapper, csResolver, node); return wrapper.Result; } void AddContextCompletion(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node) { if (state != null && !(node is AstType)) { foreach (var variable in state.LocalVariables) { if (variable.Region.IsInside(location.Line, location.Column - 1)) { continue; } wrapper.AddVariable(variable); } } if (currentMember is IUnresolvedParameterizedMember && !(node is AstType)) { var param = (IParameterizedMember)currentMember.CreateResolved(ctx); foreach (var p in param.Parameters) { wrapper.AddVariable(p); } } if (currentMember is IUnresolvedMethod) { var method = (IUnresolvedMethod)currentMember; foreach (var p in method.TypeParameters) { wrapper.AddTypeParameter(p); } } Func typePred = null; if (IsAttributeContext(node)) { var attribute = Compilation.FindType(KnownTypeCode.Attribute); typePred = t => { return t.GetAllBaseTypeDefinitions().Any(bt => bt.Equals(attribute)) ? t : null; }; } AddTypesAndNamespaces(wrapper, state, node, typePred); wrapper.Result.Add(factory.CreateLiteralCompletionData("global")); if (!(node is AstType)) { if (currentMember != null || node is Expression) { AddKeywords(wrapper, statementStartKeywords); AddKeywords(wrapper, expressionLevelKeywords); if (node == null || node is TypeDeclaration) AddKeywords(wrapper, typeLevelKeywords); } else if (currentType != null) { AddKeywords(wrapper, typeLevelKeywords); } else { AddKeywords(wrapper, globalLevelKeywords); } var prop = currentMember as IUnresolvedProperty; if (prop != null && prop.Setter != null && prop.Setter.Region.IsInside(location)) { wrapper.AddCustom("value"); } if (currentMember is IUnresolvedEvent) { wrapper.AddCustom("value"); } if (IsInSwitchContext(node)) { wrapper.AddCustom("case"); } } else { if (((AstType)node).Parent is ParameterDeclaration) { AddKeywords(wrapper, parameterTypePredecessorKeywords); } } AddKeywords(wrapper, primitiveTypesKeywords); if (currentMember != null && (node is IdentifierExpression || node is SimpleType) && (node.Parent is ExpressionStatement || node.Parent is ForeachStatement || node.Parent is UsingStatement)) { wrapper.AddCustom("var"); wrapper.AddCustom("dynamic"); } wrapper.Result.AddRange(factory.CreateCodeTemplateCompletionData()); if (node != null && node.Role == Roles.Argument) { var resolved = ResolveExpression(node.Parent); var invokeResult = resolved != null ? resolved.Item1 as CSharpInvocationResolveResult : null; if (invokeResult != null) { int argNum = 0; foreach (var arg in node.Parent.Children.Where (c => c.Role == Roles.Argument)) { if (arg == node) { break; } argNum++; } var param = argNum < invokeResult.Member.Parameters.Count ? invokeResult.Member.Parameters [argNum] : null; if (param != null && param.Type.Kind == TypeKind.Enum) { AddEnumMembers(wrapper, param.Type, state); } } } if (node is Expression) { var root = node; while (root.Parent != null) root = root.Parent; var astResolver = CompletionContextProvider.GetResolver(state, root); foreach (var type in CreateFieldAction.GetValidTypes(astResolver, (Expression)node)) { if (type.Kind == TypeKind.Enum) { AddEnumMembers(wrapper, type, state); } else if (type.Kind == TypeKind.Delegate) { AddDelegateHandlers(wrapper, type, true, true); AutoSelect = false; AutoCompleteEmptyMatch = false; } } } // Add 'this' keyword for first parameter (extension method case) if (node != null && node.Parent is ParameterDeclaration && node.Parent.PrevSibling != null && node.Parent.PrevSibling.Role == Roles.LPar) { wrapper.AddCustom("this"); } } static bool IsInSwitchContext(AstNode node) { var n = node; while (n != null && !(n is EntityDeclaration)) { 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, Func typePred = null, Predicate memberPred = null, Action callback = null) { var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); if (currentType != null) { for (var ct = currentType; ct != null; ct = ct.DeclaringTypeDefinition) { foreach (var nestedType in ct.NestedTypes) { string name = nestedType.Name; if (IsAttributeContext(node) && name.EndsWith("Attribute") && name.Length > "Attribute".Length) { name = name.Substring(0, name.Length - "Attribute".Length); } if (typePred == null) { wrapper.AddType(nestedType, name); continue; } var type = typePred(nestedType.Resolve(ctx)); if (type != null) { var a2 = wrapper.AddType(type, name); if (a2 != null && callback != null) { callback(a2, type); } } continue; } } if (this.currentMember != null && !(node is AstType)) { var def = ctx.CurrentTypeDefinition ?? Compilation.MainAssembly.GetTypeDefinition(currentType); if (def != null) { bool isProtectedAllowed = true; foreach (var member in def.GetMembers ()) { if (member is IMethod && ((IMethod)member).FullName == "System.Object.Finalize") { continue; } if (member.EntityType == EntityType.Operator) { continue; } if (member.IsExplicitInterfaceImplementation) { continue; } if (!lookup.IsAccessible(member, isProtectedAllowed)) { continue; } if (memberPred == null || memberPred(member)) { wrapper.AddMember(member); } } var declaring = def.DeclaringTypeDefinition; while (declaring != null) { foreach (var member in declaring.GetMembers (m => m.IsStatic)) { if (memberPred == null || memberPred(member)) { wrapper.AddMember(member); } } declaring = declaring.DeclaringTypeDefinition; } } } foreach (var p in currentType.TypeParameters) { wrapper.AddTypeParameter(p); } } var scope = ctx.CurrentUsingScope; for (var n = scope; n != null; n = n.Parent) { foreach (var pair in n.UsingAliases) { wrapper.AddNamespace(pair.Key); } foreach (var u in n.Usings) { foreach (var type in u.Types) { if (!lookup.IsAccessible(type, false)) continue; IType addType = typePred != null ? typePred(type) : type; if (addType != null) { string name = type.Name; if (IsAttributeContext(node) && name.EndsWith("Attribute") && name.Length > "Attribute".Length) { name = name.Substring(0, name.Length - "Attribute".Length); } var a = wrapper.AddType(addType, name); if (a != null && callback != null) { callback(a, type); } } } } foreach (var type in n.Namespace.Types) { if (!lookup.IsAccessible(type, false)) continue; IType addType = typePred != null ? typePred(type) : type; if (addType != null) { var a2 = wrapper.AddType(addType, addType.Name); if (a2 != null && callback != null) { callback(a2, type); } } } foreach (var curNs in n.Namespace.ChildNamespaces) { wrapper.AddNamespace(curNs.Name); } } } IEnumerable HandleKeywordCompletion(int wordStart, string word) { if (IsInsideCommentStringOrDirective()) { if (IsInPreprocessorDirective()) { if (word == "if" || word == "elif") { if (wordStart > 0 && document.GetCharAt(wordStart - 1) == '#') { return factory.CreatePreProcessorDefinesCompletionData(); } } } return null; } switch (word) { case "namespace": return null; case "using": if (currentType != null) { return null; } var wrapper = new CompletionDataWrapper(this); AddTypesAndNamespaces(wrapper, GetState(), null, t => null); 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 namespaceList = GetUsedNamespaces (); // var col = new CSharpTextEditorCompletion.CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, null, location); // bool isInterface = false; // HashSet baseTypeNames = new HashSet (); // if (cls != null) { // baseTypeNames.Add (cls.Name); // if (cls.ClassType == ClassType.Struct) // isInterface = true; // } // int tokenIndex = offset; // // // Search base types " : [Type1, ... ,TypeN,] " // 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 innerStack = new Stack (); // 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.Node.Parent; if (parent is VariableInitializer) { parent = parent.Parent; } if (parent is VariableDeclarationStatement) { var resolved = ResolveExpression(parent); if (resolved != null) { isAsType = resolved.Item1.Type; } } } var isAsWrapper = new CompletionDataWrapper(this); var def = isAsType != null ? isAsType.GetDefinition() : null; AddTypesAndNamespaces( isAsWrapper, GetState(), null, t => t.GetDefinition() == null || def == null || t.GetDefinition().IsDerivedFrom(def) ? t : null, m => false); 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 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; } if (currentType != null && (currentType.Kind == TypeKind.Class || currentType.Kind == TypeKind.Struct)) { string modifiers = document.GetText(firstMod, wordStart - firstMod); return GetOverrideCompletionData(currentType, 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; } var state = GetState(); if (state.CurrentTypeDefinition != null && (state.CurrentTypeDefinition.Kind == TypeKind.Class || state.CurrentTypeDefinition.Kind == TypeKind.Struct)) { string modifiers = document.GetText(firstMod, wordStart - firstMod); return GetPartialCompletionData(state.CurrentTypeDefinition, modifiers); } return null; case "public": case "protected": case "private": case "internal": case "sealed": case "static": var accessorContext = HandleAccessorContext(); if (accessorContext != null) { return accessorContext; } wrapper = new CompletionDataWrapper(this); state = GetState(); if (currentType != null) { AddTypesAndNamespaces(wrapper, state, null, null, m => false); AddKeywords(wrapper, primitiveTypesKeywords); } AddKeywords(wrapper, typeLevelKeywords); return wrapper.Result; case "new": int j = offset - 4; // string token = GetPreviousToken (ref j, true); IType hintType = null; var expressionOrVariableDeclaration = GetNewExpressionAt(j); if (expressionOrVariableDeclaration == null) return null; var astResolver = CompletionContextProvider.GetResolver(GetState(), expressionOrVariableDeclaration.Unit); hintType = CreateFieldAction.GetValidTypes( astResolver, expressionOrVariableDeclaration.Node as Expression ) .FirstOrDefault(); return CreateTypeCompletionData(hintType); 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 expr = GetExpressionAtCursor(); var rr = ResolveExpression(expr); AddContextCompletion( inList, rr != null ? rr.Item2 : GetState(), expr.Node ); return inList.Result; } 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 = line.Offset; j < line.EndOffset; j++) { char ch = document.GetCharAt(j); if (!char.IsWhiteSpace(ch)) { return document.GetText(line.Offset, j - line.Offset - 1); } } return ""; } static CSharpAmbience amb = new CSharpAmbience(); class Category : CompletionCategory { public Category(string displayText, string icon) : base (displayText, icon) { } public override int CompareTo(CompletionCategory other) { return 0; } } IEnumerable CreateTypeCompletionData(IType hintType) { var wrapper = new CompletionDataWrapper(this); var state = GetState(); Func pred = null; Action typeCallback = null; var inferredTypesCategory = new Category("Inferred Types", null); var derivedTypesCategory = new Category("Derived Types", null); if (hintType != null) { if (hintType.Kind != TypeKind.Unknown) { var lookup = new MemberLookup( ctx.CurrentTypeDefinition, Compilation.MainAssembly ); typeCallback = (data, t) => { //check if type is in inheritance tree. if (hintType.GetDefinition() != null && t.GetDefinition() != null && t.GetDefinition().IsDerivedFrom(hintType.GetDefinition())) { data.CompletionCategory = derivedTypesCategory; } }; pred = t => { if (t.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array) { return null; } // check for valid constructors if (t.GetConstructors().Count() > 0) { bool isProtectedAllowed = currentType != null ? currentType.Resolve(ctx).GetDefinition().IsDerivedFrom(t.GetDefinition()) : false; if (!t.GetConstructors().Any(m => lookup.IsAccessible( m, isProtectedAllowed ) )) { return null; } } var typeInference = new TypeInference(Compilation); typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults; var inferedType = typeInference.FindTypeInBounds( new [] { t }, new [] { hintType } ); if (inferedType != SpecialType.UnknownType) { var newType = wrapper.AddType(inferedType, amb.ConvertType(inferedType)); if (newType != null) { newType.CompletionCategory = inferredTypesCategory; } return null; } return t; }; if (!(hintType.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array)) { DefaultCompletionString = GetShortType(hintType, GetState()); var hint = wrapper.AddType(hintType, DefaultCompletionString); if (hint != null) { hint.CompletionCategory = derivedTypesCategory; } } if (hintType is ParameterizedType && hintType.TypeParameterCount == 1 && hintType.FullName == "System.Collections.Generic.IEnumerable") { var arg = ((ParameterizedType)hintType).TypeArguments.FirstOrDefault(); var array = new ArrayTypeReference(arg.ToTypeReference(), 1).Resolve(ctx); wrapper.AddType(array, amb.ConvertType(array)); } } else { var hint = wrapper.AddType(hintType, DefaultCompletionString); if (hint != null) { DefaultCompletionString = hint.DisplayText; hint.CompletionCategory = derivedTypesCategory; } } } AddTypesAndNamespaces(wrapper, state, null, pred, m => false, typeCallback); if (hintType == null || hintType == SpecialType.UnknownType) { AddKeywords(wrapper, primitiveTypesKeywords.Where(k => k != "void")); } CloseOnSquareBrackets = true; AutoCompleteEmptyMatch = true; return wrapper.Result; } IEnumerable GetOverrideCompletionData(IUnresolvedTypeDefinition type, string modifiers) { var wrapper = new CompletionDataWrapper(this); var alreadyInserted = new List(); //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 } } AddVirtuals( alreadyInserted, wrapper, modifiers, type.Resolve(ctx), declarationBegin ); return wrapper.Result; } IEnumerable GetPartialCompletionData(ITypeDefinition type, string modifiers) { var wrapper = new CompletionDataWrapper(this); 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 } } var methods = new List(); foreach (var part in type.Parts) { foreach (var method in part.Methods) { if (method.BodyRegion.IsEmpty) { if (GetImplementation(type, method) != null) { continue; } methods.Add(method); } } } foreach (var method in methods) { wrapper.Add(factory.CreateNewPartialCompletionData( declarationBegin, method.DeclaringTypeDefinition, method ) ); } return wrapper.Result; } IMethod GetImplementation(ITypeDefinition type, IUnresolvedMethod method) { foreach (var cur in type.Methods) { if (cur.Name == method.Name && cur.Parameters.Count == method.Parameters.Count && !cur.BodyRegion.IsEmpty) { bool equal = true; /*for (int i = 0; i < cur.Parameters.Count; i++) { if (!cur.Parameters [i].Type.Equals (method.Parameters [i].Type)) { equal = false; break; } }*/ if (equal) { return cur; } } } return null; } void AddVirtuals(List alreadyInserted, CompletionDataWrapper col, string modifiers, IType curType, int declarationBegin) { if (curType == null) { return; } foreach (var m in curType.GetMembers ().Reverse ()) { if (curType.Kind != TypeKind.Interface && !m.IsOverridable) { 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, currentType, m ); // check if the member is already implemented bool foundMember = curType.GetMembers().Any(cm => SignatureComparer.Ordinal.Equals( cm, m ) && cm.DeclaringTypeDefinition == curType.GetDefinition() ); if (foundMember) { continue; } if (alreadyInserted.Any(cm => SignatureComparer.Ordinal.Equals(cm, m))) continue; alreadyInserted.Add(m); data.CompletionCategory = col.GetCompletionCategory(m.DeclaringTypeDefinition); col.Add(data); } } static void AddKeywords(CompletionDataWrapper wrapper, IEnumerable keywords) { foreach (string keyword in keywords) { if (wrapper.Result.Any(data => data.DisplayText == keyword)) continue; 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(); 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, IUnresolvedMethod 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.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) { var oldDelegate = completionList.Result.FirstOrDefault(cd => cd.DisplayText == "delegate"); if (oldDelegate != null) completionList.Result.Remove(oldDelegate); 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; 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 = true; // 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 ().GetProjectFile (type2.Region.FileName) != null; result = false; } else if (type2.ProjectContent != null) { //TODO!! // result = type2.ProjectContent.Annotation ().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; } static bool IsAttributeContext(AstNode node) { AstNode n = node; while (n is AstType) { n = n.Parent; } return n is Attribute; } IEnumerable CreateTypeAndNamespaceCompletionData(TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state) { if (resolveResult == null || resolveResult.IsError) { return null; } var exprParent = resolvedNode.GetParent(); var unit = exprParent != null ? exprParent.GetParent() : null; var astResolver = unit != null ? CompletionContextProvider.GetResolver(state, unit) : null; IType hintType = exprParent != null && astResolver != null ? CreateFieldAction.GetValidTypes(astResolver, exprParent) .FirstOrDefault() : null; var result = new CompletionDataWrapper(this); if (resolveResult is NamespaceResolveResult) { var nr = (NamespaceResolveResult)resolveResult; if (!(resolvedNode.Parent is UsingDeclaration || resolvedNode.Parent != null && resolvedNode.Parent.Parent is UsingDeclaration)) { foreach (var cl in nr.Namespace.Types) { string name = cl.Name; if (hintType != null && hintType.Kind != TypeKind.Array && cl.Kind == TypeKind.Interface) { continue; } if (IsAttributeContext(resolvedNode) && name.EndsWith("Attribute") && name.Length > "Attribute".Length) { name = name.Substring(0, name.Length - "Attribute".Length); } result.AddType(cl, name); } } foreach (var ns in nr.Namespace.ChildNamespaces) { result.AddNamespace(ns.Name); } } else if (resolveResult is TypeResolveResult) { var type = resolveResult.Type; foreach (var nested in type.GetNestedTypes ()) { if (hintType != null && hintType.Kind != TypeKind.Array && nested.Kind == TypeKind.Interface) { continue; } result.AddType(nested, nested.Name); } } return result.Result; } IEnumerable CreateTypeList() { foreach (var cl in Compilation.RootNamespace.Types) { yield return factory.CreateTypeCompletionData(cl, cl.Name); } foreach (var ns in Compilation.RootNamespace.ChildNamespaces) { yield return factory.CreateNamespaceCompletionData(ns.Name); } } IEnumerable CreateParameterCompletion(MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, CompilationUnit unit, int parameter, bool controlSpace) { var result = new CompletionDataWrapper(this); var addedEnums = new HashSet(); var addedDelegates = new HashSet(); foreach (var method in resolveResult.Methods) { if (method.Parameters.Count <= parameter) { continue; } var resolvedType = method.Parameters [parameter].Type; 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.ReflectionName)) continue; string parameterDefinition = AddDelegateHandlers(result, resolvedType); string varName = "Handle" + method.Parameters [parameter].Type.Name + method.Parameters [parameter].Name; result.Result.Add( factory.CreateEventCreationCompletionData( varName, resolvedType, null, parameterDefinition, currentMember, currentType) ); } } foreach (var method in resolveResult.Methods) { if (parameter < method.Parameters.Count && method.Parameters [parameter].Type.Kind == TypeKind.Delegate) { AutoSelect = false; AutoCompleteEmptyMatch = false; } foreach (var p in method.Parameters) { result.AddNamedParameterVariable(p); } } if (!controlSpace) { if (addedEnums.Count + addedDelegates.Count == 0) { return Enumerable.Empty(); } 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 dt = state.CurrentTypeDefinition; var declaring = type.DeclaringType != null ? type.DeclaringType.GetDefinition() : null; if (declaring != null) { while (dt != null) { if (dt.Equals(declaring)) { builder.AlwaysUseShortTypeNames = true; break; } dt = dt.DeclaringTypeDefinition; } } var shortType = builder.ConvertType(type); return shortType.GetText(FormattingPolicy); } void AddEnumMembers(CompletionDataWrapper completionList, IType resolvedType, CSharpResolver state) { if (resolvedType.Kind != TypeKind.Enum) { return; } string typeString = GetShortType(resolvedType, state); completionList.AddEnumMembers(resolvedType, state, typeString); DefaultCompletionString = typeString; } IEnumerable CreateCompletionData(TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state, Func typePred = null) { if (resolveResult == null /*|| resolveResult.IsError*/) { return null; } if (resolveResult is NamespaceResolveResult) { var nr = (NamespaceResolveResult)resolveResult; var namespaceContents = new CompletionDataWrapper(this); foreach (var cl in nr.Namespace.Types) { IType addType = typePred != null ? typePred(cl) : cl; if (addType != null) namespaceContents.AddType(addType, addType.Name); } foreach (var ns in nr.Namespace.ChildNamespaces) { namespaceContents.AddNamespace(ns.Name); } return namespaceContents.Result; } IType type = resolveResult.Type; if (resolvedNode.Parent is PointerReferenceExpression && (type is PointerType)) { type = ((PointerType)type).ElementType; } //var typeDef = resolveResult.Type.GetDefinition(); var result = new CompletionDataWrapper(this); bool includeStaticMembers = false; var lookup = new MemberLookup( ctx.CurrentTypeDefinition, Compilation.MainAssembly ); if (resolveResult is LocalResolveResult) { if (resolvedNode is IdentifierExpression) { var mrr = (LocalResolveResult)resolveResult; includeStaticMembers = mrr.Variable.Name == mrr.Type.Name; } } if (resolveResult is TypeResolveResult && type.Kind == TypeKind.Enum) { foreach (var field in type.GetFields ()) { if (!lookup.IsAccessible(field, false)) continue; result.AddMember(field); } return result.Result; } bool isProtectedAllowed = resolveResult is ThisResolveResult ? true : lookup.IsProtectedAccessAllowed(type); bool skipNonStaticMembers = (resolveResult is TypeResolveResult); if (resolveResult is MemberResolveResult && resolvedNode is IdentifierExpression) { var mrr = (MemberResolveResult)resolveResult; includeStaticMembers = mrr.Member.Name == mrr.Type.Name; TypeResolveResult trr; if (state.IsVariableReferenceWithSameType( resolveResult, ((IdentifierExpression)resolvedNode).Identifier, out trr )) { if (currentMember != null && mrr.Member.IsStatic ^ currentMember.IsStatic) { skipNonStaticMembers = true; if (trr.Type.Kind == TypeKind.Enum) { foreach (var field in trr.Type.GetFields ()) { result.AddMember(field); } return result.Result; } } } // ADD Aliases var scope = ctx.CurrentUsingScope; for (var n = scope; n != null; n = n.Parent) { foreach (var pair in n.UsingAliases) { if (pair.Key == mrr.Member.Name) { foreach (var r in CreateCompletionData (location, pair.Value, resolvedNode, state)) { if (r is IEntityCompletionData && ((IEntityCompletionData)r).Entity is IMember) { result.AddMember((IMember)((IEntityCompletionData)r).Entity); } else { result.Add(r); } } } } } } if (resolveResult is TypeResolveResult && (resolvedNode is IdentifierExpression || resolvedNode is MemberReferenceExpression)) { includeStaticMembers = true; } // Console.WriteLine ("type:" + type +"/"+type.GetType ()); // Console.WriteLine ("current:" + ctx.CurrentTypeDefinition); // Console.WriteLine ("IS PROT ALLOWED:" + isProtectedAllowed + " static: "+ includeStaticMembers); // Console.WriteLine (resolveResult); // Console.WriteLine ("node:" + resolvedNode); // Console.WriteLine (currentMember != null ? currentMember.IsStatic : "currentMember == null"); if (resolvedNode.Annotation() == null) { //tags the created expression as part of an object create expression. var filteredList = new List(); foreach (var member in type.GetMembers ()) { if (member.EntityType == EntityType.Indexer || member.EntityType == EntityType.Operator || member.EntityType == EntityType.Constructor || member.EntityType == EntityType.Destructor) { continue; } if (member.IsExplicitInterfaceImplementation) { continue; } // Console.WriteLine ("member:" + member + member.IsShadowing); if (!lookup.IsAccessible(member, isProtectedAllowed)) { // Console.WriteLine ("skip access: " + member.FullName); continue; } if (resolvedNode is BaseReferenceExpression && member.IsAbstract) { continue; } bool memberIsStatic = member.IsStatic; if (!includeStaticMembers && memberIsStatic && !(resolveResult is TypeResolveResult)) { // Console.WriteLine ("skip static member: " + member.FullName); continue; } var field = member as IField; if (field != null) { memberIsStatic |= field.IsConst; } if (!memberIsStatic && skipNonStaticMembers) { continue; } if (member is IMethod && ((IMethod)member).FullName == "System.Object.Finalize") { continue; } if (member.EntityType == EntityType.Operator) { continue; } if (member.IsExplicitInterfaceImplementation) { continue; } if (member.IsShadowing) { filteredList.RemoveAll(m => m.Name == member.Name); } filteredList.Add(member); } foreach (var member in filteredList) { // Console.WriteLine ("add:" + member + "/" + member.IsStatic); result.AddMember(member); } } if (resolveResult is TypeResolveResult || includeStaticMembers) { foreach (var nested in type.GetNestedTypes ()) { if (!lookup.IsAccessible(nested.GetDefinition(), isProtectedAllowed)) continue; IType addType = typePred != null ? typePred(nested) : nested; if (addType != null) result.AddType(addType, addType.Name); } } else { foreach (var meths in state.GetExtensionMethods (type)) { foreach (var m in meths) { result.AddMember(m); } } } // IEnumerable 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; } IEnumerable CreateCaseCompletionData(TextLocation location) { var unit = ParseStub("a: break;"); if (unit == null) { return null; } var s = unit.GetNodeAt(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); 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 ExpressionResult GetExpressionBeforeCursor() { CompilationUnit baseUnit; if (currentMember == null) { baseUnit = ParseStub("a", false); var type = baseUnit.GetNodeAt(location); if (type == null) { baseUnit = ParseStub("a;", false); type = baseUnit.GetNodeAt(location); } if (type == null) { baseUnit = ParseStub("A a;", false); type = baseUnit.GetNodeAt(location); } if (type != null) { return new ExpressionResult((AstNode)type.Target, baseUnit); } } baseUnit = ParseStub("a", false); var curNode = baseUnit.GetNodeAt(location); // hack for local variable declaration missing ';' issue - remove that if it works. if (curNode is EntityDeclaration || baseUnit.GetNodeAt(location) == null && baseUnit.GetNodeAt(location) == null) { baseUnit = ParseStub("a"); curNode = baseUnit.GetNodeAt(location); } // Hack for handle object initializer continuation expressions if (curNode is EntityDeclaration || baseUnit.GetNodeAt(location) == null && baseUnit.GetNodeAt(location) == null) { baseUnit = ParseStub("a};"); } var mref = baseUnit.GetNodeAt(location); if (currentMember == null && currentType == null) { if (mref != null) { return new ExpressionResult((AstNode)mref.Target, baseUnit); } return null; } //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; if (mref == null) { var type = baseUnit.GetNodeAt(location); if (type != null) { return new ExpressionResult((AstNode)type.Target, baseUnit); } var pref = baseUnit.GetNodeAt(location); if (pref != null) { return new ExpressionResult((AstNode)pref.Target, baseUnit); } } AstNode expr = null; if (mref != null) { expr = mref.Target; } else { Expression tref = baseUnit.GetNodeAt(location); MemberType memberType = tref != null ? ((TypeReferenceExpression)tref).Type as MemberType : null; if (memberType == null) { memberType = baseUnit.GetNodeAt(location); if (memberType != null) { if (memberType.Parent is ObjectCreateExpression) { var mt = memberType.Target.Clone(); memberType.ReplaceWith(mt); expr = mt; goto exit; } else { tref = baseUnit.GetNodeAt(location); if (tref == null) { tref = new TypeReferenceExpression(memberType.Clone()); memberType.Parent.AddChild(tref, Roles.Expression); } 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); } exit: return new ExpressionResult((AstNode)expr, baseUnit); } ExpressionResult GetExpressionAtCursor() { // TextLocation memberLocation; // if (currentMember != null) { // memberLocation = currentMember.Region.Begin; // } else if (currentType != null) { // memberLocation = currentType.Region.Begin; // } else { // memberLocation = location; // } var baseUnit = ParseStub("a"); var tmpUnit = baseUnit; AstNode expr = baseUnit.GetNodeAt( location, n => n is IdentifierExpression || n is MemberReferenceExpression ); if (expr == null) { expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); } if (expr == null) expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); // try insertStatement if (expr == null && baseUnit.GetNodeAt( location.Line, location.Column ) != null) { tmpUnit = baseUnit = ParseStub("a();", false); expr = baseUnit.GetNodeAt( location.Line, location.Column + 1 ); } if (expr == null) { baseUnit = ParseStub("()"); expr = baseUnit.GetNodeAt( location.Line, location.Column - 1 ); if (expr == null) { expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); } } if (expr == null) { baseUnit = ParseStub("a", false); expr = baseUnit.GetNodeAt( location, n => n is IdentifierExpression || n is MemberReferenceExpression || n is CatchClause ); } // try statement if (expr == null) { expr = tmpUnit.GetNodeAt( location.Line, location.Column - 1 ); baseUnit = tmpUnit; } if (expr == null) { var block = tmpUnit.GetNodeAt(location); var node = block != null ? block.Statements.LastOrDefault() : null; var forStmt = node != null ? node.PrevSibling as ForStatement : null; if (forStmt != null && forStmt.EmbeddedStatement.IsNull) { expr = forStmt; var id = new IdentifierExpression("stub"); forStmt.EmbeddedStatement = new BlockStatement() { Statements = { new ExpressionStatement (id) }}; expr = id; baseUnit = tmpUnit; } } if (expr == null) { var forStmt = tmpUnit.GetNodeAt( location.Line, location.Column - 3 ); if (forStmt != null && forStmt.EmbeddedStatement.IsNull) { forStmt.VariableNameToken = Identifier.Create("stub"); expr = forStmt.VariableNameToken; baseUnit = tmpUnit; } } if (expr == null) { expr = tmpUnit.GetNodeAt( location.Line, location.Column - 1 ); baseUnit = tmpUnit; } // try parameter declaration type if (expr == null) { baseUnit = ParseStub(">", false, "{}"); expr = baseUnit.GetNodeAt( location.Line, location.Column - 1 ); } // try parameter declaration method if (expr == null) { baseUnit = ParseStub("> ()", false, "{}"); expr = baseUnit.GetNodeAt( location.Line, location.Column - 1 ); } // try expression in anonymous type "new { sample = x$" case if (expr == null) { baseUnit = ParseStub("a", false); expr = baseUnit.GetNodeAt( location.Line, location.Column ); if (expr != null) { expr = baseUnit.GetNodeAt(location.Line, location.Column) ?? expr; } if (expr == null) { expr = baseUnit.GetNodeAt(location.Line, location.Column); } } if (expr == null) { return null; } return new ExpressionResult(expr, baseUnit); } ExpressionResult 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, "a.cs", 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; } return new ExpressionResult(expr, completionUnit); } ExpressionResult GetNewExpressionAt(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, "a.cs", 0); stream.Close(); var loc = document.GetLocation(offset); var expr = completionUnit.GetNodeAt(loc, n => n is Expression); if (expr == null) { // try without ";" sb = new StringBuilder(text); sb.Append("a ()"); AppendMissingClosingBrackets(sb, text, false); stream = new System.IO.StringReader(sb.ToString()); completionUnit = parser.Parse(stream, "a.cs", 0); stream.Close(); loc = document.GetLocation(offset); expr = completionUnit.GetNodeAt(loc, n => n is Expression); if (expr == null) { return null; } } return new ExpressionResult(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); } #endregion #region Preprocessor IEnumerable 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 commentTags = new List(new string[] { "c", "code", "example", "exception", "include", "list", "listheader", "item", "term", "description", "para", "param", "paramref", "permission", "remarks", "returns", "see", "seealso", "summary", "value" } ); string GetLastClosingXmlCommentTag() { var line = document.GetLineByNumber(location.Line); restart: string lineText = document.GetText(line); if (!lineText.Trim().StartsWith("///")) return null; int startIndex = Math.Min(location.Column - 1, lineText.Length - 1) - 1; while (startIndex > 0 && lineText [startIndex] != '<') { --startIndex; if (lineText [startIndex] == '/') { // already closed. startIndex = -1; break; } } if (startIndex < 0 && line.PreviousLine != null) { line = line.PreviousLine; goto restart; } if (startIndex >= 0) { int endIndex = startIndex; while (endIndex + 1 < lineText.Length && lineText [endIndex] != '>' && !Char.IsWhiteSpace (lineText [endIndex + 1])) { endIndex++; } string tag = endIndex - startIndex - 1 > 0 ? lineText.Substring( startIndex + 1, endIndex - startIndex - 1 ) : null; if (!string.IsNullOrEmpty(tag) && commentTags.IndexOf(tag) >= 0) { return tag; } } return null; } IEnumerable GetXmlDocumentationCompletionData() { var closingTag = GetLastClosingXmlCommentTag(); if (closingTag != null) { yield return factory.CreateLiteralCompletionData( "/" + closingTag + ">" ); } 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=\"|\">" ); 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", "catch" }; 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" }; static string[] parameterTypePredecessorKeywords = new string[] { "out", "ref", "params" }; #endregion } }