Я работаю над расширением Visual Studio, которое предоставляет некоторые функции для пользовательского языка. Я сделал простую подсветку синтаксиса, и я хочу перейти к таким вещам, как подсветка синтаксической ошибки, согласование фигурных скобок, определение и т.д. Основная проблема, которую я сейчас рассматриваю, заключается в том, что для этого требуются разные типы тегов, которые (насколько мне видно) потребуют разных тегов. Тем не менее, я не вижу никакого интуитивного способа обмена информацией между метками, так как все три из этих вещей могут быть сделаны при одном анализе содержимого. Я имею в виду, я мог бы разобрать его три раза, но это не похоже на хорошее решение.
Как я могу вернуть несколько тегов из тегов (возможно, использовать ITag?) или обмениваться информацией между несколькими тегами?
Моя текущая структура такова:
internal class HighlightWordTagger : ITagger<ClassificationTag>
{
ITextBuffer TextBuffer;
IClassificationType Keyword;
IClassificationType Comment;
IClassificationType Literal;
// Probably a giant memory leak
Dictionary<ITextSnapshot, List<TagSpan<ClassificationTag>>> SnapshotResults = new Dictionary<ITextSnapshot, List<TagSpan<ClassificationTag>>>();
public HighlightWordTagger(ITextBuffer sourceBuffer, IClassificationTypeRegistryService typeService)
{
TextBuffer = sourceBuffer;
TextBuffer.Changed += (sender, args) =>
{
LexSnapshot(args.After);
TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(args.After, new Span(0, args.After.Length))));
};
Keyword = typeService.GetClassificationType("WideKeyword");
Comment = typeService.GetClassificationType("WideComment");
Literal = typeService.GetClassificationType("WideLiteral");
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
LexSnapshot(spans[0].Snapshot);
foreach (var snapshotspan in SnapshotResults[spans[0].Snapshot])
{
foreach (var span in spans)
{
if (snapshotspan.Span.IntersectsWith(span))
{
yield return snapshotspan;
}
}
}
}
Span SpanFromLexer(Lexer.Range range)
{
return new Span((int)range.begin.offset, (int)(range.end.offset - range.begin.offset));
}
void LexSnapshot(ITextSnapshot shot)
{
if (SnapshotResults.ContainsKey(shot))
return;
var lexer = new Lexer();
var list = new List<TagSpan<ClassificationTag>>();
SnapshotResults[shot] = list;
lexer.Read(
shot.GetText(),
(where, what) =>
{
if (what == Lexer.Failure.UnlexableCharacter)
return false;
var loc = new Span(
(int)where.offset,
(int)shot.Length - (int)where.offset
);
if (what == Lexer.Failure.UnterminatedComment)
list.Add(new TagSpan<ClassificationTag>(new SnapshotSpan(shot, loc), new ClassificationTag(Comment)));
if (what == Lexer.Failure.UnterminatedStringLiteral)
list.Add(new TagSpan<ClassificationTag>(new SnapshotSpan(shot, loc), new ClassificationTag(Literal)));
return false;
},
where =>
{
// Clamp this so it doesn't go over the end when we add \n in the lexer.
where.end.offset = where.end.offset > shot.Length ? (uint)(shot.Length) : where.end.offset;
var loc = SpanFromLexer(where);
list.Add(new TagSpan<ClassificationTag>(new SnapshotSpan(shot, loc), new ClassificationTag(Comment)));
},
token => {
var location = SpanFromLexer(token.location);
if (token.type == Lexer.TokenType.String || token.type == Lexer.TokenType.Integer)
{
list.Add(new TagSpan<ClassificationTag>(new SnapshotSpan(shot, location), new ClassificationTag(Literal)));
}
if (lexer.IsKeyword(token.type))
{
list.Add(new TagSpan<ClassificationTag>(new SnapshotSpan(shot, location), new ClassificationTag(Keyword)));
}
return false;
}
);
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
}
Я мог бы, наверное, лучше справиться с повторным лексированием, но это для другого вопроса.