Supporting user-specified file extensions in custom Visual Studio language service - visual-studio

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 of IVsEditorFactory. The class should have a protected constructor with a bool argument to specify whether the factory should prompt the user for an encoding (similar to one of the constructors of the DjangoEditorFactory).
You have a class ExampleEditorFactoryWithoutEncoding which extends ExampleEditorFactory and constructs the base class specifying false for the promptForEncoding argument. This class should be marked with the [Guid] attribute.
You have a class ExampleEditorFactoryWithEncoding which extends ExampleEditorFactory and constructs the base class specifying true for the promptForEncoding argument. This class should be marked with the [Guid] attribute.
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 the LinkedEditorGUID value (search down the page). This value would normally be added to the registration of ExampleEditorFactoryWithEncoding, and the value would be the GUID of ExampleEditorFactoryWithoutEncoding.

Related

How can I organize and load packages based configurable properties?

The system I'm working on can be configurated with different boolean properties. The theorical maximum of different configurations is 2^n where n is the number of such properties.
There is a configuration tool separated from the production code, which uses boolean expressions to select which code packages are loaded.
This tool contains a class that defines methods like isWithX or more complex ones like isWithXwithoutYwithZ.
A package is basically a list of class extensions that define or redefine some method definitions.
Today the packages are named like myPackageWithAWithoutBwithCwithDwithoutE.
Since there is an evolving number of boolean configuration properties, the number of different packages and the size of their names is getting ridiculous and we can't see their names without scrolling all the time.
There is also a lot of code duplication.
EDIT: since production code has no access to the configurated properties the question is now limited to those issues:
list of package names for each package and each functionality
how many methods and how to name them
Right now the list of package names is basically all different combinations of names like this one: myPackageWithAWithoutBwithCwithDwithoutE except those where there is no behaviour specific to that configuration that we need to implement.
Right now for each package and each functionality: 1 method per package with the name of the functionality.
I cannot tell you how to solve your specific problem, so I will share with you some hints that might help you get started in finding the design that will actually work for you.
Consider a hierarchy of configuration objects:
ConfigurationSettings
ApplicationSettings
UserSettings
DisplaySettings
...
The abstract class ConfigurationSettings provides basic services to read/write settings that belong to any specific section. The hierarchy allows for simpler naming conventions because selectors can be reimplemented in different subclasses.
The ApplicationSettings subclass plays a different role: it registers all the sections in its registry instance variable, a Dictionary where the keys are section names and the values the corresponding subclass instances:
ApplicationSettings current register: anEmailSettings under: `Email`
The abstract class provides the basic services for reading and writing settings:
settingsFor: section
settingAt: key for: section
settingAt: key for: section put: value
Subclasses use these services to access individual settings and implement the logic required by the client to test whether the current configuration is, has, supports, etc., a particular feature or combination. These more specific methods are implemented in terms of the more basic settingAt:for:.
When a new package registers its own subclass its testing methods become available as follows:
self <section>Settings isThisButNotThat
where, for instance,
emailSettings
^(ApplicationSettings current for: 'Email') isThisButNotThat
and similarly for any other section. As I mentioned above, the division in subclasses will allow you to have simpler selectors that implicitly refer to the section (#isThisButNotThat instead of #isEmailThisButNotThat).
One other feature that is important to support Apply/Cancel dialogs for the user to modify settings is provided by two methods:
ConfigurationSettings >> #readFrom:
and
ConfigurationSettings >> #writeOn:
So, when you open the GUI that displays settings you don't open it on the current instance but on a copy of them
settings := ApplicationSettings new readFrom: ApplicationSettings current.
Then you present in the GUI this copy to the user. If the user cancels the dialog, you simply forget the copy. Otherwise you apply the changes this way:
settings writeOn: ApplicationSettings current
The implementation of these two services follows a simple pattern:
ApplicationSettings >> readFrom: anApplicationSettings
registry keysAndValuesDo: [:area :settings | | section |
section := anApplicationSettings for: area.
settings readFrom: section]
and
ApplicationSettings >> writeOn: anApplicationSettings
registry keysAndValuesDo: [:area :settings | | settings |
section := anApplicationSettings for: area.
settings writeOn: section]
I don't fully understand all of the aspects of your problem but maybe you could use a dynamic approach. For example, you could override #doesNotUnderstand: to parse the selector sent to the configuration and extract the package names:
doesNotUnderstand: aMessage
| stream included excluded |
"parse selectors like #isWithXwithoutYwithoutZ"
stream := (#isWithXwithoutYwithoutZ allButFirst: 6) asLowercase readStream.
included := Set new.
excluded := Set new.
[ stream atEnd ] whileFalse: [
(stream peek: 3) = 'out'
ifTrue: [
stream next: 3.
excluded add: (stream upToAll: 'with') ]
ifFalse: [ included add: (stream upToAll: 'with') ] ].
Then, all you need is a bit more code to generate the list of packages from that (I hope).

How to use IProjectionBuffer in MEF without creating editor instance?

I am trying to create a Visual Studio extension which handles a multi-language content type. Much like some mvc-templates and Django or ASP.NET which a certain part of the code is in another language.
I know that I should use Projection and I already checked Django for Visual Studio extension but the solution there includes creating a Language Service and going to the trouble of creating all bits and pieces related to a code editor. Here I am trying to achieve the same goal just by using MEF.
I know that you can use IProjectionBuffer CreateProjectionBuffer() to create a projection but the question is how to replace the current TextBuffer with the created one and when is the best time to do it.
Also one may expect that if he specifies a base definition of type "projection" like this:
[Export]
[Name("Whatever")]
[BaseDefinition("code")]
[BaseDefinition("projection")]
internal static ContentTypeDefinition WhateverContentType = null;
the received TextBuffer in providers to be of type IProjectionBuffer (after all IProjectionBuffer is inherited from ITextBuffer). Which are not and it seems that a projection base definition has no effect what so ever.
So if I want to rephrase my question in a tldr version:
How and when do you apply an IProjectionBuffer to current view (using MEF and without creating an editor instance)?
So if I understand your question correctly, the answer is "you don't." A IWpfTextView is bound to a implementation of ITextBuffer (or a derived type, like IProjectionBuffer) at creation time, and can't be changed. Even if you could, many other extensions and language services would be most surprised by this and would probably crash.
To address your second question about content types: simply declaring you have a base content type of "projection" doesn't make you a projection buffer. All that really states is you might be creating projection buffers of that type, and when you do you want some extra ITagger support so taggers project through to the source buffers as you might expect them to.

Entity Framework: Why do the templates no longer generate ".designer.cs" source files

The default type generation in Entity Framework 3.5/4.0 emits each of the types in "Xxxx.designer.cs" source files (where "Xxxx" is the name of the type being generated). This then allowed for the developer to generate his own "Xxxx.cs" source files where further implementation could take place, or attributes could be added to Metadata classes etc.
The more recent T4 templates that have been made available (POCO, Self Tracking etc.), including the default type generation in VS2012, all genrate "Xxxx.cs" themselves without the ".designer" suffix.
I know that it is easy enough to override the behaviour of the T4 templates to emit the ".designer" within the file names, but am I missing something here where we are actually being discouraged to extend these classes through the partial definitions?
In a recent project where I generated Self Tracking Entities, I found that I had to rename the emitted files so that I could extend the definitions with Metadata class attributes as well as overrides of .Equals(object) and .GetHashcode() (otherwise EF could not detect duplicates properly) for example.
Is there something that I have missed where we are now expected to leave the emitted partial classes as they are or is this merely an oversight and are most of you also having to rename source files being emitted?
Code generation strategy has changed in VS2012. Before the code that was generated was using EntityObject based entities and a context class derived from the ObjectContext class. VS2012 by default generates POCO entities and DbContext dervied context. If you still want ObjectContext based context and EntityObject based entities you need to:
Right-click on the designer surface and select properties
Change the "Code Generation Strategy" from "None" to "Default"
Remove .tt files from your solution to avoid having both contexts and duplicate entities.

Get the call tree for a form

I have a VB project which has set of forms. There are number of class modules/common modules files inside that project.
I want to generate the tree hierarchy for each form.
Assume there are 10 forms f1, f2 .... f10. Form f1 is having fun1, fun2... fun5 functions.
I want to see the tree hiearchy at form level. This means what all functions are being called from each form.
There is no built-in function like that in VB6 IDE.
You need a third-party tool like http://www.aivosto.com/project/project.html to analyze your code.
I've used the Project Analyzer before and it's quite good. The standard edition should be good enough for your needs.
If you have access to the VB6 IDE then I believe you can use the Object Browser window to do what you want. You can select a form (or class or module) in the project and it will show the contained functions.

Pattern for changing program options in a GUI

I'm adding a GUI to an existing command line app. The properties used by the app are held in class(es) and I'm creating a dialog that binds to those options objects. However, if I want to cancel out of that dialog I then have reset the values of my options objects, which is where I'm running into probs.
I could take an internal copy of the option objects and use that to re-populate the original object to allow the cancel/rollback but that seems cumbersome.
I can (somehow) implement an undo function on each class - is there a pattern for that?
I use the GUI controls standalone to hold values and only update the options objects when the dialog has been confirmed.
What's best practise?
You should consider creating a new class to be used just by the GUI.
GUIs have their own needs.
Make sure to take care of multi thread issues if you have more than one thread accessing the options object.
The design patterns that address undo functionality are called Command and Memento.
I think Memento would fit better on this case.
Take a look on this question on SO: Design Pattern for Undo Engine.
The following links are of interest (and many more):
http://www.coderanch.com/t/100676/patterns/Memento-Vs-Command-pattern
http://www.developer.com/design/article.php/3720566/Working-With-Design-Patterns-Memento.htm
http://www.colourcoding.net/blog/archive/2009/07/23/reversibility-patterns-memento-and-command.aspx

Resources