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.
269 lines
7.7 KiB
269 lines
7.7 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using System.Linq; |
|
|
|
using ICSharpCode.NRefactory; |
|
using ICSharpCode.NRefactory.Parser; |
|
using ICSharpCode.NRefactory.Parser.VB; |
|
|
|
namespace ICSharpCode.SharpDevelop.Dom.VBNet |
|
{ |
|
/// <summary> |
|
/// Description of VBNetExpressionFinder. |
|
/// </summary> |
|
public class VBNetExpressionFinder : IExpressionFinder |
|
{ |
|
ParseInformation parseInformation; |
|
IProjectContent projectContent; |
|
ILexer lexer; |
|
Location targetPosition; |
|
List<int> lineOffsets; |
|
|
|
int LocationToOffset(Location location) |
|
{ |
|
if (location.Line <= 0 || location.Line >= lineOffsets.Count) |
|
return -1; |
|
return lineOffsets[location.Line - 1] + location.Column - 1; |
|
} |
|
|
|
Location OffsetToLocation(int offset) |
|
{ |
|
int lineNumber = lineOffsets.BinarySearch(offset); |
|
if (lineNumber < 0) { |
|
lineNumber = (~lineNumber) - 1; |
|
} |
|
return new Location(offset - lineOffsets[lineNumber] + 1, lineNumber + 1); |
|
} |
|
|
|
public VBNetExpressionFinder(ParseInformation parseInformation) |
|
{ |
|
this.parseInformation = parseInformation; |
|
if (parseInformation != null && parseInformation.CompilationUnit != null) { |
|
projectContent = parseInformation.CompilationUnit.ProjectContent; |
|
} else { |
|
projectContent = DefaultProjectContent.DummyProjectContent; |
|
} |
|
} |
|
|
|
public ExpressionResult FindExpression(string text, int offset) |
|
{ |
|
Init(text, offset); |
|
|
|
ExpressionFinder p = new ExpressionFinder(); |
|
lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(text)); |
|
Token t = lexer.NextToken(); |
|
|
|
// put all tokens in front of targetPosition into the EF-Parser |
|
while (t.EndLocation < targetPosition) { |
|
p.InformToken(t); |
|
t = lexer.NextToken(); |
|
} |
|
|
|
// put current token into EF-Parser if it cannot be continued (is simple operator) |
|
if (t.EndLocation == targetPosition && ((t.Kind <= Tokens.ColonAssign && t.Kind > Tokens.Identifier) || t.Kind == Tokens.EOL)) { |
|
p.InformToken(t); |
|
t = lexer.NextToken(); |
|
} |
|
|
|
// make sure semantic actions are executed |
|
p.Advance(); |
|
|
|
// remember current state, we'll use it to determine the context |
|
var block = p.CurrentBlock; |
|
|
|
ExpressionContext context = p.IsIdentifierExpected && !p.IsMissingModifier ? ExpressionContext.IdentifierExpected : GetContext(block); |
|
|
|
BitArray expectedSet; |
|
|
|
try { |
|
expectedSet = p.GetExpectedSet(); |
|
} catch (InvalidOperationException) { |
|
expectedSet = null; |
|
} |
|
|
|
// put current token into EF-Parser |
|
if (t.Location < targetPosition) { |
|
p.InformToken(t); |
|
} |
|
|
|
if (p.Errors.Any()) { |
|
foreach (var e in p.Errors) |
|
LoggingService.Warn("not expected: " + e); |
|
} |
|
|
|
if (p.NextTokenIsPotentialStartOfExpression) |
|
return new ExpressionResult("", new DomRegion(targetPosition.Line, targetPosition.Column), context, expectedSet); |
|
|
|
int lastExpressionStartOffset = LocationToOffset(p.CurrentBlock.lastExpressionStart); |
|
|
|
if (lastExpressionStartOffset < 0) |
|
return new ExpressionResult("", new DomRegion(targetPosition.Line, targetPosition.Column), context, expectedSet); |
|
|
|
return MakeResult(text, lastExpressionStartOffset, offset, context, expectedSet); |
|
} |
|
|
|
ExpressionResult MakeResult(string text, int startOffset, int endOffset, ExpressionContext context, BitArray expectedKeywords) |
|
{ |
|
// partial/incomplete expressions (especially between comments) need this hack. |
|
// see http://community.sharpdevelop.net/forums/t/11951.aspx (first post) |
|
if (startOffset > endOffset) { |
|
int tmp = startOffset; |
|
startOffset = endOffset; |
|
endOffset = tmp; |
|
} |
|
|
|
return new ExpressionResult(TrimComment(text.Substring(startOffset, endOffset - startOffset)).Trim(), |
|
DomRegion.FromLocation(OffsetToLocation(startOffset), OffsetToLocation(endOffset)), |
|
context, expectedKeywords); |
|
} |
|
|
|
string TrimComment(string text) |
|
{ |
|
bool inString = false; |
|
int i = 0; |
|
|
|
while (i < text.Length) { |
|
char ch = text[i]; |
|
|
|
if (ch == '"') |
|
inString = !inString; |
|
|
|
bool isInWord = (i > 0 && char.IsLetterOrDigit(text[i - 1])) |
|
|| (i + 1 < text.Length && char.IsLetterOrDigit(text[i + 1])); |
|
|
|
if ((ch == '\'' || ch == '_') && !inString && !isInWord) { |
|
int eol = text.IndexOfAny(new[] { '\r', '\n' }, i); |
|
|
|
if (eol > -1) { |
|
if(text[eol] == '\r' && eol + 1 < text.Length && text[eol + 1] == '\n') |
|
eol++; |
|
|
|
text = text.Remove(i, eol - i); |
|
} else { |
|
text = text.Remove(i); |
|
} |
|
|
|
continue; |
|
} |
|
|
|
i++; |
|
} |
|
|
|
return text; |
|
} |
|
|
|
void Init(string text, int offset) |
|
{ |
|
lineOffsets = new List<int>(); |
|
lineOffsets.Add(0); |
|
for (int i = 0; i < text.Length; i++) { |
|
if (i == offset) { |
|
targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); |
|
} |
|
if (text[i] == '\n') { |
|
lineOffsets.Add(i + 1); |
|
} else if (text[i] == '\r') { |
|
if (i + 1 < text.Length && text[i + 1] != '\n') { |
|
lineOffsets.Add(i + 1); |
|
} |
|
} |
|
} |
|
if (offset == text.Length) { |
|
targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); |
|
} |
|
} |
|
|
|
ExpressionContext GetContext(Block block) |
|
{ |
|
switch (block.context) { |
|
case Context.Global: |
|
return ExpressionContext.Global; |
|
case Context.TypeDeclaration: |
|
return ExpressionContext.TypeDeclaration; |
|
case Context.Type: |
|
return ExpressionContext.Type; |
|
case Context.Body: |
|
return ExpressionContext.MethodBody; |
|
case Context.Importable: |
|
return ExpressionContext.Importable; |
|
case Context.ObjectCreation: |
|
return ExpressionContext.ObjectCreation; |
|
case Context.Parameter: |
|
return ExpressionContext.Parameter; |
|
} |
|
|
|
return ExpressionContext.Default; |
|
} |
|
|
|
public ExpressionResult FindFullExpression(string text, int offset) |
|
{ |
|
Init(text, offset); |
|
|
|
ExpressionFinder p = new ExpressionFinder(); |
|
lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(text)); |
|
Token t; |
|
|
|
Block block = Block.Default; |
|
|
|
var expressionDelimiters = new[] { Tokens.EOL, Tokens.Colon, Tokens.Dot, Tokens.TripleDot, Tokens.DotAt }; |
|
|
|
while (true) { |
|
t = lexer.NextToken(); |
|
p.InformToken(t); |
|
|
|
if (block == Block.Default && t.EndLocation > targetPosition) |
|
block = p.CurrentBlock; |
|
if (block != Block.Default && (block.isClosed || expressionDelimiters.Contains(t.Kind) && block == p.CurrentBlock)) |
|
break; |
|
if (t.Kind == Tokens.EOF) |
|
break; |
|
} |
|
|
|
if (p.Errors.Any()) { |
|
foreach (var e in p.Errors) |
|
LoggingService.Warn("not expected: " + e); |
|
} |
|
|
|
BitArray expectedSet; |
|
|
|
try { |
|
expectedSet = p.GetExpectedSet(); |
|
} catch (InvalidOperationException) { |
|
expectedSet = null; |
|
} |
|
|
|
int tokenOffset; |
|
if (t == null || t.Kind == Tokens.EOF) |
|
tokenOffset = text.Length; |
|
else |
|
tokenOffset = LocationToOffset(t.Location); |
|
|
|
int lastExpressionStartOffset = LocationToOffset(block.lastExpressionStart); |
|
if (lastExpressionStartOffset >= 0) { |
|
if (offset < tokenOffset) { |
|
// offset is in front of this token |
|
return MakeResult(text, lastExpressionStartOffset, tokenOffset, GetContext(block), expectedSet); |
|
} else { |
|
// offset is IN this token |
|
return MakeResult(text, lastExpressionStartOffset, offset, GetContext(block), expectedSet); |
|
} |
|
} else { |
|
return new ExpressionResult(null, GetContext(block)); |
|
} |
|
} |
|
|
|
public string RemoveLastPart(string expression) |
|
{ |
|
return expression; |
|
} |
|
|
|
#region Helpers |
|
|
|
#endregion |
|
} |
|
}
|
|
|