How to write an add-in for changing the color of text in a Visual Studio editor?

After a long search for an easy way to change the color of the text of the #region directive in Visual Studio, I came to the conclusion that there is no easy way to do this.

I know how to change the color of the #region operator and how to change the color of the reduced area, but I want to change the color of the text with the description of the region. So:

 #region Some text <--- all this text should be in a different color public void Test() { } #endregion <--- this too 

Many seem to be looking for something like this - see How do I change the color of the headers of the extended regions in VS2008? .

So, I looked at creating a simple Visual Studio add-in to change the color. However, it is harder than I thought it would be with classes like Snapshot , Tagger , Classifier , WpfTextViewCreationListener , AdornmentLayer , etc.

Simply put, I don't know where to start! I have completed a couple of tutorials on MSDN, but they seem too complicated for what I'm trying to do.

Can someone point me to the easiest way to do this? I.e. what classes / methods / events in the VS SDK should I use. I do not mind if the color is not customizable through the interface, etc. I am using VS2010.

Edit: the mztools website I have just recommended ; I'll take a look too. Also noticed that the syntax highlighting of the StackOverflow region is pretty much what I want!

+6
source share
2 answers

In the end, I came up with a solution, at least for VS2010. Although I used this to color the #region and #endregion 'tags, a similar solution should be applicable to any textual content in a Visual Studio window.

It seems that this problem can be solved by creating IViewTaggerProvider , which will "tag" parts of the source code with "classification". Visual Studio will provide a style for the text marked with this classification, which can then be changed by the user in the desired style using Tools> Options ...> Environment> Fonts and Colors.


The Tagger provider looks like this:

 [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>; } } 

This creates an ITagger object, which, given the textual presentation of Visual Studio, will tag parts of the text with the specified classification type. Note that this will work for all text views (i.e. Source Code Editor, Find Results window, etc.). Perhaps this can be changed by editing the ContentType attribute (only C# ?).


The type of classification (in this case, "region-foreground") is defined as:

 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; } } 

The Order attribute determines when the classification will be applied compared to other classifications, which can also apply to a range of text. DisplayName will be used in the Tools> Options ... dialog box.


After determining the classification, the ITagger class can search for text in the form and provide classifications for the relevant sections of the found text.

Simply put, its task is to listen to the ViewLayoutChanged event of the text view, which is fired when the content of the provided text view changes (for example, because the user typed something).

Then he should look for the text for the area of ​​interest of the text (called "span"). Here it returns line spacing containing either #region or #endregion . I kept it simple, but the TextSearchService used to search for matches can also do searches using regular expressions.

Finally, a visual method is provided for Visual Studio to retrieve the tags for the found text, called GetTags() . For this collection of ranges, this will lead to the return of text spaces using classification tags, that is, the areas of those spaces that need to be classified in a certain way.

His code is:

 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)); } } } 

For brevity, I skipped namespaces and using statements, which are usually in the form Microsoft.VisualStudio.Text.* . To make them available, you must first download the Visual Studio 2010 SDK .


I have been using this solution for the past few months without any problems.

The only restriction I noticed is colors that do not mix, so a color with an opacity of less than 100% will not "disappear" from existing colors in the range - which can be useful for maintaining syntax highlighting.

I also have little information about its effectiveness, as it looks like it will repeatedly search for a document at each key press. I have not done research to see how Visual Studio somehow optimizes this. I notice a slowdown in Visual Studio on large files (> ~ 1000 lines), but I also use Resharper, so I cannot attribute this to this plugin only.

Since this was mainly encoded using guesswork, I welcome any comments or code changes that may clarify or simplify things or improve code performance.

+11
source

I think you could start with a Visual Studio add-in project, it will use EnvDTE, which was considered the Visual Studio object model, and please find the MSDN document here: http://msdn.microsoft.com/en-us/vstudio/ bb968855 You can control the behavior of your visual studio, for example, debugger, code editor, etc. EnvDTE.

0
source

Source: https://habr.com/ru/post/910676/


All Articles