I'm working on a custom Visual Studio language service, and have several questions regarding the way file extensions are bound to a particular language service.
Source files for the language "Example Language" has two primary file extensions: .e1
and .e2
. My extension has a class ExampleLanguagePackage
which extends Package
.
When you use the File → Open command and select a C# file (for example), the "Open" button has a dropdown arrow which allows you to select "Open With...". When you click that button, you are presented with options to open the file in the "CSharp Editor (Default)", "CSharp Editor with Encoding", or any of several other options. How can I provide a similar feature for my language, offering "Example Language (Default)" and "Example Language with Encoding" options?
When you open Tools → Options... → Text Editor → File Extension, you have the ability to bind (for example) the
.foo
extension to "Microsoft Visual C#" or any of several other options. How can I extend this page to allow user-defined file extensions to be associated with the "Example Language"?What else should I watch out for when registering these items?
Most of these items are addressed by adding a custom implementation of IVsEditorFactory
for your language and using a combination of registration attributes to register it. The actual implementation of this interface is beyond the scope of this question, but the documentation for the interface itself (and linked to that page), along with an example DjangoEditorFactory
implementation in the Python Tools for Visual Studio project helped me with my initial implementation.
To support the Example language, I will make the following assumptions.
- You've implemented an abstract class
ExampleEditorFactory
which provides the core implementation ofIVsEditorFactory
. The class should have a protected constructor with abool
argument to specify whether the factory should prompt the user for an encoding (similar to one of the constructors of theDjangoEditorFactory
).- You have a class
ExampleEditorFactoryWithoutEncoding
which extendsExampleEditorFactory
and constructs the base class specifyingfalse
for thepromptForEncoding
argument. This class should be marked with the[Guid]
attribute. - You have a class
ExampleEditorFactoryWithEncoding
which extendsExampleEditorFactory
and constructs the base class specifyingtrue
for thepromptForEncoding
argument. This class should be marked with the[Guid]
attribute.
- You have a class
- You have added the following entries to your VSPackage.resx resources file. The constants can be changed, but be aware that I have used the constant values 101 and 102 below.
101 = Example Language
102 = Example Language with Encoding
Registering the editor factories
The first thing to do is register your editor factories. This is done in two parts.
First, use the ProvideEditorFactoryAttribute
. This attribute associates a resource identifier for the display name of the factory with the factory type itself.
[ProvideEditorFactory(typeof(ExampleEditorFactoryWithoutEncoding), 101)]
[ProvideEditorFactory(typeof(ExampleEditorFactoryWithEncoding), 102)]
Next, in the Initialize
method of ExampleLanguagePackage
, add calls to RegisterEditorFactory
after you call base.Initialize()
.
protected override void Initialize()
{
base.Initialize();
RegisterEditorFactory(new ExampleEditorFactoryWithoutEncoding(this));
RegisterEditorFactory(new ExampleEditorFactoryWithEncoding(this));
}
Associate a logical view with the editor factories
I haven't found all the information I wanted about the use cases for the ProvideEditorLogicalViewAttribute
attribute, but it's important to include at least the following. Make sure to register the logical view(s) with both factories you created.
[ProvideEditorLogicalView(typeof(ExampleEditorFactoryWithoutEncoding), VSConstants.LOGVIEWID.TextView_string)]
[ProvideEditorLogicalView(typeof(ExampleEditorFactoryWithEncoding), VSConstants.LOGVIEWID.TextView_string)]
If this step is not done, the feature where double clicking in the output window can take you to a line of code will not work as expected. For example, suppose the output window contained a line like the following.
c:\dev\file.e1(14,3): unexpected expression
Associating the TextView logical view allows the IDE to use your factory when you double click on this output line to take you to line 14, column 3 of the file c:\dev\file.e1. Otherwise it will use a different factory to open a new copy of your document, and the new window will likely be missing many features.
Associate the standard file extensions .e1
and .e2
with the editor factories
This step provides the "Open With..." support for .e1 and .e2 files described in the original question 1. This step is accomplished with the ProvideEditorExtensionAttribute
attribute.
The default priority for the primary factory appears to be 50. The factory with explicit encoding should have a priority less than this, and 49 appears to be a good choice. Note that there is no need to specify the NameResourceID
named parameter because it was already specified by the ProvideEditorFactoryAttribute
usage above (the generated registry keys are identical).
[ProvideEditorExtension(typeof(ExampleEditorFactoryWithoutEncoding), ".e1", 50)]
[ProvideEditorExtension(typeof(ExampleEditorFactoryWithoutEncoding), ".e2", 50)]
[ProvideEditorExtension(typeof(ExampleEditorFactoryWithEncoding), ".e1", 49)]
[ProvideEditorExtension(typeof(ExampleEditorFactoryWithEncoding), ".e2", 49)]
Associate the .*
extension with the editor factories
This step provides the "Open With..." support for all other files, and adds support for the File Extension options described in the original question 2. This step also uses the ProvideEditorExtensionAttribute
attribute, but uses a much lower priority value to ensure the default editors for other file types are not overridden by the setting. Like in the previous step, the factory with explicit encoding is given a lower priority.
[ProvideEditorExtension(typeof(ExampleEditorFactoryWithoutEncoding), ".*", 2)]
[ProvideEditorExtension(typeof(ExampleEditorFactoryWithEncoding), ".*", 1)]
Final notes
This answer does not cover several details.
- (High importance for Visual Studio 2012) If you are planning to support Visual Studio 2012, please see the topic Cannot get custom editor to use the provisional tab for details on the new (and otherwise poorly documented) property
ProvideEditorFactoryAttribute.CommonPhysicalViewAttributes
. - (Unknown importance) The
ProvideEditorFactoryAttribute
does not support specifying theLinkedEditorGUID
value (search down the page). This value would normally be added to the registration ofExampleEditorFactoryWithEncoding
, and the value would be the GUID ofExampleEditorFactoryWithoutEncoding
.
来源:https://stackoverflow.com/questions/15263761/supporting-user-specified-file-extensions-in-custom-visual-studio-language-servi