Create dedicated theme syntax for Visual Studio

I would like to create a syntax shortcut in Visual Studio 2012 (and above) that supports different themes (dark, light, blue).

The Visual Studio Editor Classifier project template explains how to create your own colors in your environment using Microsoft.VisualStudio.Text.Classification.ClassificationFormatDefinition . It works great ...

... until you understand that there are different themes (and higher) in Visual Studio 2012, and you do not support them. Your cute blue color identifiers on a light theme become unreadable in a dark thematic environment.

As far as I understand, if you change your ClassificationFormatDefinition in Tools / Options / Fonts and Colors in a given topic (for example: Light), this will not affect the same ClassificationFormatDefinition in another theme (for example: Dark). Colors seem independent on various topics.

It's good. But how can I define the same ClassificationFormatDefinition name (for example, MyKeywords) that has the same name in all themes, but provides different colors for them? Exactly the same as Visual Studio’s own “Identifier”, which is black by default on the theme “Light” and the default value while on a black theme.

I know about the Microsoft.VisualStudio.PlatformUI.VSColorTheme.ThemeChanged event, which allows me to receive notifications when color themes change. Should I use this and somehow master the existing ClassificationFormatDefinition and assign them new colors based on a new theme? But this also raises the question: will these changed colors be saved in the environment, that is, if I restart Visual Studio, my changes will be next time in all different themes.

I did not find any attribute that would indicate which topic ClassificationFormatDefinition supports, and I did not find a lot of useful article on this subject.

Any help was appreciated.

+7
visual-studio syntax-highlighting visual-studio-2012 vspackage visual-studio-extensions
source share
3 answers

It can help you, the code from F # Power Tools seems to be listening for the ThemeChanged event and updating the classifiers - https://github.com/fsprojects/VisualFSharpPowerTools/blob/a7d7aa9dd3d2a90f21c6947867ac7d7163b9f99a/srciftfiftpfftfftpft

+2
source share

Ok, here is the workaround I found. He is far from perfect, but he is as good as he is.

The trick is to use a different basic definition when you define your own type of classification. This will use the default color for different themes. The important thing is that you should not define your own color in MyKeywordsFormatDefinition , because it disables the default behavior when switching between themes. Therefore, try to find a basic definition that matches your color. Look for the predefined Classificatoin types here: Microsoft.VisualStudio.Language.StandardClassification.PredefinedClassificationTypeNames

 internal static class Classifications { // ... public const string MyKeyword = "MyKeyword"; // ... } [Export(typeof(EditorFormatDefinition))] [ClassificationType(ClassificationTypeNames = Classifications.MyKeyword)] [Name("MyKeywords")] [DisplayName("My Keywords")] [UserVisible(true)] internal sealed class MyKeywordsFormatDefinition: ClassificationFormatDefinition { // Don't set the color here, as it will disable the default color supporting themes } [Export(typeof(ClassificationTypeDefinition))] [Name(Classifications.MyKeyword)] [BaseDefinition(PredefinedClassificationTypeNames.Keyword)] internal static ClassificationTypeDefinition MyKeywordsTypeDefinition; 

I hope this is helpful to some of you. It can even help improve the correct solution if you can truly set your own color without reusing existing color definitions.

+1
source share

I had a similar problem. I developed a syntax shortcut for DSL at work. It has two sets of colors - for light and dark themes. I needed a way to switch between these two color sets at runtime when the VS theme changes.

After some searching, I found a solution in F # github in the code responsible for integrating with VS: https://github.com/majocha/visualfsharp/blob/bcd1929828a3c424b464fe2275590f11446b288e/vsintegration/src/FSharp.Editor.fClass / Classification

The code in the F # repository is very similar to the code from Omer Raviv’s answer. I translated it into C # and got something like this:

 using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Windows.Media; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using DefGuidList = Microsoft.VisualStudio.Editor.DefGuidList; using VSConstants = Microsoft.VisualStudio.VSConstants; //... internal abstract class EditorFormatBase : ClassificationFormatDefinition, IDisposable { private const string textCategory = "text"; private readonly string classificationTypeName; protected EditorFormatBase() { VSColorTheme.ThemeChanged += VSColorTheme_ThemeChanged; //Get string ID which has to be attached with NameAttribute for ClassificationFormatDefinition-derived classes Type type = this.GetType(); classificationTypeName = type.GetCustomAttribute<NameAttribute>()?.Name; if (classificationTypeName != null) { ForegroundColor = VSColors.GetThemedColor(classificationTypeName); //Call to my class VSColors which returns correct color for the theme } } private void VSColorTheme_ThemeChanged(ThemeChangedEventArgs e) { //Here MyPackage.Instance is a singleton of my extension Package derived class, it contains references to // IClassificationFormatMapService and // IClassificationTypeRegistryService objects if (MyPackage.Instance?.ClassificationFormatMapService == null || MyPackage.Instance.ClassificationRegistry == null || classificationTypeName == null) { return; } var fontAndColorStorage = ServiceProvider.GlobalProvider.GetService(typeof(SVsFontAndColorStorage)) as IVsFontAndColorStorage; var fontAndColorCacheManager = ServiceProvider.GlobalProvider.GetService(typeof(SVsFontAndColorCacheManager)) as IVsFontAndColorCacheManager; if (fontAndColorStorage == null || fontAndColorCacheManager == null) return; Guid guidTextEditorFontCategory = DefGuidList.guidTextEditorFontCategory; fontAndColorCacheManager.CheckCache(ref guidTextEditorFontCategory, out int _ ); if (fontAndColorStorage.OpenCategory(ref guidTextEditorFontCategory, (uint) __FCSTORAGEFLAGS.FCSF_READONLY) != VSConstants.S_OK) { //Possibly log warning/error, in F# source it's ignored } Color? foregroundColorForTheme = VSColors.GetThemedColor(classificationTypeName); //VSColors is my class which stores colors, GetThemedColor returns color for the theme if (foregroundColorForTheme == null) return; IClassificationFormatMap formatMap = MyPackage.Instance.ClassificationFormatMapService .GetClassificationFormatMap(category: textCategory); if (formatMap == null) return; try { formatMap.BeginBatchUpdate(); ForegroundColor = foregroundColorForTheme; var myClasType = MyPackage.Instance.ClassificationRegistry .GetClassificationType(classificationTypeName); if (myClasType == null) return; ColorableItemInfo[] colorInfo = new ColorableItemInfo[1]; if (fontAndColorStorage.GetItem(classificationTypeName, colorInfo) != VSConstants.S_OK) //comment from F# repo: "we don't touch the changes made by the user" { var properties = formatMap.GetTextProperties(myClasType); var newProperties = properties.SetForeground(ForegroundColor.Value); formatMap.SetTextProperties(myClasType, newProperties); } } catch (Exception) { //Log error here, in F# repo there are no catch blocks, only finally block } finally { formatMap.EndBatchUpdate(); } } void IDisposable.Dispose() { VSColorTheme.ThemeChanged -= VSColorTheme_ThemeChanged; } } 

Ive used the class above as the base class for all my ClassificationFormatDefinition classes.

0
source share

All Articles