Search code examples
c#visual-studiomef

Syntax Highlight doesn't work for IVsInvisibleEditor


I have created an instance of IVsInvisibleEditor. I've used following code to achieve this:

    public IWpfTextViewHost CreateEditor(string targetFile)
    {
        var componentModel = (IComponentModel)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(SComponentModel));
        var editorAdapter = componentModel.GetService<IVsEditorAdaptersFactoryService>();
        var editorFactoryService = componentModel.GetService<ITextEditorFactoryService>();
        var invisibleEditorManager = (IVsInvisibleEditorManager)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(SVsInvisibleEditorManager));

        IVsInvisibleEditor invisibleEditor;
        ErrorHandler.ThrowOnFailure(invisibleEditorManager.RegisterInvisibleEditor(
            targetFile,
            pProject: null,
            dwFlags: (uint)_EDITORREGFLAGS.RIEF_ENABLECACHING,
            pFactory: null,
            ppEditor: out invisibleEditor));

        var docDataPointer = IntPtr.Zero;
        Guid guidIVsTextLines = typeof(IVsTextLines).GUID;

        ErrorHandler.ThrowOnFailure(invisibleEditor.GetDocData(
            fEnsureWritable: 1,
            riid: ref guidIVsTextLines,
            ppDocData: out docDataPointer));

        //Create a code window adapter
        IVsTextLines docData = (IVsTextLines)Marshal.GetObjectForIUnknown(docDataPointer);           
        var codeWindow = editorAdapter.CreateVsCodeWindowAdapter(VisualStudioServices.OLEServiceProvider);
        ErrorHandler.ThrowOnFailure(codeWindow.SetBuffer(docData));
        IVsTextView textView;
        ErrorHandler.ThrowOnFailure(codeWindow.GetPrimaryView(out textView));

        var userData = (IVsUserData)codeWindow;
        var uniqueMoniker = Guid.NewGuid().ToString();
        Guid bufferMonikerGuid = typeof(IVsUserData).GUID;
        userData.SetData(ref bufferMonikerGuid, uniqueMoniker);
        var guid = VSConstants.VsTextBufferUserDataGuid.VsTextViewRoles_guid;
        userData.SetData(ref guid, editorFactoryService.CreateTextViewRoleSet(editorFactoryService.DefaultRoles).ToString());

        var  host = editorAdapter.GetWpfTextViewHost(textView);
        host.TextView.Options.SetOptionValue(DefaultTextViewHostOptions.ZoomControlId, false);

        return host;
    }

It all works, except there is no syntax highlighting if, for instance, I use C# (.cs) file to display inside the invisible editor. After some research I've found that I need also to add a document to the RDT, which I did with following function:

    uint RegisterDocument(string targetFile)
    {
        //Then when creating the IVsInvisibleEditor, find and lock the document 
        uint itemID;
        IntPtr docData;
        uint docCookie;
        IVsHierarchy hierarchy;
        var runningDocTable = (IVsRunningDocumentTable)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(SVsRunningDocumentTable));

        var result = runningDocTable.FindAndLockDocument(
            dwRDTLockType: (uint)_VSRDTFLAGS.RDT_EditLock, 
            pszMkDocument: targetFile,
            ppHier: out hierarchy,
            pitemid: out itemID,
            ppunkDocData: out docData,
            pdwCookie: out docCookie);

        return docCookie;
    }

Now, if I display C# file, then highlight is shown (and also intelisense works), but if I for instance display a sql file, then once again I don't have syntax highlighting. Now, I investigated a little bit further and realized that maybe I should attach a language service on a document, so I used this code to achieve this:

        Guid sqlGuid = LanguageServices.Guids.TSQL;
        docData.SetLanguageServiceID(ref sqlGuid);

and voila, it works, now syntax highlighting is also working on sql files, but now I have another problem, multi-view on sql file won't work. For instance, if I have a file MyQuery.sql and I open it with invisible editor, it will be opened, syntax will be shown and everything works as expected, but now, if I try to open the same file (while invisible editor is still open) then I get the following message:

enter image description here

I believe that something with RDT is not OK when using sql file, it seems that when language services are not attached, then adding to RDT is somehow ignored, which is strange, because same code works with C# files. Is there some other way to open particular file (can be of any type) using invisible editor, am I missing something?


Solution

  • After spending days on this issue, I finally found what was the problem. So, in case that someone else also have trouble with it here is the explanation:

    When creating the InvisibleEditor, a lot of things are happening "underneath", one thing that should be set is also the ContentType for particular ITextBuffer. When sql file (which is passed as moniker parameter to RegisterInvisibleEditor method) is used, ContentType is set to plain text (but in case of csharp files, it is set to CSharp ContentType), which is clearly wrong, however, calling:

        Guid sqlGuid = LanguageServices.Guids.TSQL;
        docData.SetLanguageServiceID(ref sqlGuid);
    

    should solve this issue, setting language service will set the correct ContentType, but for some reason in my case this didn't work. After some debugging I found that ContentType, when it is opened by dbl click in the solution explorer, is different one than anticipated. It was "Sql Server Tools" ContentType not "T-SQL90", so, I tried to find guid of that type using IVsTextBuffer:

            Guid langId;
            vsTextBuffer.GetLanguageServiceID(out langId);
    

    this gave me correct guid, so when replaced with my tsql languange service, it worked as expected. In case that someone needs these guids, they can be found at HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\Languages (depending on VS installation).

    At the end, here is the helper class which I used to map these guids:

        public static class LanguageServices
        {
            public static class Guids
            {
                public static Guid VisualBasic = new Guid("E34ACDC0-BAAE-11D0-88BF-00A0C9110049");
                public static Guid CSharp = new Guid("694DD9B6-B865-4C5B-AD85-86356E9C88DC");
                public static Guid FSharp = new Guid("bc6dd5a5-d4d6-4dab-a00d-a51242dbaf1b");
                public static Guid CPlusPlus = new Guid("B2F072B0-ABC1-11D0-9D62-00C04FD9DFD9");
                public static Guid Css = new Guid("A764E898-518D-11d2-9A89-00C04F79EFC3");
                public static Guid Html = new Guid("58E975A0-F8FE-11D2-A6AE-00104BCC7269");
                public static Guid JavaScript = new Guid("59E2F421-410A-4fc9-9803-1F4E79216BE8");
                public static Guid TSQL = new Guid("43AF1158-FED5-432e-8E8F-23B6FD592857");
                public static Guid SQL = new Guid("ed1a9c1c-d95c-4dc1-8db8-e5a28707a864");
                public static Guid Xaml = new Guid("c9164055-039b-4669-832d-f257bd5554d4");
                public static Guid Xml = new Guid("f6819a78-a205-47b5-be1c-675b3c7f0b8e");
            }
        }