📄 expressionfinder.cs
字号:
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision: 915 $</version>
// </file>
using System;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Dom;
namespace CSharpBinding.Parser
{
/// <summary>
/// Description of ExpressionFinder.
/// </summary>
public class ExpressionFinder : IExpressionFinder
{
string fileName;
public ExpressionFinder(string fileName)
{
this.fileName = fileName;
}
#region Capture Context
ExpressionResult CreateResult(string expression, string inText, int offset)
{
if (expression == null)
return new ExpressionResult(null);
if (expression.StartsWith("using "))
return new ExpressionResult(expression.Substring(6).TrimStart(), ExpressionContext.Namespace, null);
if (!hadParenthesis && expression.StartsWith("new ")) {
return new ExpressionResult(expression.Substring(4).TrimStart(), GetCreationContext(), null);
}
if (IsInAttribute(inText, offset))
return new ExpressionResult(expression, ExpressionContext.Attribute);
return new ExpressionResult(expression);
}
ExpressionContext GetCreationContext()
{
UnGetToken();
if (GetNextNonWhiteSpace() == '=') { // was: "= new"
ReadNextToken();
if (curTokenType == Ident) { // was: "ident = new"
int typeEnd = offset;
ReadNextToken();
int typeStart = -1;
while (curTokenType == Ident) {
typeStart = offset + 1;
ReadNextToken();
if (curTokenType == Dot) {
ReadNextToken();
} else {
break;
}
}
if (typeStart >= 0) {
string className = text.Substring(typeStart, typeEnd - typeStart);
int pos = className.IndexOf('<');
string nonGenericClassName, genericPart;
int typeParameterCount = 0;
if (pos > 0) {
nonGenericClassName = className.Substring(0, pos);
genericPart = className.Substring(pos);
pos = 0;
do {
typeParameterCount += 1;
pos = genericPart.IndexOf(',', pos + 1);
} while (pos > 0);
} else {
nonGenericClassName = className;
genericPart = null;
}
ClassFinder finder = new ClassFinder(fileName, text, typeStart);
IReturnType t = finder.SearchType(nonGenericClassName, typeParameterCount);
IClass c = (t != null) ? t.GetUnderlyingClass() : null;
if (c != null) {
ExpressionContext context = ExpressionContext.TypeDerivingFrom(c, true);
if (context.ShowEntry(c)) {
if (genericPart != null) {
DefaultClass genericClass = new DefaultClass(c.CompilationUnit, c.ClassType, c.Modifiers, c.Region, c.DeclaringType);
genericClass.FullyQualifiedName = c.FullyQualifiedName + genericPart;
genericClass.Documentation = c.Documentation;
context.SuggestedItem = genericClass;
} else {
context.SuggestedItem = c;
}
}
return context;
}
}
}
} else {
UnGet();
ReadNextToken();
if (curTokenType == Ident && lastIdentifier == "throw") {
return ExpressionContext.TypeDerivingFrom(ProjectContentRegistry.Mscorlib.GetClass("System.Exception"), true);
}
}
return ExpressionContext.ObjectCreation;
}
bool IsInAttribute(string txt, int offset)
{
// Get line start:
int lineStart = offset;
while (--lineStart > 0 && txt[lineStart] != '\n');
bool inAttribute = false;
int parens = 0;
for (int i = lineStart + 1; i < offset; i++) {
char ch = txt[i];
if (char.IsWhiteSpace(ch))
continue;
if (!inAttribute) {
// outside attribute
if (ch == '[')
inAttribute = true;
else
return false;
} else if (parens == 0) {
// inside attribute, outside parameter list
if (ch == ']')
inAttribute = false;
else if (ch == '(')
parens = 1;
else if (!char.IsLetterOrDigit(ch) && ch != ',')
return false;
} else {
// inside attribute, inside parameter list
if (ch == '(')
parens++;
else if (ch == ')')
parens--;
}
}
return inAttribute && parens == 0;
}
#endregion
#region RemoveLastPart
/// <summary>
/// Removed the last part of the expression.
/// </summary>
/// <example>
/// "arr[i]" => "arr"
/// "obj.Field" => "obj"
/// "obj.Method(args,...)" => "obj.Method"
/// </example>
public string RemoveLastPart(string expression)
{
text = expression;
offset = text.Length - 1;
ReadNextToken();
if (curTokenType == Ident && Peek() == '.')
GetNext();
return text.Substring(0, offset + 1);
}
#endregion
#region Find Expression
public ExpressionResult FindExpression(string inText, int offset)
{
inText = FilterComments(inText, ref offset);
return CreateResult(FindExpressionInternal(inText, offset), inText, offset);
}
public string FindExpressionInternal(string inText, int offset)
{
// warning: Do not confuse this.offset and offset
this.text = inText;
this.offset = this.lastAccept = offset;
this.state = START;
hadParenthesis = false;
if (this.text == null) {
return null;
}
while (state != ERROR) {
ReadNextToken();
state = stateTable[state, curTokenType];
if (state == ACCEPT || state == ACCEPT2) {
lastAccept = this.offset;
}
if (state == ACCEPTNOMORE) {
lastExpressionStartPosition = this.offset + 1;
return this.text.Substring(this.offset + 1, offset - this.offset);
}
}
if (lastAccept < 0)
return null;
lastExpressionStartPosition = this.lastAccept + 1;
return this.text.Substring(this.lastAccept + 1, offset - this.lastAccept);
}
int lastExpressionStartPosition;
internal int LastExpressionStartPosition {
get {
return lastExpressionStartPosition;
}
}
#endregion
#region FindFullExpression
public ExpressionResult FindFullExpression(string inText, int offset)
{
int offsetWithoutComments = offset;
string textWithoutComments = FilterComments(inText, ref offsetWithoutComments);
string expressionBeforeOffset = FindExpressionInternal(textWithoutComments, offsetWithoutComments);
if (expressionBeforeOffset == null || expressionBeforeOffset.Length == 0)
return CreateResult(null, textWithoutComments, offsetWithoutComments);
StringBuilder b = new StringBuilder(expressionBeforeOffset);
// append characters after expression
bool wordFollowing = false;
int i;
for (i = offset + 1; i < inText.Length; ++i) {
char c = inText[i];
if (Char.IsLetterOrDigit(c) || c == '_') {
if (Char.IsWhiteSpace(inText, i - 1)) {
wordFollowing = true;
break;
}
b.Append(c);
} else if (Char.IsWhiteSpace(c)) {
// ignore whitespace
} else if (c == '(' || c == '[') {
int otherBracket = SearchBracketForward(inText, i + 1, c, (c == '(') ? ')' : ']');
if (otherBracket < 0)
break;
if (c == '[') {
// do not include [] when it is an array declaration (versus indexer call)
bool ok = false;
for (int j = i + 1; j < otherBracket; j++) {
if (inText[j] != ',' && !char.IsWhiteSpace(inText, j)) {
ok = true;
break;
}
}
if (!ok) {
break;
}
}
b.Append(inText, i, otherBracket - i + 1);
break;
} else if (c == '<') {
// accept only if this is a generic type reference
int typeParameterEnd = FindEndOfTypeParameters(inText, i);
if (typeParameterEnd < 0)
break;
b.Append(inText, i, typeParameterEnd - i + 1);
i = typeParameterEnd;
} else {
break;
}
}
ExpressionResult res = CreateResult(b.ToString(), textWithoutComments, offsetWithoutComments);
if (res.Context == ExpressionContext.Default && wordFollowing) {
b = new StringBuilder();
for (; i < inText.Length; ++i) {
char c = inText[i];
if (char.IsLetterOrDigit(c) || c == '_')
b.Append(c);
else
break;
}
if (b.Length > 0) {
if (ICSharpCode.NRefactory.Parser.CSharp.Keywords.GetToken(b.ToString()) < 0) {
res.Context = ExpressionContext.Type;
}
}
}
return res;
}
int FindEndOfTypeParameters(string inText, int offset)
{
int level = 0;
for (int i = offset; i < inText.Length; ++i) {
char c = inText[i];
if (Char.IsLetterOrDigit(c) || Char.IsWhiteSpace(c)) {
// ignore identifiers and whitespace
} else if (c == ',' || c == '?' || c == '[' || c == ']') {
// , : seperating generic type parameters
// ? : nullable types
// [] : arrays
} else if (c == '<') {
++level;
} else if (c == '>') {
--level;
} else {
return -1;
}
if (level == 0)
return i;
}
return -1;
}
#endregion
#region SearchBracketForward
// like CSharpFormattingStrategy.SearchBracketForward, but operates on a string.
private int SearchBracketForward(string text, int offset, char openBracket, char closingBracket)
{
bool inString = false;
bool inChar = false;
bool verbatim = false;
bool lineComment = false;
bool blockComment = false;
if (offset < 0) return -1;
int brackets = 1;
for (; offset < text.Length; ++offset) {
char ch = text[offset];
switch (ch) {
case '\r':
case '\n':
lineComment = false;
inChar = false;
if (!verbatim) inString = false;
break;
case '/':
if (blockComment) {
if (offset > 0 && text[offset - 1] == '*') {
blockComment = false;
}
}
if (!inString && !inChar && offset + 1 < text.Length) {
if (!blockComment && text[offset + 1] == '/') {
lineComment = true;
}
if (!lineComment && text[offset + 1] == '*') {
blockComment = true;
}
}
break;
case '"':
if (!(inChar || lineComment || blockComment)) {
if (inString && verbatim) {
if (offset + 1 < text.Length && text[offset + 1] == '"') {
++offset; // skip escaped quote
inString = false; // let the string go on
} else {
verbatim = false;
}
} else if (!inString && offset > 0 && text[offset - 1] == '@') {
verbatim = true;
}
inString = !inString;
}
break;
case '\'':
if (!(inString || lineComment || blockComment)) {
inChar = !inChar;
}
break;
case '\\':
if ((inString && !verbatim) || inChar)
++offset; // skip next character
break;
default:
if (ch == openBracket) {
if (!(inString || inChar || lineComment || blockComment)) {
++brackets;
}
} else if (ch == closingBracket) {
if (!(inString || inChar || lineComment || blockComment)) {
--brackets;
if (brackets == 0) {
return offset;
}
}
}
break;
}
}
return -1;
}
#endregion
#region Comment Filter and 'inside string watcher'
int initialOffset;
public string FilterComments(string text, ref int offset)
{
if (text.Length <= offset)
return null;
this.initialOffset = offset;
StringBuilder outText = new StringBuilder();
int curOffset = 0;
while (curOffset <= initialOffset) {
char ch = text[curOffset];
switch (ch) {
case '@':
if (curOffset + 1 < text.Length && text[curOffset + 1] == '"') {
outText.Append(text[curOffset++]); // @
outText.Append(text[curOffset++]); // "
if (!ReadVerbatimString(outText, text, ref curOffset)) {
return null;
}
}else{
outText.Append(ch);
++curOffset;
}
break;
case '\'':
outText.Append(ch);
curOffset++;
if(! ReadChar(outText, text, ref curOffset)) {
return null;
}
break;
case '"':
outText.Append(ch);
curOffset++;
if (!ReadString(outText, text, ref curOffset)) {
return null;
}
break;
case '/':
if (curOffset + 1 < text.Length && text[curOffset + 1] == '/') {
offset -= 2;
curOffset += 2;
if (!ReadToEOL(text, ref curOffset, ref offset)) {
return null;
}
} else if (curOffset + 1 < text.Length && text[curOffset + 1] == '*') {
offset -= 2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -