Sorry we haven’t made any new SyntaxEditor for WPF posts recently. We’ve been hammering away at the TODO items in code. We also have made a ton of performance improvements so that everything from document loading to scrolling is very fast. It’s cool stuff, but nothing we can show in a screenshot. :)
We’re now on the home stretch towards code completion of the SyntaxEditor for WPF public beta. Just a few outstanding areas left to work out before it will be ready.
One portion we need to finalize is how best to support highlighting style configuration from your side as a language developer. Let me describe how highlighting styles work in SyntaxEditor for WPF, they are a little different in concept than in SyntaxEditor 4.0.
In SyntaxEditor for WPF, we have achieved true separation of text/parsing and UI. In SyntaxEditor 4.0, you would assign highlighting styles directly to tokens. However in the WPF version, this wouldn’t match our separation design since highlighting styles are UI-related. Therefore what we have instead is something call classifications.
You define a set of classification types (implementing IClassificationType). We provide some common implementations such as classification types for general comments, identifiers, strings, etc. An IClassifier looks at tokens that were lexical parsed from your document and converts those into classified ranges, meaning a certain range of text has a certain classification type.
Take this line for example:
In a C# classifier, the first three letters will be assigned a Keyword classification type. The foo range can be assigned an Identifier classification type and so on.
The classification type is purely logical and doesn’t provide any highlighting information. So how does the SyntaxEditor control know what text to highlight as what? It uses a registry to map from a classification type to a related highlighting style. Therefore if our registry is configured properly, the Keyword classification type will return a blue foreground style from the registry.
Please give your feedback on the following…
Now here is where we’d love to get your input. Please provide some comments on any of the rest of this post because it’s still a bit up in the air as to how we’ll keep the implementation.
Right now, we have two registries. The first is a classification type registry. This basically just stores instances of IClassificationType that are keyed on their string Key. There is an ambient version of this registry out there so that say there are two languages that both use the classification for Comment. They can check to see if a classification type keyed as Comment is already registered, and reuse it if so.
The idea here is that an options dialog that allows customization of highlighting styles could enumerate the classification type registry and list each one in a ListBox. As the end user clicks the ListBox, the second registry (described earlier in this article that maps from classification type to highlighting style) would be checked to grab the highlighting style for the classification type and allow customization of it.
IClassificationType is currently defined like this:
1: public interface IClassificationType {
2: string Description { get; }
3: string Key { get; }
4: }
The Key is a constant key that uniquely identifies the classification type. The description is more of a description that could be localized in your application. For instance an XML comment classification type might have a Key of “XMLComment” but a Description of “XML Comment” where the latter may have been pulled from localized resources. The Description is what would show up in the ListBox.
Here is how a classification type instance can be registered with the ambient classification type registry:
1: AmbientClassificationTypeRegistry.Instance.Register(ClassificationTypes.Comment);
Note that ClassificationTypes static class provides some built-in common IClassificationType instances. In this case, we registered the Comment one.
Next we need to configure the classification type to highlighting style mapping before a language is created.
1: AmbientHighlightingStyleRegistry.Instance.Register(ClassificationTypes.Comment.Key,
2: new HighlightingStyle(ClassificationTypes.Comment.Key, Colors.Green, Colors.Transparent));
This added a mapping in AmbientHighlightingStyleRegistry, from a classification type to a green foreground style.
When a language is created that would use a Comment classification type, it would look it up in the registry like this:
1: IClassificationType commentCT =
2: AmbientClassificationTypeRegistry.Instance[ClassificationTypes.Comment.Key];
Thus the commentCT variable would end up with an instance of ClassificationTypes.Comment. When any text is classified with a classification type of Comment, it will end up being green in the editor.
You may be asking why we bother doing the registry lookup in that last code snippet instead of assigning commentCT directly to ClassificationTypes.Comment. Well assume that multiple languages you make are creating a new IClassificationType called Operator. The first IClassificationType with the key Operator will be the one that is used in the registry. If another IClassificationType keyed Operator is attempted to be registered, it will be ignored. Thus, the lookup in the code snippet above ensures that we get the instance that is registered.
Questions, issues, thoughts
The registries all need to be set up in your application before languages are loaded that use them. Also if you will have a highlighting style customization page in your options dialog, all of the classification types and styles should be set up before loading that.
So our question to you as a language designer is, where should the code to set up the registries be? Should you have to create an instance of a language to register that info and then just dispose the language if you don’t need it right away? Or should there be smaller classes that accomplish this? In that case you’d have to create those at app startup to do your registering but at least you wouldn’t be loading full-blown language classes. We could also have language classes auto call a related registrar class to ensure they are registered by the time the language is used.
If you are a SyntaxEditor alpha tester and have the latest build from today, you can see one implementation idea of a registrar class in the second Getting Started QuickStart.
Please post your comments now, thanks!