c# - 如何编写加载项以在 Visual Studio 编辑器中更改文本颜色?

标签 c# visual-studio syntax-highlighting visual-studio-addins regions

在搜索了很长时间以寻找一种在 Visual Studio 中更改 #region 指令的文本颜色的简单方法后,我得出的结论是没有简单的方法可以做到这一点。

我知道如何更改#region 语句的颜色,以及如何更改折叠区域的颜色,但我想更改带有区域描述的文本的颜色。所以:

#region Some text   <--- all this text should be in a different color

public void Test()
{
}

#endregion          <--- this too

似乎很多人都在寻找这样的东西 - 请参阅 How to change the color of expanded regions' titles in VS2008? .

所以我一直在考虑编写一个简单的 Visual Studio 插件来更改颜色。 但是,它比我想象的要复杂,有像 Snapshot 这样的类。 , Tagger , Classifier , WpfTextViewCreationListener , AdornmentLayer等等

简单地说,我不知道从哪里开始!我关注了几个 tutorials在 MSDN 站点上,但它们对于我想要做的事情来说似乎太复杂了。

有人能告诉我最简单的方法吗? IE。我应该使用 VS SDK 中的哪些类/方法/事件。我不介意颜色是否无法通过 UI 等进行自定义。我正在使用 VS2010。

编辑:刚刚有了 mztools向我推荐的网站;我也去那里看看。还注意到 StackOverflow 的 region 语法高亮显示正是我想要的!

最佳答案

我最终想出了一个解决方案,至少对于 VS2010。 虽然我使用它来为“#region”和“#endregion”标记着色,但类似的解决方案应该适用于 Visual Studio 窗口中的任何文本内容。

似乎可以通过创建一个 IViewTaggerProvider 来解决此类问题,它将使用“分类”“标记”部分源代码。 Visual Studio 将为标记有该分类的文本提供一种样式,然后用户可以通过工具>选项...>环境>字体和颜色将其更改为所需的样式。


Tagger 提供者看起来像:

[Export(typeof(IViewTaggerProvider))]
[ContentType("any")]
[TagType(typeof(ClassificationTag))]
public sealed class RegionTaggerProvider : IViewTaggerProvider
{
    [Import]
    public IClassificationTypeRegistryService Registry;

    [Import]
    internal ITextSearchService TextSearchService { get; set; }

    public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
    {
        if (buffer != textView.TextBuffer)
            return null;

        var classType = Registry.GetClassificationType("region-foreground");
        return new RegionTagger(textView, TextSearchService, classType) as ITagger<T>;
    }
}

这将创建一个 ITagger 对象,在给定 Visual Studio TextView 的情况下,该对象将使用给定的分类类型标记部分文本。请注意,这将适用于所有 TextView (即源代码编辑器、“查找结果”窗口等)。可以通过编辑 ContentType 属性来更改此设置(仅更改为 C#?)。


分类类型(在本例中为“region-foreground”)定义为:

public static class TypeExports
{
    [Export(typeof(ClassificationTypeDefinition))]
    [Name("region-foreground")]
    public static ClassificationTypeDefinition OrdinaryClassificationType;
}

[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "region-foreground")]
[Name("region-foreground")]
[UserVisible(true)]
[Order(After = Priority.High)]
public sealed class RegionForeground : ClassificationFormatDefinition
{
    public RegionForeground()
    {
        DisplayName = "Region Foreground";
        ForegroundColor = Colors.Gray;
    }
}

Order 属性决定了与其他可能也适用于一段文本的分类相比,该分类何时应用。 DisplayName 将在工具 > 选项... 对话框中使用。


定义分类后,ITagger 类可以搜索 View 的文本并为其找到的文本的适用部分提供分类。

简单地说,它的工作是监听 TextView 的 ViewLayoutChanged 事件,当提供的 TextView 的内容发生变化时(例如,因为用户输入了某些内容),该事件会被触发。

然后它必须在文本中搜索它感兴趣的文本区域(称为“跨度”)。在这里,它返回包含 #region#endregion 的行跨度。我一直保持简单,但用于查找匹配项的 TextSearchService 也可以使用正则表达式进行搜索。

最后,为 Visual Studio 提供了一个方法来检索它找到的文本的标签,称为 GetTags()。对于给定的跨度集合,这将返回带有分类标签的文本跨度,即应该以某种方式分类的那些跨度区域。

它的代码是:

public sealed class RegionTagger : ITagger<ClassificationTag>
{
    private readonly ITextView m_View;
    private readonly ITextSearchService m_SearchService;
    private readonly IClassificationType m_Type;
    private NormalizedSnapshotSpanCollection m_CurrentSpans;

    public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };

    public RegionTagger(ITextView view, ITextSearchService searchService, IClassificationType type)
    {
        m_View = view;
        m_SearchService = searchService;
        m_Type = type;

        m_CurrentSpans = GetWordSpans(m_View.TextSnapshot);

        m_View.GotAggregateFocus += SetupSelectionChangedListener;
    }

    private void SetupSelectionChangedListener(object sender, EventArgs e)
    {
        if (m_View != null)
        {
            m_View.LayoutChanged += ViewLayoutChanged;
            m_View.GotAggregateFocus -= SetupSelectionChangedListener;
        }
    }

    private void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
    {
        if (e.OldSnapshot != e.NewSnapshot)
        {
            m_CurrentSpans = GetWordSpans(e.NewSnapshot);
            TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(e.NewSnapshot, 0, e.NewSnapshot.Length)));
        }
    }

    private NormalizedSnapshotSpanCollection GetWordSpans(ITextSnapshot snapshot)
    {
        var wordSpans = new List<SnapshotSpan>();
        wordSpans.AddRange(FindAll(@"#region", snapshot).Select(regionLine => regionLine.Start.GetContainingLine().Extent));
        wordSpans.AddRange(FindAll(@"#endregion", snapshot).Select(regionLine => regionLine.Start.GetContainingLine().Extent));
        return new NormalizedSnapshotSpanCollection(wordSpans);
    }

    private IEnumerable<SnapshotSpan> FindAll(String searchPattern, ITextSnapshot textSnapshot)
    {
        if (textSnapshot == null)
            return null;

        return m_SearchService.FindAll(
            new FindData(searchPattern, textSnapshot) {
                    FindOptions = FindOptions.WholeWord | FindOptions.MatchCase
                });
    }

    public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
    {
        if (spans == null || spans.Count == 0 || m_CurrentSpans.Count == 0)
            yield break;

        ITextSnapshot snapshot = m_CurrentSpans[0].Snapshot;
        spans = new NormalizedSnapshotSpanCollection(spans.Select(s => s.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive)));

        foreach (var span in NormalizedSnapshotSpanCollection.Intersection(m_CurrentSpans, spans))
        {
            yield return new TagSpan<ClassificationTag>(span, new ClassificationTag(m_Type));
        }
    }
}

为简洁起见,我省略了命名空间和 using 语句,它们通常采用 Microsoft.VisualStudio.Text.* 的形式。为了使这些可用,Visual Studio 2010 SDK必须先下载。


过去几个月我一直在使用这个解决方案,没有任何问题。

我注意到的一个限制是颜色没有“混合”,因此不透明度低于 100% 的颜色不会“淡出”跨度中的现有颜色 - 这可能有助于保留语法突出显示。

我也不太了解它的效率,因为看起来它会在每次按键时重复搜索文档。我还没有研究过 Visual Studio 是否以某种方式对此进行了优化。我确实注意到 Visual Studio 在处理大文件(> ~1000 行)时速度变慢,但我也使用 Resharper,所以我不能将其单独归因于这个插件。

由于这主要是通过猜测进行编码,因此我欢迎任何可以澄清或简化事情或改进代码性能的评论或代码更改。

关于c# - 如何编写加载项以在 Visual Studio 编辑器中更改文本颜色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9697876/

相关文章:

asp.net - Visual Studio ASP.NET Development Server 开始返回 HTTP 503 错误的原因是什么?

c# - TFS 2010-从 checkin 任务中获取文件的特定版本

c# - Entity Framework Code First 通用数据库审计字段

c# - NHibernate 的独立 Hello World 类型示例

c# - ASP.NET Core WebApi

intellij-idea - IntelliJ IDEA中ES6模板文字(反引号)的文本突出显示

macos - Mac OS X 有语法高亮编辑器组件吗

具有接口(interface)的 C# MVC 通用存储库类

c# - 尝试按原样编译 Microsoft 提供的 .NET 源代码,出现数百个错误

regex - 如何使用正则表达式捕获每行的第 n 个模式?