|
|
|
|
@ -85,113 +85,20 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
@@ -85,113 +85,20 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
|
|
|
|
|
this.CSharpParsedFile = parsedFile; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IUnresolvedTypeDefinition FindInnerType (IUnresolvedTypeDefinition parent, TextLocation location) |
|
|
|
|
{ |
|
|
|
|
if (parent == null) |
|
|
|
|
return null; |
|
|
|
|
var currentType = parent; |
|
|
|
|
foreach (var type in parent.NestedTypes) { |
|
|
|
|
if (type.Region.Begin < location && location < type.Region.End) |
|
|
|
|
currentType = FindInnerType (type, location); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return currentType; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsInsideType (IUnresolvedEntity currentType, TextLocation location) |
|
|
|
|
{ |
|
|
|
|
int startOffset = document.GetOffset (currentType.Region.Begin); |
|
|
|
|
int endOffset = document.GetOffset (location); |
|
|
|
|
bool foundEndBracket = false; |
|
|
|
|
|
|
|
|
|
var bracketStack = new Stack<char> (); |
|
|
|
|
|
|
|
|
|
bool isInString = false, isInChar = false; |
|
|
|
|
bool isInLineComment = false, isInBlockComment = false; |
|
|
|
|
|
|
|
|
|
for (int i = startOffset; i < endOffset; i++) { |
|
|
|
|
char ch = document.GetCharAt (i); |
|
|
|
|
switch (ch) { |
|
|
|
|
case '(': |
|
|
|
|
case '[': |
|
|
|
|
case '{': |
|
|
|
|
if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) |
|
|
|
|
bracketStack.Push (ch); |
|
|
|
|
break; |
|
|
|
|
case ')': |
|
|
|
|
case ']': |
|
|
|
|
case '}': |
|
|
|
|
if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) |
|
|
|
|
if (bracketStack.Count > 0) |
|
|
|
|
bracketStack.Pop (); |
|
|
|
|
break; |
|
|
|
|
case '\r': |
|
|
|
|
case '\n': |
|
|
|
|
isInLineComment = false; |
|
|
|
|
break; |
|
|
|
|
case '/': |
|
|
|
|
if (isInBlockComment) { |
|
|
|
|
if (i > 0 && document.GetCharAt (i - 1) == '*') |
|
|
|
|
isInBlockComment = false; |
|
|
|
|
} else if (!isInString && !isInChar && i + 1 < document.TextLength) { |
|
|
|
|
char nextChar = document.GetCharAt (i + 1); |
|
|
|
|
if (nextChar == '/') |
|
|
|
|
isInLineComment = true; |
|
|
|
|
if (!isInLineComment && nextChar == '*') |
|
|
|
|
isInBlockComment = true; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case '"': |
|
|
|
|
if (!(isInChar || isInLineComment || isInBlockComment)) |
|
|
|
|
isInString = !isInString; |
|
|
|
|
break; |
|
|
|
|
case '\'': |
|
|
|
|
if (!(isInString || isInLineComment || isInBlockComment)) |
|
|
|
|
isInChar = !isInChar; |
|
|
|
|
break; |
|
|
|
|
default : |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return bracketStack.Any (t => t == '{'); |
|
|
|
|
public IMemberProvider MemberProvider { |
|
|
|
|
get; |
|
|
|
|
set; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected void SetOffset (int offset) |
|
|
|
|
{ |
|
|
|
|
Reset (); |
|
|
|
|
|
|
|
|
|
this.offset = offset; |
|
|
|
|
this.location = document.GetLocation (offset); |
|
|
|
|
|
|
|
|
|
this.currentType = null; |
|
|
|
|
|
|
|
|
|
foreach (var type in CSharpParsedFile.TopLevelTypeDefinitions) { |
|
|
|
|
if (type.Region.Begin < location) |
|
|
|
|
currentType = type; |
|
|
|
|
} |
|
|
|
|
currentType = FindInnerType (currentType, location); |
|
|
|
|
|
|
|
|
|
// location is beyond last reported end region, now we need to check, if the end region changed
|
|
|
|
|
if (currentType != null && currentType.Region.End < location) { |
|
|
|
|
if (!IsInsideType (currentType, location)) |
|
|
|
|
currentType = null; |
|
|
|
|
} |
|
|
|
|
this.currentMember = null; |
|
|
|
|
if (this.currentType != null) { |
|
|
|
|
foreach (var member in currentType.Members) { |
|
|
|
|
if (member.Region.Begin < location && (currentMember == null || currentMember.Region.Begin < member.Region.Begin)) |
|
|
|
|
currentMember = member; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// location is beyond last reported end region, now we need to check, if the end region changed
|
|
|
|
|
// NOTE: Enums are a special case, there the "last" field needs to be treated as current member
|
|
|
|
|
if (currentMember != null && currentMember.Region.End < location && currentType.Kind != TypeKind.Enum) { |
|
|
|
|
if (!IsInsideType (currentMember, location)) |
|
|
|
|
currentMember = null; |
|
|
|
|
} |
|
|
|
|
var stack = GetBracketStack (GetMemberTextToCaret ().Item1); |
|
|
|
|
if (stack.Count == 0) |
|
|
|
|
currentMember = null; |
|
|
|
|
var provider = MemberProvider ?? new DefaultMemberProvider (this); |
|
|
|
|
provider.GetCurrentMembers (offset, out currentType, out currentMember); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#region Context helper methods
|
|
|
|
|
@ -558,5 +465,127 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
@@ -558,5 +465,127 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
class DefaultMemberProvider : IMemberProvider |
|
|
|
|
{ |
|
|
|
|
CSharpCompletionEngineBase engine; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public DefaultMemberProvider (CSharpCompletionEngineBase engine) |
|
|
|
|
{ |
|
|
|
|
this.engine = engine; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void GetCurrentMembers (int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember) |
|
|
|
|
{ |
|
|
|
|
var document = engine.document; |
|
|
|
|
var location = engine.location; |
|
|
|
|
|
|
|
|
|
currentType = null; |
|
|
|
|
|
|
|
|
|
foreach (var type in engine.CSharpParsedFile.TopLevelTypeDefinitions) { |
|
|
|
|
if (type.Region.Begin < location) |
|
|
|
|
currentType = type; |
|
|
|
|
} |
|
|
|
|
currentType = FindInnerType (currentType, location); |
|
|
|
|
|
|
|
|
|
// location is beyond last reported end region, now we need to check, if the end region changed
|
|
|
|
|
if (currentType != null && currentType.Region.End < location) { |
|
|
|
|
if (!IsInsideType (currentType, location)) |
|
|
|
|
currentType = null; |
|
|
|
|
} |
|
|
|
|
currentMember = null; |
|
|
|
|
if (currentType != null) { |
|
|
|
|
foreach (var member in currentType.Members) { |
|
|
|
|
if (member.Region.Begin < location && (currentMember == null || currentMember.Region.Begin < member.Region.Begin)) |
|
|
|
|
currentMember = member; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// location is beyond last reported end region, now we need to check, if the end region changed
|
|
|
|
|
// NOTE: Enums are a special case, there the "last" field needs to be treated as current member
|
|
|
|
|
if (currentMember != null && currentMember.Region.End < location && currentType.Kind != TypeKind.Enum) { |
|
|
|
|
if (!IsInsideType (currentMember, location)) |
|
|
|
|
currentMember = null; |
|
|
|
|
} |
|
|
|
|
var stack = engine.GetBracketStack (engine.GetMemberTextToCaret ().Item1); |
|
|
|
|
if (stack.Count == 0) |
|
|
|
|
currentMember = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IUnresolvedTypeDefinition FindInnerType (IUnresolvedTypeDefinition parent, TextLocation location) |
|
|
|
|
{ |
|
|
|
|
if (parent == null) |
|
|
|
|
return null; |
|
|
|
|
var currentType = parent; |
|
|
|
|
foreach (var type in parent.NestedTypes) { |
|
|
|
|
if (type.Region.Begin < location && location < type.Region.End) |
|
|
|
|
currentType = FindInnerType (type, location); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return currentType; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsInsideType (IUnresolvedEntity currentType, TextLocation location) |
|
|
|
|
{ |
|
|
|
|
var document = engine.document; |
|
|
|
|
|
|
|
|
|
int startOffset = document.GetOffset (currentType.Region.Begin); |
|
|
|
|
int endOffset = document.GetOffset (location); |
|
|
|
|
bool foundEndBracket = false; |
|
|
|
|
|
|
|
|
|
var bracketStack = new Stack<char> (); |
|
|
|
|
|
|
|
|
|
bool isInString = false, isInChar = false; |
|
|
|
|
bool isInLineComment = false, isInBlockComment = false; |
|
|
|
|
|
|
|
|
|
for (int i = startOffset; i < endOffset; i++) { |
|
|
|
|
char ch = document.GetCharAt (i); |
|
|
|
|
switch (ch) { |
|
|
|
|
case '(': |
|
|
|
|
case '[': |
|
|
|
|
case '{': |
|
|
|
|
if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) |
|
|
|
|
bracketStack.Push (ch); |
|
|
|
|
break; |
|
|
|
|
case ')': |
|
|
|
|
case ']': |
|
|
|
|
case '}': |
|
|
|
|
if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) |
|
|
|
|
if (bracketStack.Count > 0) |
|
|
|
|
bracketStack.Pop (); |
|
|
|
|
break; |
|
|
|
|
case '\r': |
|
|
|
|
case '\n': |
|
|
|
|
isInLineComment = false; |
|
|
|
|
break; |
|
|
|
|
case '/': |
|
|
|
|
if (isInBlockComment) { |
|
|
|
|
if (i > 0 && document.GetCharAt (i - 1) == '*') |
|
|
|
|
isInBlockComment = false; |
|
|
|
|
} else if (!isInString && !isInChar && i + 1 < document.TextLength) { |
|
|
|
|
char nextChar = document.GetCharAt (i + 1); |
|
|
|
|
if (nextChar == '/') |
|
|
|
|
isInLineComment = true; |
|
|
|
|
if (!isInLineComment && nextChar == '*') |
|
|
|
|
isInBlockComment = true; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case '"': |
|
|
|
|
if (!(isInChar || isInLineComment || isInBlockComment)) |
|
|
|
|
isInString = !isInString; |
|
|
|
|
break; |
|
|
|
|
case '\'': |
|
|
|
|
if (!(isInString || isInLineComment || isInBlockComment)) |
|
|
|
|
isInChar = !isInChar; |
|
|
|
|
break; |
|
|
|
|
default : |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return bracketStack.Any (t => t == '{'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |