1

So I'm trying to create my own visual studio language plugin for a DSL. My question is, I've implemented LanguageService and override GetScanner. Through debugging I see that it actually returns the IScanner implementation, but it never calls ScanTokenAndProvideInfoAboutIt.

All of this loading happens after opening a file with an extension my language package is tied to, which leads me to believe that it's actually trying to use my plugin for it.

I'm a bit lost here, the process of creating visual studio plugins looks intuitive on the surface, but seems cursed under the covers.

internal class ValLanguageScanner : IScanner
{
    private IVsTextBuffer m_buffer;
    string m_source;

    public ValLanguageScanner(IVsTextBuffer buffer)
    {
        // Breakpoint gets hit here(to to creation in GetScanner method)
        m_buffer = buffer;
    }

    bool IScanner.ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state)
    {
        // Breakpoint never gets hit here
        var foundToken = false;
        return foundToken;
    }

    void IScanner.SetSource(string source, int offset)
    {
        m_source = source.Substring(offset);
    }
}

public class ValLanguageService : LanguageService
{
    private const string _name = "Val";
    private LanguagePreferences m_preferences;
    private ValLanguageScanner m_scanner;

    public override LanguagePreferences GetLanguagePreferences()
    {
        if (m_preferences == null)
        {
            m_preferences = new LanguagePreferences(this.Site,typeof(ValLanguageService).GUID,this.Name);
            m_preferences.Init();
        }
        return m_preferences;
    }

    public override IScanner GetScanner(IVsTextLines buffer)
    {
        if (m_scanner == null)
        {
            m_scanner = new ValLanguageScanner(buffer);
        }
        return m_scanner;
    }

    public override AuthoringScope ParseSource(ParseRequest req)
    {
        return new ValLanguageAuthoringScope();
    }

    public override string GetFormatFilterList()
    {
        return "Val(*.val)|*.val";
    }

    public override string Name
    {
        get { return _name; }
    }
}

    [PackageRegistration(UseManagedResourcesOnly = true)]
[ProvideServiceAttribute(typeof(ValLanguageService),
                         ServiceName = "Val Language Service")]
[ProvideLanguageExtensionAttribute(typeof(ValLanguageService),
                                   ".val")]
[ProvideLanguageExtensionAttribute(typeof(ValLanguageService),
                                   ".vallist")]
[InstalledProductRegistration("#110", "#112", "1.0")]
public class ValLanguagePackage : Package, IOleComponent
{
    private uint m_componentID;

    public ValLanguagePackage()
    {
        Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
    }

    protected override void Initialize()
    {
        base.Initialize();  // required

        // Proffer the service.
        IServiceContainer serviceContainer = this as IServiceContainer;
        ValLanguageService langService = new ValLanguageService();
        langService.SetSite(this);
        serviceContainer.AddService(typeof(ValLanguageService),
                                    langService,
                                    true);

        // Register a timer to call our language service during
        // idle periods.
        IOleComponentManager mgr = GetService(typeof(SOleComponentManager))
                                   as IOleComponentManager;
        if (m_componentID == 0 && mgr != null)
        {
            OLECRINFO[] crinfo = new OLECRINFO[1];
            crinfo[0].cbSize = (uint)Marshal.SizeOf(typeof(OLECRINFO));
            crinfo[0].grfcrf = (uint)_OLECRF.olecrfNeedIdleTime |
                                          (uint)_OLECRF.olecrfNeedPeriodicIdleTime;
            crinfo[0].grfcadvf = (uint)_OLECADVF.olecadvfModal |
                                          (uint)_OLECADVF.olecadvfRedrawOff |
                                          (uint)_OLECADVF.olecadvfWarningsOff;
            crinfo[0].uIdleTimeInterval = 1000;
            int hr = mgr.FRegisterComponent(this, crinfo, out m_componentID);
        }
    }


    protected override void Dispose(bool disposing)
    {
        if (m_componentID != 0)
        {
            Debug.WriteLine("OMG DISPOSING");
            IOleComponentManager mgr = GetService(typeof(SOleComponentManager))
                                       as IOleComponentManager;
            if (mgr != null)
            {
                int hr = mgr.FRevokeComponent(m_componentID);
            }
            m_componentID = 0;
        }
        Debug.WriteLine("OMG DISPOSING BASE");
        base.Dispose(disposing);
        Debug.WriteLine("OMG DISPOSED");
    }


    #region IOleComponent Members

    public int FDoIdle(uint grfidlef)
    {
        bool bPeriodic = (grfidlef & (uint)_OLEIDLEF.oleidlefPeriodic) != 0;
        // Use typeof(TestLanguageService) because we need to
        // reference the GUID for our language service.
        LanguageService service = GetService(typeof(ValLanguageService))
                                  as LanguageService;
        if (service != null)
        {
            service.OnIdle(bPeriodic);
        }
        return 0;
    }

    public int FContinueMessageLoop(uint uReason,
                                    IntPtr pvLoopData,
                                    MSG[] pMsgPeeked)
    {
        return 1;
    }

    public int FPreTranslateMessage(MSG[] pMsg)
    {
        return 0;
    }

    public int FQueryTerminate(int fPromptUser)
    {
        return 1;
    }

    public int FReserved1(uint dwReserved,
                          uint message,
                          IntPtr wParam,
                          IntPtr lParam)
    {
        return 1;
    }

    public IntPtr HwndGetWindow(uint dwWhich, uint dwReserved)
    {
        return IntPtr.Zero;
    }

    public void OnActivationChange(IOleComponent pic,
                                   int fSameComponent,
                                   OLECRINFO[] pcrinfo,
                                   int fHostIsActivating,
                                   OLECHOSTINFO[] pchostinfo,
                                   uint dwReserved)
    {
    }

    public void OnAppActivate(int fActive, uint dwOtherThreadID)
    {
    }

    public void OnEnterState(uint uStateID, int fEnter)
    {
    }

    public void OnLoseActivation()
    {
    }

    public void Terminate()
    {
    }

    #endregion
}
Kelly Elton
  • 4,373
  • 10
  • 53
  • 97
  • I know what you feel. The easiest way to solve such problems is to directly debug the VS2013 code(the language part is mostly written in C#!). Get a decent decompiler which can generate pdb's(Reflector for example), and see WHEN & WHY should your ScanTokenAndProvideInfoAboutIt be called. By the way, I don't suggest you to implement language like that. Take a look at PyTools implementation to see how they have integrated python language. I've been where you are, methods not getting called, etc.. Next problem you're going to have is ParseSource not being called :-) – Erti-Chris Eelmaa May 23 '14 at 14:07
  • Implement IClassifier if you want to add syntax highlighting. – Erti-Chris Eelmaa May 23 '14 at 14:08
  • I want syntax highlighting as well as intellisense(which is more important here). – Kelly Elton May 23 '14 at 16:52
  • then implement IIntellisenseController http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.language.intellisense.iintellisensecontroller.aspx – Erti-Chris Eelmaa May 23 '14 at 20:18
  • Right but what about custom project types and a build process etc? All of that isn't in MEF yet is it? – Kelly Elton May 23 '14 at 21:46
  • I have implemented custom project types through MPF. Not exactly sure what you mean by build process though. – Erti-Chris Eelmaa May 27 '14 at 09:52
  • Right but the IClassifier and IIntellisenseController are MEF not MPF – Kelly Elton May 27 '14 at 17:16
  • Yes. MPF allows you to modify everything else, and MEF allows you to modify features related to Visual Studio editor. That's what I've done(I have custom project type, custom build process), completely full-blown language implementation through MEF. – Erti-Chris Eelmaa May 27 '14 at 18:55
  • So ok, you're saying that I can have both MPF and MEF inside of one plugin? – Kelly Elton May 27 '14 at 21:01
  • Oh jesus, so I set the LanugageService implementations name to the same name I set in the ProvideServiceAttribute, and it seemed to work. – Kelly Elton May 27 '14 at 21:36
  • Also, you're right when you said "Next problem you're going to have is ParseSource not being called :-)" – Kelly Elton May 27 '14 at 22:38
  • Yeah nvm, that part was way easier. – Kelly Elton May 27 '14 at 23:11
  • Did you try the ProvideAutoLoad attribute on your Package class (like `[ProvideAutoLoad(UIContextGuids.NoSolution)]` and `[ProvideAutoLoad(UIContextGuids.SolutionExists)]`)? Maybe it didn't get loaded correctly for whatever reason. My scanner works here. I've implicitly implemented the interface, but that shouldn't cause a problem. – Ray Jun 15 '14 at 06:25

0 Answers0