为VS扩展创建具有多个标记类型的标记器

Pup*_*ppy 11 visual-studio-2012

我正在开发一个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 { };
    }
Run Code Online (Sandbox Code Playgroud)

我可能会做得更好,不要那么多,但这是另一个问题.

Pup*_*ppy 5

我最终不得不将这些问题分开.您可以使用ITextBuffer.Properties.GetOrCreateSingletonProperty将您选择的任意对象与文本缓冲区相关联.我最终创建了一个单独的lexer类,将它与文本缓冲区相关联,然后简单地执行除了标记之外的几乎所有逻辑.然后在每个标记器的实现中,我只是在lexer中查询结果,然后标记它们.这允许多个标记符依赖于相同的词法分析器实例.

考虑到大多数词法分析器和解析器会生成不止一种标签,我很惊讶VS会让你非常难以产生这种结果.