Visual Studio Editor Choosing System

This document gives an overview of how the Visual Studio editor choosing system works, and as an example discusses the XML Editor’s choosing system.  Visual Studio has the ability to associate multiple editors with a single a file extension.  For instance, .xaml files have multiple editor implementations associated with them. This raises the question of how Visual Studio chooses a specific editor implementation when asked to open a file. his document gives an overview of how the Visual Studio editor choosing system works.  Below, we discuss how Visual Studio decides which editor to open for a file extension; we review the IVsEditorFactoryChooser interface that further advances the choosing mechanism, and the Visual Studio XML Editor’s implementation of this interface: XmlChooser.

Definitions

For clarity, in the discussion below, it helps if we define a few terms.

Physical View: A Visual Studio editor supports different types of windows for editing documents, each known as a Physical View. One example of a physical view is a code editor window, another is a forms designer.

Logical View: Each physical view could be enumerated further to have more classification. A Logical View can have code, design, debug, and text views, and the editor could support these views with different physical views. For instance the WPF editor supports both WPF Designer and the Code view, the design and code logical views of this editor. (more information available at: https://bit.ly/cE1EUP)

Visual Studio Registry Hive: Visual Studio keeps most of the settings under registry.The root of the VS hive for 32 bit systems is HKLM\SOFTWARE\Microsoft\VisualStudio\10.0. {VSRoot} will be used in this document to reference this hive.

Visual Studio Editors

An editor needs to get elements and features of a language.  One example is formatting information.  A Language Service is a Visual Studio service that provides this information for a specific language. A language service is needed A language service can provide information to multiple editors. For more information on Language Services you may refer to https://bit.ly/bisnEE.

Editor Choosing when Opening File

The editor choosing on Visual Studio is done by file extensions. If you have a new file extension that you want to open in a language service, you’ll need to create a new entry under {VSRoot}\Editors. This entry consists of properties such as the display name and the package GUID. Underneath this entry there are following keys:

  • {VSRoot}\Editors\{Your Editor GUID}\LogicalViews
  • {VSRoot}\Editors\{Your Editor GUID}\Extensions

The {VSRoot}\Editors\{GUID}\LogicalViews contains logical views that are represented by a GUID:

7651A700-06E5-11D1-8EBD-00A0C90F26EA

Debugging: IDE is in debugging mode.

7651A701-06E5-11D1-8EBD-00A0C90F26EA

Code: The file is opened in code view.

7651A702-06E5-11D1-8EBD-00A0C90F26EA

Designer: The file is opened under the design view

7651A703-06E5-11D1-8EBD-00A0C90F26EA

Text: Called when user clicks an Error item at Error page.

These logical views represent which views this editor supports. Visual Studio queries the registered editors when there is a need to change the logical view. The query happens when user starts debugging, stops debugging, clicks on Design View at the View menu, or clicks on an error at the error list. If a document is already open within one view, Visual Studio could still query the editor for the same logical view.

The {VSRoot}\Editors\{Your Editor GUID}\Extensions registry key holds a list of file extensions and DWORD values to prioritize the editor against the other editors in Visual Studio for that specific file extension. A specific editor is chosen to open a document when it has the highest priority among the editors that are both registered for the document’s file extension and registered to support the desired logical view. However, if the editor rejects the request, then Visual Studio will query the proceeding prior editor until an editor is found. For instance, in Visual Studio 2010, the xml extension is mapped to XmlEditor under {VSRoot}\Editors\{fa3cd31e-987b-443a-9b81-186104e8dac1} with priority of 42. XmlEditor is the second at the priority list therefore would be queried if the most prior editor rejects the document open query.

To sum up this section, Visual Studio have file extension based logic that assigns a file extension to an editor. This is done through {VSRoot}\Editors for prioritizing each editor with a file extension. Each editor’s file extension has a priority and the editor with highest priority extension will be called first to open the file.

The need for XmlChooser

In most cases, the file extensions uniquely define what the file contents are. In XML world however, file extensions are less important. For example, the .xaml is used for Silverlight, Workflows, WPF … and each of these define a different editor. By just looking the extension we cannot decide which editor this file belongs to.

The major identifiers used in XML are the XML namespaces, especially, the root namespace. Thus, the real choosing logic should be based on the XML root namespace.

The IVsEditorFactoryChooser

In Visual Studio 10.0, the IVsEditorFactoryChooser interface is added to enable registered editors to implement an editor choosing logic so that these editors are no longer a real Visual Studio Editor, but rather they are editor choosers. If these Editor Choosers are registered for the specific file extension, VisualStudio will query for this extension, and if implemented Visual Studio will forward the result of the choosing to the appropriate editor. An editor chooser that implements IVsEditorFactoryChooser, can either return another editor, or just returns “cannot open this file” to force VS to try other editors in the priority list. MSDN article of IVsEditorFactory chooser can be found here https://bit.ly/c8Op5w.

XmlChooser is the implementation of IVsEditorFactoryChooser. XmlChooser is designed to handle open file requests for XML. XmlChooser is registered in the Visual Studio Registry Hive as an editor with a priority of 0x21 (43 decimal).  Since this is the highest priority “Editor”, the XmlChooser will have priority over any other XML based editor registration.

Registering for XmlChooser

If you have a file type that’s based on XML which has following properties:

  • has a common file extension and
  • the identifier for that file extension is the root namespace,

And if you want to register a new editor for this file then you should register the file extension not just only through the editors/projects registry hives that’s mentioned above, but also through XmlChooserFactory hive.

If your editor’s file extension is unique, then there is no need to be part of XmlChooser.

Pre-requisites

We are assuming you already have this editor working with Visual Studio. This means that you should have registered your editor at {VSRoot}\Editors\{EditorGuid}, including specifying the the extension you want to register, and a priority value for your editor a DWORD value higher than 0x21 at {VSRoot}\ Editors\{YourEditorGuid}\Extensions. (The reason why you would want to have a priority value higher than 0x21 is that XmlChooser is registered for all extensions at 0x21, if you have a value lower than 0x21 and if your document is an XML based file, then XMLEditor will open the document.)

At this state, your editor should be functioning without interference of XmlChooser.

Registry changes to register to the XmlChooser

We will need to update two registry locations to have Visual Studio to point to XmlChooser for your editor with your file extension.

  1. Update Projects hive to change the EditorFactoryNotify
  2. Add your file extension to the Editors\XmlChooser’s hive

1. Update Projects hive to change the EditorFactoryNotify

Each Project in Visual Studio keeps a list of file extensions and Editor GUIDs to associate an extension with an Editor. In order to associate a file extension to an editor in Visual Studio, you need to add/modify each project’s file extensions registry entry. To associate your file extension with XmlChooser the following update to the registry is required

{VSRoot}\Projects\{ProjectGuid}\FileExtensions\ {.YourFileExtension} should contain EditorFactoryNotify key that should point to XmlChooser’s GUID {32CC8DFA-2D70-49b2-94CD-22D57349B778}

This will force the project system to point to XmlChooser when the file needs to be opened.

2. Add your file extension to the Editors\XmlChooser’s hive

XmlChooser at Visual Studio Editors registry hive contains the list of extensions, with priorities. If your file extension is already registered with other editors, you should add your file extension to the XmlChooser’s Editors hive with the highest priority. As XmlChooser’s guid is 32CC8DFA-2D70-49b2-94CD-22D57349B778, the following location needs to be updated:

{VSRoot}\Editors\{32CC8DFA-2D70-49b2-94CD-22D57349B778}\Extensions should contain yourFileExtension with DWORD priority value. As we mentioned above, the value of this key must be higher than the value at {VSRoot}\Editors\{YourEditorGuid}\Extensions at Pre-requisites section.

In case that you are adding an extension that’s already being handled by XmlChooser such as .xaml, your editor’s file extension priority that we mentioned at Pre-requisites section must be lower than XmlChooser to allow XmlChooser to take priority over your editor.

XmlChooserFactory

The functionality of hierarchical choosing at XmlChooserFactory is handled through the registry. The following tree structure handles the association between registered file extensions, logical views, and the editors.

{VSRoot}

XmlChooserFactory

{XmlEditor related settings}

FileTypeA

{FileTypeA related settings}

FileTypeB

{FileTypeB related settings}

FileTypeC

{FileTypeC related settings that’s more specific than FileTypeB}

FileTypeD

{FileTypeD related settings}

This hive is at {VSRoot}\XmlChooserFactory typical structure is as follows:

The keys at the root node XmlChooserFactory is for settings for XmlEditor itself. Users of this hive are not expected to change this node.

XmlChooserFactory have child nodes (FileTypeA, FileTypeB… in the example above). Each child node’s name is just a human readable string that has no functional significance. You may pick any unique name for your editor. Each child node can have other child nodes (see the FileTypeC) which define a node that’s more specific (limiting) than the parent node. All settings are inherited to the child node from parent node.

Each child node has the following entries:

  • Extension=REG_SZ:{YourFileExtension}: The file type’s file extension. (Optional)

  • Namespace= REG_SZ:{YourRootNamespace}: The file type’s root namespace. (Optional)

  • Match= REG_SZ:“both”: If this entry is specified there could be a match if both the Extension and Namespace matches if this key does not exist, there is a match if any of the Extension and Namespace matches. (Optional)

  •  IsDataSet=dword:00000001: This is special instruction to sniff DataSet designer’s XSD files as their Extension and Namespace is not unique enough to distinguish them from a normal XSD designer. This can be considered internal. (Optional)

  • {7651A700-06E5-11D1-8EBD-00A0C90F26EA}=REG_SZ:{ARegisteredEditorGuid}

  • {7651A701-06E5-11D1-8EBD-00A0C90F26EA}=REG_SZ:{ARegisteredEditorGuid}

  • {7651A702-06E5-11D1-8EBD-00A0C90F26EA}=REG_SZ:{ARegisteredEditorGuid}

  • {7651A703-06E5-11D1-8EBD-00A0C90F26EA}=REG_SZ:{ARegisteredEditorGuid}:   
    For each logical view that your scenarios support you should associate the logical view with an editor that’s registered at {VSRoot}\Editors hive. If the associated editor GUID is not found on the {VSRoot}\Editors hive, the result will be defaulted to XmlEditor. (Optional)

  • DefaultLogicalView={ALogicalViewGuid}: DefaultLogicalView is taken into consideration when the open file request has Primary Logical View (see https://bit.ly/cE1EUP). ALogicalViewGuid should be replaced with one of the following values: {7651A700-06E5-11D1-8EBD-00A0C90F26EA}, {7651A701-06E5-11D1-8EBD-00A0C90F26EA}, {7651A702-06E5-11D1-8EBD-00A0C90F26EA}, {7651A703-06E5-11D1-8EBD-00A0C90F26EA}

  • XmlEditorBlockSxS={ARegisteredEditorGuid}: If you want your editor not to be side by side with XmlEditor. (Optional)

    You may want to set this key if your editor does not support side by side editing with XmlEditor. If this key is set, when the user tries to open a document in XmlEditor that’s already open by the editor that’s specified by ARegisteredEditorGuid, XmlEditor will ask the user to close that editor.

    If this is the only functionality is needed, you may also create a node under XmlChooserFactory, with just this key.

As noted above, each node has definitions to create the relation between a file extension, a logical view, and an editor. A relation can be specified if a file type fits into one or many of the following:

  • have a namespace that’s unique to the file type,
  • have an extension that’s unique to the file type, 
  • both of extension and namespace could uniquely identify the file type

Extension, Namespace, and Match values will be used to test if a candidate file type is a match. Once there is a match, the logical views – editors associations and the DefaultLogicalView key is used to resolve the editor to be opened.

Example

An example for XmlChooserFactory are as follows:

/* The following blocks Editor with Guid CCCCCCCC-DDDD-EEEE-FFFF-BBBBBBBBBBBB to be side by side with Xml Editor. */

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\XmlChooserFactory\EditorOne]
"XmlEditorBlockSxS"="{CCCCCCCC-DDDD-EEEE-FFFF-BBBBBBBBBBBB}"

/*a file with xaml extension AND https://schemas.microsoft.com/example/2009 namespace should be opened in XmlEditor (FA3CD31E-987B-443A-9B81-186104E8DAC1) for debugging and code logical views, The editor with GUID CCCCCCCC-DDDD-EEEE-FFFF-CCCCCCCCCCCC for Designer, and the editor with guid CCCCCCCC-DDDD-EEEE-FFFF-DDDDDDDDDDDD for text view (actually text view generally is equal to the code view). */

 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\XmlChooserFactory\MyEditor]
"Extension"="xaml"
"Match"="both"
"Namespace"=https://schemas.microsoft.com/example/2009
"DefaultLogicalView"="{7651A702-06E5-11D1-8EBD-00A0C90F26EA}"
"{7651A700-06E5-11D1-8EBD-00A0C90F26EA}"="{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
"{7651A701-06E5-11D1-8EBD-00A0C90F26EA}"="{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
"{7651A702-06E5-11D1-8EBD-00A0C90F26EA}"="{CCCCCCCC-DDDD-EEEE-FFFF-CCCCCCCCCCCC}"
"{7651A703-06E5-11D1-8EBD-00A0C90F26EA}"="{CCCCCCCC-DDDD-EEEE-FFFF-DDDDDDDDDDDD}"

Conclusion

When a file with a unique extension is bound to an editor with its supported logical views, the editor registration is rather straight forward. However when there are overloads to files things could get complex pretty rapidly. We hope this document helped you understand the issues and solutions.

Visual Studio handles the problem of which editor to choose through the {VSRoot}\Editors and {VSRoot}\Projects hives. Each project registers extensions, and each editors’ prioritized file extensions define the order that Visual Studio tries to open a file with that editor. It’s up to the editor to reject the file, and when that happens, next registered editor is tested to open the file.

However this mechanism is not enough when there are many editors defined for same file extension.   To solve this problem, the IVsEditorFactoryChooser interfacemay be implemented.  Implementations of this interface allows programmatic determination of which editor to use. 

In Visual Studio 10.0 we introduced XmlChooser that implements IVsEditorFactoryChooser. We created a hierarchy in registry so that our customers/partners who have XML based files could hook up their editors. XmlChooser takes into consideration the file’s extension and root namespace of the document.  Additionally, it allows logical view-editor pairings and default logical views to be defined.

The XmlChooser also helps to define blocking side by side experience with other editors, where no other editor is allowed to have the file open when XML Editor opens the file.

Yours truly,

Sinan Usakli
Software Designer Engineer
Data Modeling Group
Microsoft Corp