Как написать надстройку для изменения цвета текста в редакторе Visual Studio? - PullRequest
7 голосов
/ 14 марта 2012

После долгого поиска простого способа изменить цвет текста директивы #region в Visual Studio я пришел к выводу, что простого способа сделать это не существует.

Я знаю, какизменить цвет оператора #region и как изменить цвет свернутой области, но я хочу изменить цвет текста с описанием области.Итак:

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

public void Test()
{
}

#endregion          <--- this too

Кажется, многие люди ищут что-то подобное - см. Как изменить цвет заголовков расширенных областей в VS2008? .

Итак, я пытался написать простую надстройку для Visual Studio, чтобы изменить цвет.Однако это сложнее, чем я думал, с такими классами, как Snapshot, Tagger, Classifier, WpfTextViewCreationListener, AdornmentLayer и т. Д.

Проще говоря, я не знаю с чего начать!Я следовал за несколькими учебниками на сайте MSDN, но они кажутся слишком сложными для того, что я пытаюсь сделать.

Может кто-нибудь указать мне на самый простой способ сделать это?То есть.какие классы / методы / события в VS SDK я должен использовать.Я не против, если цвет не настраивается через пользовательский интерфейс и т.д.Я использую VS2010.

Редактировать: Только что рекомендованный мне сайт mztools ;Я тоже там посмотрю.Также заметил, что подсветка синтаксиса StackOverflow region s в значительной степени именно то, что я хочу!

Ответы [ 2 ]

13 голосов
/ 29 мая 2012

Я в конце концов нашел решение, по крайней мере, для 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, будет помечать части текста заданным типом классификации. Обратите внимание, что это будет работать для всех текстовых представлений (то есть редактора исходного кода, окон «Найти результаты» и т. Д.). Это можно изменить, отредактировав атрибут ContentType (просто C#?).


Тип классификации (в данном случае «регион-передний план») определяется как:

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 может осуществлять поиск текста представления и предоставлять классификации для применимых разделов текста, который он находит.

Проще говоря, его работа заключается в прослушивании события ViewLayoutChanged текстового представления, которое запускается при изменении содержимого предоставленного текстового представления (например, потому что пользователь что-то набрал).

Затем он должен искать в тексте интересующую его область текста (называемую «span»). Здесь он возвращает отрезки строк, содержащие либо #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));
        }
    }
}

Для краткости я опустил пространства имен и операторы использования, которые обычно имеют форму Microsoft.VisualStudio.Text.*. Чтобы они были доступны, сначала необходимо загрузить Visual Studio 2010 SDK .


Последние несколько месяцев я пользуюсь этим решением без проблем.

Я заметил одно ограничение: цвета не «смешиваются», поэтому цвет с непрозрачностью менее 100% не «затухает» существующие цвета в диапазоне, что может быть полезно для сохранения подсветки синтаксиса.

Я также мало представляю его эффективность, так как похоже, что он будет многократно искать документ при каждом нажатии клавиши. Я не провел исследования, чтобы увидеть, оптимизирует ли Visual Studio это как-то. Я замечаю замедление Visual Studio для больших файлов (> ~ 1000 строк), но я также использую Resharper, поэтому я не могу приписать это только этому плагину.

Поскольку это было написано в основном с использованием догадок, я приветствую любые комментарии или изменения кода, которые могли бы прояснить или упростить вещи или улучшить производительность кода.

0 голосов
/ 14 марта 2012

Полагаю, вы могли бы начать с проекта надстройки Visual Studio, он будет использовать EnvDTE, который рассматривался как объектная модель Visual Studio, и, пожалуйста, найдите документ MSDN здесь: http://msdn.microsoft.com/en-us/vstudio/bb968855 Вы можете контролировать свое поведение Visual Studioкак отладчик, редактор кода и так далее EnvDTE.

...