Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Visual Studio.NET / Extensibility / February 2005

Tip: Looking for answers? Try searching our database.

New language with managed code?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
ssyladin - 21 Feb 2005 17:41 GMT
I'm trying to add a new language service to VS 2003 using C#.  I've managed
to associate the extension to my project and instantiate the core editor
(using Dr. Ex's example of hosting the code editor in a toolwindow for the
bulk of my code), and I try to set the language of the text buffer with
GetLanguageServiceID , but I can't seem to figure out how to proffer my
IVsLanguageInfo object as a service to VS.

I have my own set of code to implement a lexer and parser, so I don't need
Babel.  I would like to do this in 100% C#, except for the wizard created
package installer.  I'm fine with having to manually set  registry entries
vs. using VSIP helpers...

Can anyone help?
"Ed Dore [MSFT]" - 22 Feb 2005 00:34 GMT
I did this a while back with the intent to build a language service for
.RGS files (to colorize them), but never did finish it up. I did get it
wired up properly though, at least to the point of dumping the text from
IVsColorizer::ColorizeLine to Trace.WriteLine :-)

Basically I had two classes (RGSColorizer which is derived from
IVsColorizer, and RGSLanguageService which is derived from
IVsLanguageInfo). The RGSLanguageService is decorated with a Guid
attribute. For example:

  [Guid("BE8FD236-8677-4625-9E3C-F5A2E521CBF0")]
  public class RGSLanguageService : IVsLanguageInfo
  {  ...  }

To register the language service I derived a new class from
RegistrationAttribute (see below) to set up the registry keys. Then it was
just a matter of proffering the service in my package object's Initialize
method which looked like the following:

     ...
     [ProvideLanguageService("RGS Language Service",
typeof(RGSLanguageService), ".rgs", typeof(RGSServices), 300)]
     ....
     public class RGSServices : MSVSIP.Helper.Package, IVsInstalledProduct
     {
        ..........
       protected override void Initialize()
       {
           Trace.WriteLine (string.Format("Entering Initialize() of: {0}",
this.ToString()));
           base.Initialize();
 
           // Create and proffer the language service
           languageService = new RGSLanguageService(this);
           ((IServiceContainer)this).AddService(languageService.GetType(),
languageService, true);
        }
      ...........
     }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple=true,
Inherited=true)]
    public class ProvideLanguageServiceAttribute : RegistrationAttribute
    {
        private string langName;
        private string guidLangService;
        private uint   langResID;
        private string guidPackage;
        private string fileextensions;  // ; delimited. for example:
".rgs;.txt;.cpp"

        private uint showCompletion;
        private uint showSmartIndent;
        private uint requestStockColors;
        private uint showHotURLs;
        private uint defaultToNonHotURLs;
        private uint defaultToInsertSpaces;
        private uint showDropdownBarOption;
        private uint singleCodeWindowOnly;
        private uint enableAdvMembersOption;
        private uint supportCF_HTML;
   
        private string GetGuidStrFromObject(object obj)
        {
            if (obj is string)
                return (string)obj;
            else if (obj is Type)
                return ((Type)obj).GUID.ToString("B");
            else if (obj is Guid)
                return ((Guid)obj).ToString("B");
            else
                throw new ArgumentException("Invalid ProvideLanguageService argument!");
        }

        public ProvideLanguageServiceAttribute(string langName, object
guidLangService, string fileextensions, object guidPackage, uint langResID)
        {
            this.langName = langName;
            this.fileextensions = fileextensions;
            this.langResID = langResID;
            this.guidLangService = GetGuidStrFromObject(guidLangService);
            this.guidPackage = GetGuidStrFromObject(guidPackage);
           
            this.showCompletion = 0;
            this.showSmartIndent = 0;
            this.requestStockColors = 0;
            this.showHotURLs = 0;
            this.defaultToNonHotURLs = 0;
            this.defaultToInsertSpaces = 0;
            this.showDropdownBarOption = 0;
            this.singleCodeWindowOnly = 1;
            this.enableAdvMembersOption = 0;
            this.supportCF_HTML = 0;
        }

        #region Properties
       
        public bool ShowCompletion
        {
            get
            {
                return showCompletion > 0;
            }
            set
            {
                if (value) showCompletion = 1;
                else showCompletion = 0;
            }
        }

        public bool ShowSmartIndent
        {
            get
            {
                return showSmartIndent > 0;
            }
            set
            {
                if (value) showSmartIndent = 1;
                else showSmartIndent = 0;
            }
        }

        public bool RequestStockColors
        {
            get
            {
                return requestStockColors > 0;
            }
            set
            {
                if (value) requestStockColors = 1;
                else requestStockColors = 0;
            }
        }

        public bool ShowHotURLs
        {
            get
            {
                return showHotURLs > 0;
            }
            set
            {
                if (value) showHotURLs = 1;
                else showHotURLs = 0;
            }       
        }

        public bool DefaultToNonHotURLs
        {
            get
            {
                return defaultToNonHotURLs > 0;
            }
            set
            {
                if (value) defaultToNonHotURLs = 1;
                else defaultToNonHotURLs = 0;
            }
        }

        public bool DefaultToInsertSpaces
        {
            get
            {
                return defaultToInsertSpaces > 0;
            }
            set
            {
                if (value) defaultToInsertSpaces = 1;
                else defaultToInsertSpaces = 0;
            }
        }

        public bool ShowDropdownBarOption
        {
            get
            {
                return showDropdownBarOption > 0;
            }
            set
            {
                if (value) showDropdownBarOption = 1;
                else showDropdownBarOption = 0;
            }
        }

        public bool SingleCodeWindowOnly
        {
            get
            {
                return singleCodeWindowOnly > 0;
            }
            set
            {
                if (value) singleCodeWindowOnly = 1;
                else singleCodeWindowOnly = 0;
            }
        }
       
        public bool EnableAdvMembersOption
        {
            get
            {
                return enableAdvMembersOption > 0;
            }
            set
            {
                if (value) enableAdvMembersOption = 1;
                else enableAdvMembersOption = 0;
            }
        }
       
        public bool SupportCF_HTML
        {
            get
            {
                return supportCF_HTML > 0;
            }
            set
            {
                if (value) supportCF_HTML = 1;
                else supportCF_HTML = 0;
            }
        }

        #endregion Properties

        public override void Register(RegistrationContext context)
        {
            // Register Language Service settings under <VS Reg
Root>\Languages\Languages Services\<langsvc name>
            string regKeyName = string.Format("Languages\\Language Services\\{0}",
langName);
            Key key = context.CreateKey(regKeyName);
            key.SetValue("", guidLangService);
            key.SetValue("LangResID", langResID);
            key.SetValue("Package", guidPackage);
            key.SetValue("ShowCompletion", showCompletion);
            key.SetValue("ShowSmartIndent", showSmartIndent);
            key.SetValue("RequestStockColors", requestStockColors);
            key.SetValue("ShowHotURLs", showHotURLs);
            key.SetValue("Default to Non Hot URLs", defaultToNonHotURLs);
            key.SetValue("DefaultToInsertSpaces", defaultToInsertSpaces);
            key.SetValue("ShowDropdownBarOption", showDropdownBarOption);
            key.SetValue("Single Code Window Only", singleCodeWindowOnly);
            key.SetValue("EnableAdvancedMembersOption", enableAdvMembersOption);
            key.SetValue("Support CF_HTML", supportCF_HTML);
            key.Close();

            // Register File extensions under <VS Reg Root>\Languages\File
Extensions\<file extension>
            char[] delimiters = {' ' , ',' , '.' , ':' , ';'};
            string[] extensions = fileextensions.Split(delimiters,10);
            foreach (string s in extensions)
            {
                regKeyName = string.Format("Languages\\File Extensions\\.{0}", s);
                key = context.CreateKey(regKeyName);
                key.SetValue("", guidLangService);
                key.Close();
            }

            // Register language service under <VS Reg Root>\Services<langsvc guid>
            regKeyName = string.Format("Services\\{0}", guidLangService);
            key = context.CreateKey(regKeyName);
            key.SetValue("", guidPackage);
            key.SetValue("Name", langName);
            key.Close();
        }

        public override void Unregister(RegistrationContext context)
        {
            // Remove <VS Reg Root>\Languages\Language Services\<langName> key
            string regKeyName = string.Format("Languages\\Language Services\\{0}",
langName);
            context.RemoveKey(regKeyName);

            // Remove file extensions <VS Reg Root>\Languages\File Extensions\<file
extension>
            char[] delimiters = {' ' , ',' , '.' , ':' , ';'};
            string[] extensions = fileextensions.Split(delimiters,10);
            foreach (string s in extensions)
            {
                regKeyName = string.Format("Languages\\File Extensions\\.{0}", s);
                context.RemoveKey(regKeyName);
            }   

            // Remove entry under <VS Reg Root>\Services<langsvc guid>
            regKeyName = string.Format("Services\\{0}", guidLangService);
            context.RemoveKey(regKeyName);
        }

    }

Hopefully, that'll get you pointed in the right direction.

Sincerely,
Ed Dore [MSFT]

This post is 'AS IS' with no warranties, and confers no rights.
ssyladin - 22 Feb 2005 03:31 GMT
Everything looks good and, with a little renaming I got things compiling and
sort-of running.  

Now, for the big question:   in your IVsPackage::Initialize  method you
create the RGSLanguageService class with "this".  How does one handle that in
RGSLanguageService's constructor?  To what end?

The big "sort-of" comment comes from VS initializing my package, then
calling "GetColorizer" (returns null/0) and "GetCodeWindowManager" (same
deal) but it gives me a nasty "The instruction at 0x... referenced memory at
0x0000000.  The memory could not be read".

Now, looking at the VSIP docs, specifically "Checklist: Creating a Language
Service", it says I need to do 3 things:
1) Implement IVsPackage (done) and implement IServiceProvider (done as part
of IVsPackage)
2) Implement IVsLanguageInfo (done with stub functions)
3) Impelment your language (whoo hoo!)

Trying to figure out what was going wrong, I went back to my version and
realized two things.  The first was that I wasn't populating an entry in
Services (not specifically mentioned in "Language Service Registry
Information", but still common sense).  The second was that I had created an
editor/code window via the IVsEditorFactory::CreateEditorInstance method.  A
small spark of intuition later and the example you provided worked well, once
I created a class that inherited from IVsCodeWindowManager and gave it empty
functions.  

Not to sound nitpicky, but hopefully to save others a couple of hours of
headaches in the future, what specifically do we need to make sure to
implement to ensure our language is loaded and used in a code window
properly?  Is the IVsEditorFactory a better way to go about things, or an
"empty" implementation of IVsCodeWindowManager?

That aside, all is well in Language-ville and I'm chugging on implementing
my IVsColorizer and staying sane.

Thanks a ton for the example Ed!!!

- Johann

> I did this a while back with the intent to build a language service for
> .RGS files (to colorize them), but never did finish it up. I did get it
[quoted text clipped - 297 lines]
>
> This post is 'AS IS' with no warranties, and confers no rights.
"Ed Dore [MSFT]" - 24 Feb 2005 20:01 GMT
Hi Johann,

I'm not sure why I wanted to pass the package object to the
RGSLanguageService class. I suspect I initially though I'd need a reference
back to the package object for some reason. Never did use it though :-)

With regards to proffering a service, there will be some walkthrough info
with the Whidbey VSIP docs. I ran into the same problem the first time I
tried to proffer a service. I couldn't figure out how the IDE could
possibly know about the service (or the package that implemented it).
Eventually, I found the registry info. I think Arron Marten blogged this
topic a while back to :
http://blogs.msdn.com/aaronmar/archive/2004/03/12/88646.aspx.

I'm not sure if a custom editor factory is a necessity, I'd have to go back
and run some experiments to see there was a way to associate a particular
file extension with the default text editor, and if it was smart enough to
find and use the language service. I suspect not, but I haven't tried to
confirm it.

Below is the CreateEditorInstance from that same sample. Note, I create a
VsTextBuffer and a VsCodeWindow COM object via ILoadRegistry. I just
noticed that I don't make a call to IVsTextBuffer::SetLanguageServiceID, so
I'm thinking that the IDE does indeed know how to load the language service
based on the file extension. Interesting....

       public int CreateEditorInstance(uint grfCreateDoc, string
pszMkDocument,string pszPhysicalView,
            IVsHierarchy pvHier, uint itemid, System.IntPtr punkDocDataExisting, out
System.IntPtr ppunkDocView,
            out System.IntPtr ppunkDocData, out string pbstrEditorCaption, out Guid
pguidCmdUI, out int pgrfCDW)
       {
           Trace.WriteLine(string.Format("Entering {0}
CreateEditorInstance()", this.ToString()));

           // Initialize to null
           ppunkDocView = new System.IntPtr ();
           ppunkDocData = new System.IntPtr ();
           pguidCmdUI = new Guid ();
           pgrfCDW = 0;
           pbstrEditorCaption = null;

           // Validate inputs
           if ((grfCreateDoc & (NativeMethods.CEF_OPENFILE |
NativeMethods.CEF_SILENT)) == 0)
           {
               return NativeMethods.E_INVALIDARG;
           }

           if (punkDocDataExisting != IntPtr.Zero)
           {
               return NativeMethods.VS_E_INCOMPATIBLEDOCDATA;
           }

            // Use ILocalRegistry to create text buffer and code window
            ServiceProvider sp = new ServiceProvider(vsServiceProvider);
            ILocalRegistry localRegistry =
(ILocalRegistry)sp.GetService(typeof(ILocalRegistry));
            Debug.Assert(localRegistry != null, "Unable to retrieve ILocalRegistry");
   
            // Create the document (text buffer)
            IntPtr vsTextLines = IntPtr.Zero;
            Guid guidTextLines = typeof(IVsTextLines).GUID;
            int hr = localRegistry.CreateInstance(typeof(VsTextBufferClass).GUID,
null, ref guidTextLines, NativeMethods.CLSCTX_INPROC_SERVER, out
vsTextLines);
            Debug.Assert(hr == 0, "Failed to create instance of TextBuffer");
            IVsTextLines textLines = Marshal.GetObjectForIUnknown(vsTextLines) as
IVsTextLines;

            // Site it, so it can find/query for various services
            IObjectWithSite ows = (IObjectWithSite)textLines;
            ows.SetSite(vsServiceProvider);                   

            // Create the codewindow (editor)
            IntPtr vsCodeWindow = IntPtr.Zero;
            Guid guidCodeWindow = typeof(IVsCodeWindow).GUID;
            hr = localRegistry.CreateInstance(typeof(VsCodeWindowClass).GUID, null,
ref guidCodeWindow, NativeMethods.CLSCTX_INPROC_SERVER, out vsCodeWindow);
            Debug.Assert(hr == 0, "Failed to create instance of CodeWindow");
            IVsCodeWindow codeWindow = Marshal.GetObjectForIUnknown(vsCodeWindow) as
IVsCodeWindow;

            // Attach buffer to code window
            codeWindow.SetBuffer(textLines);       

            // use the CMDUIGUID_TextEditor guid to enable generic text editor
keybindings
            pguidCmdUI = new Guid("8B382828-6202-11d1-8870-0000F87579D2");
 
            // pass back both the editor and document
           ppunkDocView = vsCodeWindow;
           ppunkDocData = vsTextLines;
           pbstrEditorCaption = "";

           return NativeMethods.S_OK;
       }

Ed Dore [MSFT]

This post is 'AS IS' with no warranties, and confers no rights.

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.