Create Visual Studio Theme Specific Syntax Highlighting

杀马特。学长 韩版系。学妹 提交于 2019-12-04 03:27:52

Ok, here's a workaround I've found. It is far from perfect, but it is as good as it gets.

The trick is to use another base definition when you define your own classification type. This will use their default color for the different themes. The important thing is that you must not define your own color in MyKeywordsFormatDefinition because that disables the default behavior when switching between themes. So try to find a base definition that matches your color. Look for 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 it will be useful for some of you. Even maybe help to refine a proper solution when you can actually set your own color without reusing existing color definitions.

This might help you, code from F# Power Tools, seems to be listening to the ThemeChanged event and updating the classifiers - https://github.com/fsprojects/VisualFSharpPowerTools/blob/a7d7aa9dd3d2a90f21c6947867ac7d7163b9f99a/src/FSharpVSPowerTools/SyntaxConstructClassifierProvider.cs

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

After some search I found a solution in the F# github in the code responsible for the integration with VS: https://github.com/majocha/visualfsharp/blob/bcd1929828a3c424b464fe2275590f11446b288e/vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs

The code in F# repo is quite similar to the code from Omer Raviv’s answer. I translated it into C# and get 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's 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;
    }
}

I’ve used the class above as the base class for all my ClassificationFormatDefinition classes.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!