c# – Setting Language Service in Custom Editor not working


Right now I have implemented my own custom editor and is configured so that when I open up any .cs file, VS will display the file using my editor. However, the text is bland and has no coloring to it. Therefore, I am trying to make the file appear as a .cs file in the traditional VS code editor. I believe the way to achieve this is by setting the language service for the text buffer. The result of the SetLanguageServiceID is 0 so it is called correctly but I am not sure why this is not working. Here is my implemented editor factory:

using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using System.IO;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TextManager.Interop;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.ComponentModelHost;

namespace CustomTextEditorFactory
{
    [Guid("F86E3EAF-07DF-40CD-8E4A-6C01BA7E2BAF")]
    internal class CustomEditorFactory : IVsEditorFactory
    {
        private ServiceProvider _serviceProvider;
        private readonly Package _package;

        public CustomEditorFactory(Package package)
        {
            _package = package ?? throw new ArgumentNullException(nameof(package));
        }

        public int SetSite(IOleServiceProvider psp)
        {
            _serviceProvider = new ServiceProvider(psp);
            return VSConstants.S_OK;
        }

        public object GetService(Type serviceType)
        {
            return _serviceProvider.GetService(serviceType);
        }

        public int MapLogicalView(ref Guid rguidLogicalView, out string pbstrPhysicalView)
        {
            pbstrPhysicalView = null;
            if (rguidLogicalView == VSConstants.LOGVIEWID_Primary ||
                rguidLogicalView == VSConstants.LOGVIEWID_Code)
            {
                return VSConstants.S_OK;
            }
            return VSConstants.E_NOTIMPL;
        }

        public int Close()
        {
            _serviceProvider?.Dispose();
            return VSConstants.S_OK;
        }

        public int CreateEditorInstance(
            uint grfCreateDoc,
            string pszMkDocument,
            string pszPhysicalView,
            IVsHierarchy pvHier,
            uint itemid,
            IntPtr punkDocDataExisting,
            out IntPtr ppunkDocView,
            out IntPtr ppunkDocData,
            out string pbstrEditorCaption,
            out Guid pguidCmdUI,
            out int pgrfCDW)
        {
            ppunkDocView = IntPtr.Zero;
            ppunkDocData = IntPtr.Zero;
            pbstrEditorCaption = null;
            pguidCmdUI = Guid.Empty;
            pgrfCDW = 0;

            // Found some sort of factory that is able to create a VsTextBuffer object
            IComponentModel compMod = GetService(typeof(SComponentModel)) as IComponentModel;
            IVsEditorAdaptersFactoryService adapterFactory = compMod.GetService<IVsEditorAdaptersFactoryService>();
            IVsTextBuffer textBuffer = adapterFactory.CreateVsTextBufferAdapter(GetService(typeof(IOleServiceProvider)) as IOleServiceProvider);

            // add file content to the text buffer
            string fileContent = File.ReadAllText(pszMkDocument);
            textBuffer.InitializeContent(fileContent, fileContent.Length);

            // set the language service for the text buffer for syntax highlighting
            Guid languageServiceGuid = new Guid("{694DD9B6-B865-4C5B-AD85-86356E9C88DC}");
            int result = textBuffer.SetLanguageServiceID(ref languageServiceGuid);

            // CodeWindow object contains IVsTextLines for its text buffer so cast the buffer as such
            IVsTextLines textLines = (IVsTextLines)textBuffer;


            Type codeWindowType = typeof(IVsCodeWindow);
            Guid riid = codeWindowType.GUID;
            Guid clsid = typeof(VsCodeWindowClass).GUID;
            IVsCodeWindow codeWindow = (IVsCodeWindow)_package.CreateInstance(ref clsid, ref riid, codeWindowType);

            codeWindow.SetBuffer(textLines);

            // Obtain the primary view managed by the CodeWindow object (we set the buffer but not the text view)
            IVsTextView primaryView;
            codeWindow.GetPrimaryView(out primaryView);

            // set output parameters for VS
            ppunkDocView = Marshal.GetIUnknownForObject(codeWindow);
            ppunkDocData = Marshal.GetIUnknownForObject(textLines);
            pbstrEditorCaption = "Custom!!!";
            pguidCmdUI = Guid.Empty;
            pgrfCDW = 0;

            return VSConstants.S_OK;
        }
    }
}

And here is my package file (this is a VSIX project)

using Microsoft.VisualStudio.Shell;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Task = System.Threading.Tasks.Task;

namespace CustomTextEditorFactory
{
    [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
    [ProvideEditorExtension(typeof(CustomEditorFactory), ".cs", 700)]
    [Guid(RenderCustomTextEditorPackage.PackageGuidString)]
    public sealed class RenderCustomTextEditorPackage : AsyncPackage
    {
        public const string PackageGuidString = "AF177B7B-8E7C-4F93-B0B6-AC5670B94080";

        #region Package Members

        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            // Register the editor factory
            this.RegisterEditorFactory(new CustomEditorFactory(this));
        }

        #endregion
    }
}

I have tried setting the language service of textLines itself but that did not change anything. I also have tried setting the language service before and after content initialization.

Leave a Reply

Your email address will not be published. Required fields are marked *