mirror of https://github.com/icsharpcode/ILSpy.git
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.
813 lines
22 KiB
813 lines
22 KiB
// |
|
// CSharpCompletionEngineBase.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.CSharp.Resolver; |
|
using ICSharpCode.NRefactory.Editor; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using ICSharpCode.NRefactory.Semantics; |
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
|
using ICSharpCode.NRefactory.CSharp.TypeSystem; |
|
|
|
namespace ICSharpCode.NRefactory.CSharp.Completion |
|
{ |
|
/// <summary> |
|
/// Acts as a common base between code completion and parameter completion. |
|
/// </summary> |
|
public class CSharpCompletionEngineBase |
|
{ |
|
protected IDocument document; |
|
protected int offset; |
|
protected TextLocation location; |
|
protected IUnresolvedTypeDefinition currentType; |
|
protected IUnresolvedMember currentMember; |
|
|
|
#region Input properties |
|
public CSharpTypeResolveContext ctx { get; private set; } |
|
|
|
public IProjectContent ProjectContent { get; private set; } |
|
|
|
ICompilation compilation; |
|
|
|
protected ICompilation Compilation { |
|
get { |
|
if (compilation == null) |
|
compilation = ProjectContent.Resolve (ctx).Compilation; |
|
return compilation; |
|
} |
|
} |
|
#endregion |
|
|
|
protected CSharpCompletionEngineBase(IProjectContent content, ICompletionContextProvider completionContextProvider, CSharpTypeResolveContext ctx) |
|
{ |
|
if (content == null) |
|
throw new ArgumentNullException("content"); |
|
if (ctx == null) |
|
throw new ArgumentNullException("ctx"); |
|
if (completionContextProvider == null) |
|
throw new ArgumentNullException("completionContextProvider"); |
|
|
|
this.ProjectContent = content; |
|
this.CompletionContextProvider = completionContextProvider; |
|
this.ctx = ctx; |
|
} |
|
|
|
|
|
public ICompletionContextProvider CompletionContextProvider { |
|
get; |
|
private set; |
|
} |
|
|
|
public void SetOffset (int offset) |
|
{ |
|
Reset (); |
|
|
|
this.offset = offset; |
|
this.location = document.GetLocation (offset); |
|
CompletionContextProvider.GetCurrentMembers (offset, out currentType, out currentMember); |
|
} |
|
|
|
public 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; |
|
var 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; |
|
Stack<int> indexStack = new Stack<int> (); |
|
while (cpos > startPos) { |
|
char c = document.GetCharAt (cpos); |
|
if (c == ')') { |
|
parenDepth++; |
|
} |
|
if (c == '>') { |
|
chevronDepth++; |
|
} |
|
if (c == '}') { |
|
if (indexStack.Count > 0) { |
|
parenDepth = indexStack.Pop (); |
|
} else { |
|
parenDepth = 0; |
|
} |
|
chevronDepth = 0; |
|
} |
|
if (indexStack.Count == 0 && (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<')) { |
|
int p = GetCurrentParameterIndex (startPos, cpos + 1); |
|
if (p != -1) { |
|
cpos++; |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
if (c == '(') { |
|
parenDepth--; |
|
} |
|
if (c == '<') { |
|
chevronDepth--; |
|
} |
|
if (c == '{') { |
|
indexStack.Push (parenDepth); |
|
chevronDepth = 0; |
|
} |
|
cpos--; |
|
} |
|
return false; |
|
} |
|
|
|
public int GetCurrentParameterIndex (int triggerOffset, int endOffset) |
|
{ |
|
char lastChar = document.GetCharAt (endOffset - 1); |
|
if (lastChar == '(' || lastChar == '<') { |
|
return 0; |
|
} |
|
var parameter = new Stack<int> (); |
|
var bracketStack = new Stack<Stack<int>> (); |
|
bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; |
|
for (int i = triggerOffset; i < endOffset; i++) { |
|
char ch = document.GetCharAt (i); |
|
char nextCh = i + 1 < document.TextLength ? document.GetCharAt (i + 1) : '\0'; |
|
switch (ch) { |
|
case '{': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
bracketStack.Push (parameter); |
|
parameter = new Stack<int> (); |
|
break; |
|
case '[': |
|
case '(': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
parameter.Push (0); |
|
break; |
|
case '}': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
if (bracketStack.Count > 0) { |
|
parameter = bracketStack.Pop (); |
|
} else { |
|
return -1; |
|
} |
|
break; |
|
case ']': |
|
case ')': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
if (parameter.Count > 0) { |
|
parameter.Pop (); |
|
} else { |
|
return -1; |
|
} |
|
break; |
|
case '<': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
parameter.Push (0); |
|
break; |
|
case '>': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
if (parameter.Count > 0) { |
|
parameter.Pop (); |
|
} |
|
break; |
|
case ',': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
if (parameter.Count > 0) { |
|
parameter.Push (parameter.Pop () + 1); |
|
} |
|
break; |
|
case '/': |
|
if (inString || inChar || inVerbatimString) { |
|
break; |
|
} |
|
if (nextCh == '/') { |
|
i++; |
|
inSingleComment = true; |
|
} |
|
if (nextCh == '*') { |
|
inMultiLineComment = true; |
|
} |
|
break; |
|
case '*': |
|
if (inString || inChar || inVerbatimString || inSingleComment) { |
|
break; |
|
} |
|
if (nextCh == '/') { |
|
i++; |
|
inMultiLineComment = false; |
|
} |
|
break; |
|
case '@': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { |
|
break; |
|
} |
|
if (nextCh == '"') { |
|
i++; |
|
inVerbatimString = true; |
|
} |
|
break; |
|
case '\n': |
|
case '\r': |
|
inSingleComment = false; |
|
inString = false; |
|
inChar = false; |
|
break; |
|
case '\\': |
|
if (inString || inChar) { |
|
i++; |
|
} |
|
break; |
|
case '"': |
|
if (inSingleComment || inMultiLineComment || inChar) { |
|
break; |
|
} |
|
if (inVerbatimString) { |
|
if (nextCh == '"') { |
|
i++; |
|
break; |
|
} |
|
inVerbatimString = false; |
|
break; |
|
} |
|
inString = !inString; |
|
break; |
|
case '\'': |
|
if (inSingleComment || inMultiLineComment || inString || inVerbatimString) { |
|
break; |
|
} |
|
inChar = !inChar; |
|
break; |
|
} |
|
} |
|
if (parameter.Count == 0 || bracketStack.Count > 0) { |
|
return -1; |
|
} |
|
|
|
return parameter.Pop() + 1; |
|
} |
|
|
|
#region Context helper methods |
|
public class MiniLexer |
|
{ |
|
readonly string text; |
|
|
|
public bool IsFistNonWs = true; |
|
public bool IsInSingleComment = false; |
|
public bool IsInString = false; |
|
public bool IsInVerbatimString = false; |
|
public bool IsInChar = false; |
|
public bool IsInMultiLineComment = false; |
|
public bool IsInPreprocessorDirective = false; |
|
|
|
public MiniLexer(string text) |
|
{ |
|
this.text = text; |
|
} |
|
|
|
public void Parse(Action<char> act = null) |
|
{ |
|
Parse(0, text.Length, act); |
|
} |
|
|
|
public void Parse(int start, int length, Action<char> act = null) |
|
{ |
|
for (int i = start; i < length; i++) { |
|
char ch = text [i]; |
|
char nextCh = i + 1 < text.Length ? text [i + 1] : '\0'; |
|
switch (ch) { |
|
case '#': |
|
if (IsFistNonWs) |
|
IsInPreprocessorDirective = true; |
|
break; |
|
case '/': |
|
if (IsInString || IsInChar || IsInVerbatimString) |
|
break; |
|
if (nextCh == '/') { |
|
i++; |
|
IsInSingleComment = true; |
|
} |
|
if (nextCh == '*') |
|
IsInMultiLineComment = true; |
|
break; |
|
case '*': |
|
if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment) |
|
break; |
|
if (nextCh == '/') { |
|
i++; |
|
IsInMultiLineComment = false; |
|
} |
|
break; |
|
case '@': |
|
if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment) |
|
break; |
|
if (nextCh == '"') { |
|
i++; |
|
IsInVerbatimString = true; |
|
} |
|
break; |
|
case '\n': |
|
case '\r': |
|
IsInSingleComment = false; |
|
IsInString = false; |
|
IsInChar = false; |
|
IsFistNonWs = true; |
|
IsInPreprocessorDirective = false; |
|
break; |
|
case '\\': |
|
if (IsInString || IsInChar) |
|
i++; |
|
break; |
|
case '"': |
|
if (IsInSingleComment || IsInMultiLineComment || IsInChar) |
|
break; |
|
if (IsInVerbatimString) { |
|
if (nextCh == '"') { |
|
i++; |
|
break; |
|
} |
|
IsInVerbatimString = false; |
|
break; |
|
} |
|
IsInString = !IsInString; |
|
break; |
|
case '\'': |
|
if (IsInSingleComment || IsInMultiLineComment || IsInString || IsInVerbatimString) |
|
break; |
|
IsInChar = !IsInChar; |
|
break; |
|
} |
|
if (act != null) |
|
act(ch); |
|
IsFistNonWs &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; |
|
} |
|
} |
|
} |
|
|
|
|
|
protected bool IsInsideCommentStringOrDirective(int offset) |
|
{ |
|
var lexer = new MiniLexer(document.Text); |
|
lexer.Parse(0, offset); |
|
return |
|
lexer.IsInSingleComment || |
|
lexer.IsInString || |
|
lexer.IsInVerbatimString || |
|
lexer.IsInChar || |
|
lexer.IsInMultiLineComment || |
|
lexer.IsInPreprocessorDirective; |
|
} |
|
|
|
|
|
protected bool IsInsideCommentStringOrDirective() |
|
{ |
|
var text = GetMemberTextToCaret(); |
|
var lexer = new MiniLexer(text.Item1); |
|
lexer.Parse(); |
|
return |
|
lexer.IsInSingleComment || |
|
lexer.IsInString || |
|
lexer.IsInVerbatimString || |
|
lexer.IsInChar || |
|
lexer.IsInMultiLineComment || |
|
lexer.IsInPreprocessorDirective; |
|
} |
|
|
|
protected bool IsInsideDocComment () |
|
{ |
|
var text = GetMemberTextToCaret (); |
|
bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; |
|
bool singleLineIsDoc = false; |
|
|
|
for (int i = 0; i < text.Item1.Length - 1; i++) { |
|
char ch = text.Item1 [i]; |
|
char nextCh = text.Item1 [i + 1]; |
|
|
|
switch (ch) { |
|
case '/': |
|
if (inString || inChar || inVerbatimString) |
|
break; |
|
if (nextCh == '/') { |
|
i++; |
|
inSingleComment = true; |
|
singleLineIsDoc = i + 1 < text.Item1.Length && text.Item1 [i + 1] == '/'; |
|
if (singleLineIsDoc) { |
|
i++; |
|
} |
|
} |
|
if (nextCh == '*') |
|
inMultiLineComment = true; |
|
break; |
|
case '*': |
|
if (inString || inChar || inVerbatimString || inSingleComment) |
|
break; |
|
if (nextCh == '/') { |
|
i++; |
|
inMultiLineComment = false; |
|
} |
|
break; |
|
case '@': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) |
|
break; |
|
if (nextCh == '"') { |
|
i++; |
|
inVerbatimString = true; |
|
} |
|
break; |
|
case '\n': |
|
case '\r': |
|
inSingleComment = false; |
|
inString = false; |
|
inChar = false; |
|
break; |
|
case '\\': |
|
if (inString || inChar) |
|
i++; |
|
break; |
|
case '"': |
|
if (inSingleComment || inMultiLineComment || inChar) |
|
break; |
|
if (inVerbatimString) { |
|
if (nextCh == '"') { |
|
i++; |
|
break; |
|
} |
|
inVerbatimString = false; |
|
break; |
|
} |
|
inString = !inString; |
|
break; |
|
case '\'': |
|
if (inSingleComment || inMultiLineComment || inString || inVerbatimString) |
|
break; |
|
inChar = !inChar; |
|
break; |
|
} |
|
} |
|
|
|
return inSingleComment && singleLineIsDoc; |
|
} |
|
|
|
protected CSharpResolver GetState () |
|
{ |
|
return new CSharpResolver (ctx); |
|
/*var state = new CSharpResolver (ctx); |
|
|
|
state.CurrentMember = currentMember; |
|
state.CurrentTypeDefinition = currentType; |
|
state.CurrentUsingScope = CSharpUnresolvedFile.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, CSharpUnresolvedFile, navigator); |
|
Unit.AcceptVisitor (visitor, null); |
|
try { |
|
var newState = visitor.GetResolverStateBefore (node); |
|
if (newState != null) |
|
state = newState; |
|
} catch (Exception) { |
|
} |
|
} |
|
|
|
return state;*/ |
|
} |
|
#endregion |
|
|
|
#region Basic parsing/resolving functions |
|
static Stack<Tuple<char, int>> GetBracketStack (string memberText) |
|
{ |
|
var bracketStack = new Stack<Tuple<char, int>> (); |
|
|
|
bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; |
|
|
|
for (int i = 0; i < memberText.Length; i++) { |
|
char ch = memberText [i]; |
|
char nextCh = i + 1 < memberText.Length ? memberText [i + 1] : '\0'; |
|
switch (ch) { |
|
case '(': |
|
case '[': |
|
case '{': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) |
|
break; |
|
bracketStack.Push (Tuple.Create (ch, i)); |
|
break; |
|
case ')': |
|
case ']': |
|
case '}': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) |
|
break; |
|
if (bracketStack.Count > 0) |
|
bracketStack.Pop (); |
|
break; |
|
case '/': |
|
if (inString || inChar || inVerbatimString) |
|
break; |
|
if (nextCh == '/') { |
|
i++; |
|
inSingleComment = true; |
|
} |
|
if (nextCh == '*') |
|
inMultiLineComment = true; |
|
break; |
|
case '*': |
|
if (inString || inChar || inVerbatimString || inSingleComment) |
|
break; |
|
if (nextCh == '/') { |
|
i++; |
|
inMultiLineComment = false; |
|
} |
|
break; |
|
case '@': |
|
if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) |
|
break; |
|
if (nextCh == '"') { |
|
i++; |
|
inVerbatimString = true; |
|
} |
|
break; |
|
case '\n': |
|
case '\r': |
|
inSingleComment = false; |
|
inString = false; |
|
inChar = false; |
|
break; |
|
case '\\': |
|
if (inString || inChar) |
|
i++; |
|
break; |
|
case '"': |
|
if (inSingleComment || inMultiLineComment || inChar) |
|
break; |
|
if (inVerbatimString) { |
|
if (nextCh == '"') { |
|
i++; |
|
break; |
|
} |
|
inVerbatimString = false; |
|
break; |
|
} |
|
inString = !inString; |
|
break; |
|
case '\'': |
|
if (inSingleComment || inMultiLineComment || inString || inVerbatimString) |
|
break; |
|
inChar = !inChar; |
|
break; |
|
default : |
|
break; |
|
} |
|
} |
|
return bracketStack; |
|
} |
|
|
|
public static void AppendMissingClosingBrackets (StringBuilder wrapper, string memberText, bool appendSemicolon) |
|
{ |
|
var bracketStack = GetBracketStack (memberText); |
|
bool didAppendSemicolon = !appendSemicolon; |
|
//char lastBracket = '\0'; |
|
while (bracketStack.Count > 0) { |
|
var t = bracketStack.Pop (); |
|
switch (t.Item1) { |
|
case '(': |
|
wrapper.Append (')'); |
|
if (appendSemicolon) |
|
didAppendSemicolon = false; |
|
//lastBracket = ')'; |
|
break; |
|
case '[': |
|
wrapper.Append (']'); |
|
if (appendSemicolon) |
|
didAppendSemicolon = false; |
|
//lastBracket = ']'; |
|
break; |
|
case '<': |
|
wrapper.Append ('>'); |
|
if (appendSemicolon) |
|
didAppendSemicolon = false; |
|
//lastBracket = '>'; |
|
break; |
|
case '{': |
|
int o = t.Item2 - 1; |
|
if (!didAppendSemicolon) { |
|
didAppendSemicolon = true; |
|
wrapper.Append (';'); |
|
} |
|
|
|
bool didAppendCatch = false; |
|
while (o >= "try".Length) { |
|
char ch = memberText [o]; |
|
if (!char.IsWhiteSpace (ch)) { |
|
if (ch == 'y' && memberText [o - 1] == 'r' && memberText [o - 2] == 't') { |
|
wrapper.Append ("} catch {}"); |
|
didAppendCatch = true; |
|
} |
|
break; |
|
} |
|
o--; |
|
} |
|
if (!didAppendCatch) |
|
wrapper.Append ('}'); |
|
break; |
|
} |
|
} |
|
if (!didAppendSemicolon) |
|
wrapper.Append (';'); |
|
} |
|
|
|
protected SyntaxTree ParseStub(string continuation, bool appendSemicolon = true, string afterContinuation = null) |
|
{ |
|
var mt = GetMemberTextToCaret(); |
|
if (mt == null) { |
|
return null; |
|
} |
|
|
|
string memberText = mt.Item1; |
|
var memberLocation = mt.Item2; |
|
int closingBrackets = 1; |
|
int generatedLines = 0; |
|
var wrapper = new StringBuilder(); |
|
bool wrapInClass = memberLocation != new TextLocation(1, 1); |
|
if (wrapInClass) { |
|
wrapper.Append("class Stub {"); |
|
wrapper.AppendLine(); |
|
closingBrackets++; |
|
generatedLines++; |
|
} |
|
wrapper.Append(memberText); |
|
wrapper.Append(continuation); |
|
AppendMissingClosingBrackets(wrapper, memberText, appendSemicolon); |
|
wrapper.Append(afterContinuation); |
|
if (closingBrackets > 0) { |
|
wrapper.Append(new string('}', closingBrackets)); |
|
} |
|
var parser = new CSharpParser (); |
|
foreach (var sym in CompletionContextProvider.ConditionalSymbols) |
|
parser.CompilerSettings.ConditionalSymbols.Add (sym); |
|
parser.InitialLocation = new TextLocation(memberLocation.Line - generatedLines, 1); |
|
var result = parser.Parse(wrapper.ToString ()); |
|
return result; |
|
} |
|
|
|
// string cachedText = null; |
|
|
|
protected virtual void Reset () |
|
{ |
|
// cachedText = null; |
|
} |
|
|
|
protected Tuple<string, TextLocation> GetMemberTextToCaret() |
|
{ |
|
return CompletionContextProvider.GetMemberTextToCaret(offset, currentType, currentMember); |
|
} |
|
|
|
protected ExpressionResult GetInvocationBeforeCursor(bool afterBracket) |
|
{ |
|
SyntaxTree baseUnit; |
|
baseUnit = ParseStub("a", false); |
|
|
|
var section = baseUnit.GetNodeAt<AttributeSection>(location.Line, location.Column - 2); |
|
var attr = section != null ? section.Attributes.LastOrDefault() : null; |
|
if (attr != null) { |
|
return new ExpressionResult((AstNode)attr, baseUnit); |
|
} |
|
|
|
//var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; |
|
var mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); |
|
AstNode expr = null; |
|
if (mref is InvocationExpression) { |
|
expr = ((InvocationExpression)mref).Target; |
|
} else if (mref is ObjectCreateExpression) { |
|
expr = mref; |
|
} else { |
|
baseUnit = ParseStub(")};", false); |
|
mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); |
|
if (mref is InvocationExpression) { |
|
expr = ((InvocationExpression)mref).Target; |
|
} else if (mref is ObjectCreateExpression) { |
|
expr = mref; |
|
} |
|
} |
|
|
|
if (expr == null) { |
|
// work around for missing ';' bug in mcs: |
|
baseUnit = ParseStub("a", true); |
|
|
|
section = baseUnit.GetNodeAt<AttributeSection>(location.Line, location.Column - 2); |
|
attr = section != null ? section.Attributes.LastOrDefault() : null; |
|
if (attr != null) { |
|
return new ExpressionResult((AstNode)attr, baseUnit); |
|
} |
|
|
|
//var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; |
|
mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); |
|
expr = null; |
|
if (mref is InvocationExpression) { |
|
expr = ((InvocationExpression)mref).Target; |
|
} else if (mref is ObjectCreateExpression) { |
|
expr = mref; |
|
} |
|
} |
|
|
|
if (expr == null) { |
|
return null; |
|
} |
|
return new ExpressionResult ((AstNode)expr, baseUnit); |
|
} |
|
|
|
public class ExpressionResult |
|
{ |
|
public AstNode Node { get; private set; } |
|
public SyntaxTree Unit { get; private set; } |
|
|
|
|
|
public ExpressionResult (AstNode item2, SyntaxTree item3) |
|
{ |
|
this.Node = item2; |
|
this.Unit = item3; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return string.Format ("[ExpressionResult: Node={0}, Unit={1}]", Node, Unit); |
|
} |
|
} |
|
|
|
protected Tuple<ResolveResult, CSharpResolver> ResolveExpression (ExpressionResult tuple) |
|
{ |
|
return ResolveExpression (tuple.Node); |
|
} |
|
|
|
protected Tuple<ResolveResult, CSharpResolver> ResolveExpression(AstNode expr) |
|
{ |
|
if (expr == null) { |
|
return null; |
|
} |
|
AstNode resolveNode; |
|
if (expr is Expression || expr is AstType) { |
|
resolveNode = expr; |
|
} else if (expr is VariableDeclarationStatement) { |
|
resolveNode = ((VariableDeclarationStatement)expr).Type; |
|
} else { |
|
resolveNode = expr; |
|
} |
|
try { |
|
var root = expr.AncestorsAndSelf.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree); |
|
if (root == null) { |
|
return null; |
|
} |
|
if (root is Accessor) |
|
root = root.Parent; |
|
var csResolver = CompletionContextProvider.GetResolver (GetState(), root); |
|
var result = csResolver.Resolve(resolveNode); |
|
var state = csResolver.GetResolverStateBefore(resolveNode); |
|
return Tuple.Create(result, state); |
|
} catch (Exception e) { |
|
Console.WriteLine(e); |
|
return null; |
|
} |
|
} |
|
|
|
#endregion |
|
} |
|
} |