New extension: CSS Is Less

While browsing StackOverflow for questions about VS2010, I came upon a question about how to make .less files open in the CSS editor. Coincidentally, I had written up an extension to do this in response to a tweet from about a month ago. Since the popularity of this has doubled (from one to two people), I went ahead and stuck this up on the Visual Studio Gallery.

There was a similar question to this posted on the editor forums on msdn a week or so ago. Since three is a crowd, I figured I'd throw together a blog article about how this extension works, in case anyone else finds themselves in need of something similar.

Modifying the registry

At it's core, this extension is extremely simple; it's a pkgdef file that looks like this (for the Less case):

 [$RootKey$\Languages\File Extensions\.less]
@="{A764E898-518D-11d2-9A89-00C04F79EFC3}"

[$RootKey$\Editors\{A764E89A-518D-11d2-9A89-00C04F79EFC3}\Extensions]
"less"=dword:00000028

This creates two registry entries:

  1. The File Extensions entry associates the given extension with the given language service. This is how the editor figures out what ContentType to use for the given file extension, which controls what MEF extensions are loaded for that content type, and also sets the language service GUID on the IVsTextBuffer the editor uses.
  2. The other entry associates the given file extension with the CSS language service's editor factory. This is used by the shell to pick which editor factory is used to create the buffer and view for the given editor instance. Technically speaking, most editor factories create the same buffer/view as every other (the adapters over the new editor), but these editor factories may also do other setup that their language services require to work fully.

The pkgdef file lets us do this as an extension and without worrying about the registry root, courtesy of the $RootKey$ variable.

The only other piece of information there is the dword value, which is just the priority of that file extension to editor factory mapping. It doesn't really matter for a unique file extension, and I'm not sure why I picked the 00000028 value; it may have just been the value of a different file extension mapping for the CSS language service.

The extension

Making the extension was also pretty simple:

  1. Install the SDK, if you haven't already.
  2. Create whatever type of VSIX project you want. If you are going to be also adding editor extensions, you can start with one of those projects. If not, the "VSIX Project" will do (under Visual C#->Extensibility in the New Project dialog).
  3. Create a new pkgdef file (by creating a text file and changing the name).
  4. In the properties page for that file, make sure "Copy to VSIX" is set to true.
  5. Set the pkgdef file's contents to what I pasted above.
  6. If you care, you can also make sure the (empty) binary you build isn't stuck in the VSIX by adding the following to a <PropertyGroup> in the .csproj file:
 <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>

...and that's it. If you build and run or debug that project, it'll open an instance of the experimental hive, which should have that extension loaded. Because the pkgdef file automatically figures out the correct registry root to go with the registry hive that instance of Visual Studio is using, only the experimental hive will have the new file extension mapping. If you want your regular VS to have it as well, just find the VSIX in the bin\Debug or bin\Release folder the project lives in and double-click it from explorer (that will install it in VS).

If you want to apply this solution to a different language service or file extension, you need to:

  • File extension – change all the instances of less to whatever you want.
  • Language service – first, find the language service you want. You can search the registry for it by looking under HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0_Config\Languages\Language Services. The editor factories for a given language service generally have the same GUID as the language service itself, though I'm not sure if that is a requirement. In any case, the editor factories are under HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0_Config\Editors, so you can check to be sure.

You can also add this to any existing extension, by just repeating steps #3-#5 above. If you already have a pkgdef file, you just need to add those two declarations to the existing file.

Comments

  • Anonymous
    March 01, 2010
    Noah -- I am writing an editor extension that I want to activate for specific XML namespaces, and for specific file extensions.  (I want to completely replace the built in XML functionality with my own.)  Is this the secret sauce that I need to make it work for me?  I have followed the pattern you used in ContentTypes.cs from your MarkDown project, but as soon as the VS Editor sees XML in my file, it takes over with it's own XML editor, and my extensions are ignored.  Now I am trying to find the GUID for my extension, but can't see it assigned anywhere... I couldn't find one in your Markdown project either. A partial answer to my question on the MSDN forms provides a reference to ProvideXmlEditorChooserDesignerViewAttribute, but unfortunately there are no examples of usage. Maybe I should get one of my friends to post this question on StackOverflow to get the critical mass of 2 people to justify a blog entry on this topic?? :) Thanks in advance for any help. ~ Cameron

  • Anonymous
    March 03, 2010
    Hah, no need for the extra mass :)  I'll try to answer as best I can. The problem with the "sees XML in my file" isn't the VS editor, strictly speaking; it's the XML editor factory.  From my understanding, it looks at the file to see what attributes are in it, and uses the XML language service if it finds XML tags. There is a way for different languages services to play together for XML, but I don't know exactly how it is done.  I can find that out, though. Can you send me an email (noahric at ms) to continue following up on this? -Noah