You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1767 lines
72 KiB
1767 lines
72 KiB
// |
|
// 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 (), t => currentType != null ? !currentType.Equals (t) : true); |
|
return wrapper.Result; |
|
} |
|
return null; |
|
} |
|
|
|
return HandleKeywordCompletion (tokenIndex, token); |
|
// Automatic completion |
|
default: |
|
if (IsInsideComment () || IsInsideString ()) |
|
return null; |
|
if (!(char.IsLetter (completionChar) || completionChar == '_')) |
|
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; |
|
} |
|
|
|
var identifierStart = GetExpressionAtCursor (); |
|
if (identifierStart == null) |
|
return null; |
|
CSharpResolver csResolver; |
|
AstNode n = identifierStart.Item2; |
|
var contextList = new CompletionDataWrapper (this); |
|
|
|
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); |
|
csResolver = visitor.GetResolverStateBefore (n); |
|
// 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; |
|
// if (IsInLinqContext (result)) { |
|
// tokenIndex = offset; |
|
// token = GetPreviousToken (ref tokenIndex, false); // token last typed |
|
// token = GetPreviousToken (ref tokenIndex, false); // possible linq keyword ? |
|
// triggerWordLength = 1; |
|
// |
|
// if (linqKeywords.Contains (token)) { |
|
// if (token == "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; |
|
// } 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<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); |
|
} |
|
} |
|
|
|
AddTypesAndNamespaces (wrapper, state); |
|
|
|
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); |
|
} |
|
|
|
AddKeywords (wrapper, primitiveTypesKeywords); |
|
wrapper.Result.AddRange (factory.CreateCodeTemplateCompletionData ()); |
|
} |
|
|
|
void AddTypesAndNamespaces (CompletionDataWrapper wrapper, CSharpResolver state, Predicate<ITypeDefinition> typePred = null, Predicate<IMember> memberPred = null) |
|
{ |
|
var currentType = state.CurrentTypeDefinition ?? this.currentType; |
|
if (currentType != null) { |
|
for (var ct = currentType; ct != null; ct = ct.DeclaringTypeDefinition) { |
|
foreach (var nestedType in ct.NestedTypes) { |
|
if (typePred == null || typePred (nestedType)) |
|
wrapper.AddType (nestedType, nestedType.Name); |
|
} |
|
} |
|
|
|
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)) |
|
wrapper.AddType (type, 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 (), 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 (), 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, 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); |
|
// 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, Expression, CompilationUnit> GetExpressionAtCursor () |
|
{ |
|
if (currentMember == null && currentType == null) |
|
return null; |
|
|
|
var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; |
|
var baseUnit = ParseStub (""); |
|
|
|
Expression expr = baseUnit.GetNodeAt<IdentifierExpression> (location.Line, location.Column - 1); |
|
if (expr == null) { |
|
baseUnit = ParseStub ("()"); |
|
expr = baseUnit.GetNodeAt<IdentifierExpression> (location.Line, location.Column - 1); |
|
if (expr == null) |
|
return null; |
|
} |
|
var member = Unit.GetNodeAt<AttributedNode> (memberLocation); |
|
var member2 = baseUnit.GetNodeAt<AttributedNode> (memberLocation); |
|
member2.Remove (); |
|
|
|
if (member is TypeDeclaration) { |
|
member.AddChild (member2, TypeDeclaration.MemberRole); |
|
} else { |
|
member.ReplaceWith (member2); |
|
} |
|
|
|
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); |
|
state = visitor.GetResolverStateBefore (node) ?? state; |
|
} |
|
|
|
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 |
|
} |
|
} |
|
|
|
|