Design Details

These are the technical design details of this release, followed by a discussion of the challenges presented by this release and the design decisions for its implementation.

What it contains

This release contains the following components:
  • 2 GAT recipes for configuring and removing the editor from a DSL solution
  • A class library containing several assemblies with base classes and interfaces for the runtime of the editor. References to these assemblies are added to the projects of the DSL solution.
    • DslEditorPowerToy.Common – helpers for Visual Studio automation
    • DslEditorPowerToy.Common.Languages – helpers for DSL integration
    • DslEditorPowerToy.Common.Recipes – base classes and common converters, actions, value providers, and references for recipes.
    • DslEditorPowerToy.Controls.Common – base classes and interface for editor controls
    • DslEditorPowerToy.Controls.Editors – example editors
    • DslEditorPowerToy.Controls.ToolWindows – base classes for the tool-windows
  • A number of Text Templates which are added to the DSL solution and used to generate the DSL specific instances of the editor, tool-window and Visual Studio integration classes.
    • Each of these templates is transformed along with all other text templates provided by the DSL solution.
    • Each template contains partial classes that can be extended for the particular instance of the editor and tool-window
  • Documentation on how to use the PowerToy and how customize the editor (online on this site)

How does it work?

Problem Space

In order to provide the functionality of this release, classes for the tool-window, editor, menus and commands need to be added and registered with the VS package class which the DSL solution uses to integrate with Visual Studio.
These classes, and patterns for their use, were harvested from previous prototype implementations that successfully integrated tool-windows like the ones provided here. These reference implementations were then generalised and templatized. In that generalisation process several common classes were factored out and a class library was created. The remaining code represents the instances of the classes that will be tailored to the specific DSL solution.
Clearly, a means to customize this code for a specific DSL was required, and a means to make that customization easy to configure and add to the DSL solution was sought. Using technologies such as Text Templates and Recipes was suitably applied to this problem.
Text Templates are ideal for generating variable code given some configuration, and recipes are ideal for automating the generation of that code, and applying that generated code to a particular project of a solution. Further recipes are ideal for configuring the project to accept that code.

Binding PowerToy to a DSL Solution

In order to configure a DSL Solution, the first step is to associate the PowerToy Guidance Package (containing the recipes) to a particular DSL Solution. This is done with the ‘Guidance Package Manager’. (see How to Use for details on how to install and configure the PowerToy.
Once a solution containing DSL(s) is associated to the PowerToy recipes, you can execute the recipes on the DSL solutions.

Configuring the Editor

In this release, the PowerToy needs only to add files to the package project of a DSL Solution. Although in future releases, the PowerToy may need to add files, or assembly references etc. to the language project of the DSL solution as well.
Because any VS solution can potentially contain more than one DSL language, and the developer is free to rename the DSL projects, their artefacts and change the structure of the projects freely, there is no reliable means to determine the package project of a DSL solution. DSL projects are currently not ‘marked’ in any way to indicate they contain DSL’s.
It is feasible to detect the presence of certain artefacts in a project of any given solution which strongly suggest the presence of a DSL, such as the *.dsl file, but this is far from very reliable evidence.
Another challenge is telling the language project (called ‘Dsl’ by default) apart from the package project (called ‘DslPackage’ by default. Again it is feasible that a DSL developer could combine the two projects into one, or possibly even distribute the contents of these projects between many different other projects.
Because of these challenges, this release of the PowerToy configuration recipe prompts the user to select the package project of the DSL solution they wish to configure. Since it is the package project that contains the Visual Studio managed package framework (MPF) class to which the tool-window needs to be registered.
Once we know the project which contains the MPF package class, we can add code that extends this class (using partial classes) and integrate our tool-window and editor etc.
It is feasible that a DSL developer could run the configuration recipe on the same DSL project. Perhaps to reset the original configuration. Because of this, this recipe supports the ability to reconfigure or reset the configuration, and it takes some steps to remove old configuration before applying new configuration.

Removing the Editor

Once the PowerToy has configured a DSL solution, it is feasible that the DSL developer will wish to remove the editor and its configuration at some point in the future. To this end the PowerToy provides a removal recipe that when run, undoes the entire configuration to the DSL solution.
The challenge here is determining when a DSL solution has been configured and which package projects in the solution had been configured. Finally what parameters were used in that configuration.
It is highly likely that a DSL developer could have altered the configuration done by the PowerToy to suit his own purposes in the solution, so assuming the original configuration still stands would cause large issues in the removal phase, leading to potential incomplete removal. Of course, guarding against all possible cases is probably unreasonable, and a reasonable middle-ground needs to be sought.
Because of these challenges it was decided to leave ‘evidence’ of the configuration of the DSL solution within the configured projects themselves that would give hints when removing the configuration. This is done as user properties of the configured projects. This saved configuration can then be loaded as the default values when the removal recipe is run.
When the removal recipe completes this evidence is removed from the project artefacts.

Adding Artefacts

Most customization to a DSL tends to extend or enhance the DSL in some way. Adding tool-windows and supporting commands is no different. We can use the same code generation pattern used by the DSL tools themselves to provide the coding patterns for generating classes to represent the additional features we need.
In this way therefore, the PowerToy recipes add Text Template files to the DSL Solution that get transformed along with the other code templates present in the solution. This means that the classes that are generated from the PowerToy can remain within the same namespace and integrate easily with existing code of the DSL. This code can then be extended using partial classes, or replaced in more specific cases.
In order to do this, on installation the PowerToy configuration recipe must create Text Templates appropriate to the DSL solution it is configuring, and there are several parameters of these templates that must be configured in the installation phase. The PowerToy configuration recipe therefore uses a set of Text Templates to generate the Text Templates that are added to the DSL solution. This extra level of template generation provides us the precise match to the final DSL solution.
One particular problem with this extra generation indirection is that files like resource files (*.resx) that have ‘Custom Tools’ associated with them are not automatically configured with the Custom Tool. The specific Custom Tool needs to be configured after the Text Template is generated.

Adding Class Libraries

The code generated by the Text Templates, added by the PowerToy configuration recipes, use class libraries containing base classes and interfaces to simplify the implementation and minimise the generated code of these templates.
These class libraries are installed to the DSL development machine (in the GAC), and must be referenced by the projects of the DSL solution.
This is simply achieved by adding assembly references to the DSL solution projects.

VS Commands and Menus

In this release, the PowerToy includes menus to activate the tool-window containing the editor. These menus appear on the diagram context menu, and VS main menus (View | Other Windows).
In order to add these commands to the DSL solution, currently, Visual Studio requires these commands to be defined in a .ctc file. There is only one .ctc file per package allowed.
This presents a number of challenges since the PowerToy has to add definitions into this file at particular places, and the file format is rather esoteric and rigid in form.
CTC files can be extended by using #include statements to include commands defined in other header (*.h) files, separate from the main CTC file. This is a good means to define commands per aspect or component. However, these #include statements have to be inserted into the CTC file in the right place and the command definitions also need to included in the right blocks within this file.
Assuming that a CTC file is well formatted for human readability, then most of the definition blocks and their delimiters are formatted in such a way that macros can be inserted per line basis.
This means that we can #include a header file (*.h) containing #define macros for each part of the CTC file, and then insert these macros in the CTC file at the appropriate line. This then maintains the readability of the file whilst allowing us to define our commands in a separate header file.
Of course, just defining these commands statically in a CTC file is not enough to actually add commands to the IDE. As well as that, the package needs these commands added as MenuCommands to a CommandSet, and this CommandSet handles the enabling and actions of these commands.
For this we generate a partial class extension of the existing CommandSet class of the DSL solution, and add handlers to invoke these commands.

Last edited Mar 12, 2007 at 8:15 PM by jezzsa, version 1


No comments yet.