Visual Studio 2010+中的自定义关键字着色

时间:2014-07-10 20:47:24

标签: c# visual-studio visual-studio-extensions

我尝试在我的Visual Studio编辑器中为C#代码仅为某些关键字添加自定义着色。我希望能够将任何实现IDisposable的类型着色为不同的颜色。理想情况下,我想在我可以编辑的某种配置中创建一个简单的类/接口列表,这些类/接口派生自IDisposable。 (虽然如果你说有一个方法/插件会自动找到所有的一次性类型并独立地为它们着色,那就是圣杯)。

我做了大量的研究,它看起来像一个编辑分类器"扩展可能会成功。然而,我创建了一个仅仅试图为单词" Stream"虽然它确实击中了我试图突出显示该单词的代码,但它并没有在编辑器中突出显示。

我已将我的VS扩展程序添加到Github here

这看起来真的应该是相当简单的,但我已经在这个问题上找到了许多小巷,但却发现了死胡同。有没有更简单的方法来做到这一点,还是我的扩展被破坏了?

更新

很奇怪。我刚刚再次运行我的扩展程序,虽然它没有突出显示编辑器中的文本,但它突出显示了" Stream"在鼠标悬停在类型/变量上的弹出文本中!有没有办法让它适用于编辑器?

enter image description here

3 个答案:

答案 0 :(得分:3)

根据您是否正在使用Jetbrains Resharper,您可以为此编写插件。这样,您不仅可以在变量上添加IDisposable的可视通知,而且还可以提供快速修正,当且仅当它没有被调用时,这就是我想要捕获的内容。请注意,我可以想象已经有一个R#插件。我知道我也考虑过这一点,但是我懒得为它写一个插件。

不要误会我的意思 - 如果您还没有使用r#,那么您应该考虑尝试一下。

除此之外,您还可以使用此功能:API-QuickFix

还有一些方法来定义自定义关键字,就像resharper一样,由自定义标记给出并对其应用快速修正。

PS:不,我不在Jetbrains工作。它就是那么好了:)

更新:

潜在的VS扩展修复?

检查一下:MSDN Link Highlighting Text

我尝试打开你的github项目,但是我不知道我只会检查msdn。你似乎是从错误的阶级派生出来满足你的需求?

MSDN关键字"编辑器 - 扩展编辑器 - 演练:突出显示文本"

我知道SO需要网站上的代码,但msdn链接下降是不太可能的,并且使用给定的信息可以很容易地找到内容:)

答案 1 :(得分:2)

我参加派对有点晚了,但是,嘿,为什么不把我的2美分扔进去。

正如您在问题中解释的那样,您的项目有两个基本部分:

  1. 查找实现IDisposable
  2. 的类
  3. 突出显示
  4. 第一个是迄今为止最困难的,但并非不可能。基于单词列表的方法可能是最简单的,但是Roslyn应该可以在运行中弄清楚哪些类继承IDisposible

    您还可以在构建后在后台加载项目的已编译的.exe / .dll并找出其中的类型,但您仍然需要编写一些神奇的胶水代码,以找出代码中的哪些短类名称引用程序集中的实际全名类。

    第二部分,突出显示,一旦你知道如何做就很容易(这有助于我花了最后几个月全职工作来扩展VS)。当然,使用Visual Studio,没有什么比它看起来那么简单(尽管微软努力使其变得用户友好)。所以,我已经构建了一个示例扩展,它突出了名为" Stream"在C#文件中,可以帮助您入门。

    highlighting at work

    相关代码如下,完整project source is on GitHub)。它从分类标记提供者开始:

    [Export(typeof(ITaggerProvider))]
    [ContentType("CSharp")]
    [TagType(typeof(ClassificationTag))]
    [Name("HighlightDisposableTagger")]
    public class HighlightDisposableTaggerProvider : ITaggerProvider
    {
        [Import]
        private IClassificationTypeRegistryService _classificationRegistry = null;
    
        [Import]
        private IClassifierAggregatorService _classifierAggregator = null;
    
        private bool _reentrant;
    
        public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
        {
            if (_reentrant)
                return null;
    
            try {
                _reentrant = true;
                var classifier = _classifierAggregator.GetClassifier(buffer);
                return new HighlightDisposableTagger(buffer, _classificationRegistry, classifier) as ITagger<T>;
            }
            finally {
                _reentrant = false;
            }
        }
    }
    

    然后是标记器本身:

    public class HighlightDisposableTagger : ITagger<ClassificationTag>
    {
        private const string DisposableFormatName = "HighlightDisposableFormat";
    
        [Export]
        [Name(DisposableFormatName)]
        public static ClassificationTypeDefinition DisposableFormatType = null;
    
        [Export(typeof(EditorFormatDefinition))]
        [Name(DisposableFormatName)]
        [ClassificationType(ClassificationTypeNames = DisposableFormatName)]
        [UserVisible(true)]
        public class DisposableFormatDefinition : ClassificationFormatDefinition
        {
            public DisposableFormatDefinition()
            {
                DisplayName = "Disposable Format";
                ForegroundColor = Color.FromRgb(0xFF, 0x00, 0x00);
            }
        }
    
        public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
    
        private ITextBuffer _subjectBuffer;
        private ClassificationTag _tag;
        private IClassifier _classifier;
        private bool _reentrant;
    
        public HighlightDisposableTagger(ITextBuffer subjectBuffer, IClassificationTypeRegistryService typeService, IClassifier classifier)
        {
            _subjectBuffer = subjectBuffer;
    
            var classificationType = typeService.GetClassificationType(DisposableFormatName);
            _tag = new ClassificationTag(classificationType);
            _classifier = classifier;
        }
    
        public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
        {
            if (_reentrant) {
                return Enumerable.Empty<ITagSpan<ClassificationTag>>();
            }
    
            var tags = new List<ITagSpan<ClassificationTag>>();
            try {
                _reentrant = true;
    
                foreach (var span in spans) {
                    if (span.IsEmpty)
                        continue;
    
                    foreach (var token in _classifier.GetClassificationSpans(span)) {
                        if (token.ClassificationType.IsOfType(/*PredefinedClassificationTypeNames.Identifier*/ "User Types")) {
                            // TODO: Somehow figure out if this refers to a class which implements IDisposable
                            if (token.Span.GetText() == "Stream") {
                                tags.Add(new TagSpan<ClassificationTag>(token.Span, _tag));
                            }
                        }
                    }
                }
                return tags;
            }
            finally {
                _reentrant = false;
            }
        }
    }
    

    我只在VS2010上对此进行了测试,但它也应该适用于VS2013(唯一可能不同的是类别分类名称,但是通过良好放置的断点很容易发现)。我从来没有写过VS2012的扩展程序,因此我无法对此发表评论,但我知道它在大多数方面与VS2013非常接近。

答案 2 :(得分:1)

所以,一个可能的解决方案(我相信这个有效):

1)创建自己的内容类型,该类型继承自csharp。

2)创建新的TextViewCreationListener,它将使用您自己的内容替换所有“csharp”内容类型,从而可能“撤消”所有其他分类器。

3)注册您的分类器以处理您自己的内容类型。

以下是一些代码:

[Export(typeof(IVsTextViewCreationListener))]
[ContentType("csharp")]
[TextViewRole(PredefinedTextViewRoles.Editable)]
class TextViewCreationListener : IVsTextViewCreationListener {
    internal readonly IVsEditorAdaptersFactoryService _adaptersFactory;

    [Import] internal IContentTypeRegistryService ContentTypeRegistryService = null;

    [ImportingConstructor]
    public TextViewCreationListener(IVsEditorAdaptersFactoryService adaptersFactory) {
        _adaptersFactory = adaptersFactory;
    }

    #region IVsTextViewCreationListener Members

    public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) {
        var textView = _adaptersFactory.GetWpfTextView(textViewAdapter);

        var myContent = ContentTypeRegistryService.GetContentType(MyContentType);
        if(myContent == null)
        {
            ContentTypeRegistryService.AddContentType(MyContentType, new[] {"csharp"});
            myContent = ContentTypeRegistryService.GetContentType(MyContentType);
        }

        // some kind of check if the content type is not already MyContentType. 
        textView.TextBuffer.ChangeContentType(myContent, null);
    }

    #endregion
}

现在,只需修改IClassifierProvider即可注册您自己的内容类型,如下所示:[ContentType(MyContentType)]

在你自己的IClassifier中,你基本上可以进行自己的计算,一旦你认为你无法处理这些东西,就可以将控制传递给其他分类器。

如果您使用MEF并导入IClassifierAggregatorService,您可以获得一个“MASTER-classifier”,它将为您运行所有逻辑。我还没有实现它,但我在过去提出类似的建议,似乎有效。或者,您可以将[ImportMany]List<IClassifier>一起使用,并过滤掉csharp的内容?!