CSS Syntax Highlighting in the DigitalRune Text Editor Control
For projects where I need some form of syntax highlighting, I tend to use the open source DigitalRune Text Editor Control which is a modified version of the text editor used in SharpDevelop. While it has a number of syntax definitions built in, the one it didn't have was for CSS formatting.
After doing a quick search on the internet and finding pretty much nothing, I created my own. This article describes that process, along with how to embed the definition directly in a custom version of the control, or loading it into the vendor supplied control.
Creating the rule set
Each definition is an XML document which contains various sections describing how to syntax highlight a document. An XSD schema is available, named Mode.xsd and located in the /Resources directory in the control's source code.
Here's an example of an (almost) empty definition - I've filled in the definition name and the list of file extensions it will support:
<?xml version="1.0" encoding="utf-8"?>
<SyntaxDefinition name="CSS" extensions="*.css">
<RuleSets>
</RuleSets>
</SyntaxDefinition>
The RuleSets
element contains one of more RuleSet
elements
which in turn describe formatting. I'm not sure how the control
decides to process these, but in my example I started with an
unnamed rule which references a named rule, and in turn that
references another - seems to work fine.
There are two key constructs we'll be using for highlighting - first is span highlighting, where an block of text which starts and ends with given symbols is highlighted. The second is keywords, where distinct words are highlighted. From having a quick look through the source code to figure out problems, there appears to be one or two other constructs available, but I'll ignore these for now.
First, I need to add a rule for comments, which should be quite
straight forward - look for a /*
and end with */
:
<RuleSet ignorecase="false">
<Span name="Comment" bold="false" italic="false" color="Green" stopateol="false">
<Begin>/*</Begin>
<End>*/</End>
</Span>
</RuleSet>
The Span
tag creates a span highlighting construct. The
Begin
and End
tags describe the phrase that marks the
beginning and end of the text to match. The stopateol
attribute determines if the line breaks should stop at the end
of a line. The formatting properties should be evident!
Next, I added another span rule to process the highlighting of
the actual CSS rules - so anything between {
and }
.
<Span name="CssClass" rule="CssClass" bold="false" italic="false" color="Black" stopateol="false">
<Begin>{</Begin>
<End>}</End>
</Span>
Note this time the rule
attribute - this is pointing to a new
ruleset (more on that below). Without this attribute, I found
that I was unable to style keywords and values inside the CSS
rule, as the span above always took precedence. The new ruleset
looks similar to this, although in this example I have stripped
out most of the CSS property names. (The list of which came from
w3schools)
<RuleSet name="CssClass" ignorecase="true">
<Span name="Value" rule="ValueRules" bold="false" italic="false" color="Blue" stopateol="false">
<Begin color="Black">:</Begin>
<End color="Black">;</End>
</Span>
<KeyWords name="CSSLevel1PropertyNames" bold="false" italic="false" color="Red">
<Key word="background" />
<Key word="background-attachment" />
(snip)
</KeyWords>
<KeyWords name="CSSLevel2PropertyNames" bold="false" italic="false" color="Red">
<Key word="border-collapse" />
<Key word="border-spacing" />
(snip)
</KeyWords>
<KeyWords name="CSSLevel3PropertyNames" bold="false" italic="false" color="Red">
<Key word="@font-face" />
<Key word="@keyframes" />
(snip)
</KeyWords>
</RuleSet>
First is a new span to highlight attribute values (found between
the :
and ;
characters in blue, and then 3 sets of a new
construct - KeyWords
. This basically matches a given word and
formats it appropriately. In this example, I have split each of
the 3 major CSS versions into separate sections, on the off
chance you want to reconfigure the file to only support a
subset, for example CSS1 and CSS2. Also note that I haven't
included any vendor prefixes.
One thing to note, in the Value
span above, the begin and end
tags have color
attributes. This overrides the overall span
color (blue) and colors those individual colors with the
override (black). Again, from checking the scheme it looks like
this can be done for most elements, and supports the color
,
bold
and italic
attributes, plus a bgcolor
attribute that
I haven't used yet.
The span in the above ruleset references a final ruleset, as follows:
<RuleSet name="ValueRules" ignorecase="false">
<Span name="Comment" bold="false" italic="false" color="Green" stopateol="false">
<Begin>/*</Begin>
<End>*/</End>
</Span>
<<pan name="String" bold="false" italic="false" color="BlueViolet" stopateol="true">
<Begin>"</Begin>
<End>"</End>
</Span>
<Span name="Char" bold="false" italic="false" color="BlueViolet" stopateol="true">
<Begin>'</Begin>
<End>'</End>
</Span>
<KeyWords name="Flags" bold="true" italic="false" color="BlueViolet">
<Key word="!important" />
</KeyWords>
</RuleSet>
This ruleset has 3 spans, and one keyword. I had to duplicate
the comment span from the first ruleset, I couldn't comment
highlighting to work inside { }
blocks otherwise - probably
some subtlety of the definition format that I'm missing. This is
followed by two spans which highlight strings (depending on
whether single or double quoted). Finally, we have a keyword
rule for formatting !important
. (Of course, ideally you
wouldn't be using this keyword at all, but you never know!)
Put together, this definition nicely highlights CSS. Except for
one thing - everything outside a comment or style block is
black. And I want it to be something else! Initially I tried
just setting the ForeColor
property of the control itself, but
this was blatantly ignored when it drew itself. Fortunately a
scan of the schema gave the answer - you can add an
Environment
tag and set up a large bunch of colors. Or one, in
this case.
<Environment>
<Default color="Maroon" bgcolor="White" />
</Environment>
Now save the file somewhere with the .xshd
extension - in
keeping with the convention of the existing definitions, I named
it CSS-Mode.xshd
.
Loading the definition into the Text Editor control
This is where I was a little bit stumped - as I didn't have a clue how to get the definition in. Fortunately, DigitalRune's technical support were able to help.
If you are using a custom version of the source code, you can add the definition directly into the source and have it available with the compiled assembly. However, if you are using the vendor supplied assembly, you'll need to include the definition with your application in order to load it in.
Compiling the definition into the assembly
This is quite straight forward, and easily recommended if you have a custom version.
Copy the definition file into the Resources folder of the control's project
Set the Build Action to be Embedded Resource
Open
SyntaxModes.xml
located in the same folder and add a mode tag which points to your definition, for example<Mode file="CSS-Mode.xshd" name="CSS" extensions=".css"/>
While I haven't checked to see if it is enforced, common sense would suggest you ensure the
name
andextensions
attributes match in both the syntax definition and the ruleset definition.Compile the solution.
With that done, your definition is now available for use!
Loading the definitions externally
You don't need to compile the definitions into the control assembly, but can load them externally. To do this, you need to have the definition file and the syntax mode file available for loading.
Add a new folder to your project and copy into this folder your
.xshd
file and set the Copy to Output Directory property to Copy always.Create a file named
SyntaxMode.xml
in the folder, and paste in the definition below. You'll also need to set the copy to output directory attribute.<?xml version="1.0" encoding="utf-8"?> <SyntaxModes version="1.0"> <Mode file="CSS-Mode.xshd" name="CSS" extensions=".css" /> </SyntaxModes>
The following line of code will load the definition file into the text editor control:
HighlightingManager.Manager.AddSyntaxModeFileProvider(new FileSyntaxModeProvider(definitionsFolder));
Setting up the Text Editor Control
To instruct instances of the Text Editor control to use CSS
syntax highlighting, add the following line of code to your
application (replacing CSS
with the name of your definition
if you called it something different):
textEditorControl.Document.HighlightingStrategy = HighlightingManager.Manager.FindHighlighter("CSS");
Syntax highlighting isn't appearing, what went wrong?
Rather frustratingly, the control doesn't raise an error if a
definition file is invalid, it just silently ignores it and uses
a default highlighting scheme. Use the source code for the
control so you can catch the exceptions being raised by the
HighlightingDefinitionParser
class in order to determine any
problems. Remember the definition you create is implicitly
linked to the schema and so must conform to it.
SharpDevelop?
As the DigitalRune control is derived from the original SharpDevelop editing component, I believe this article and sample code will work in exactly the same way for the SharpDevelop control. However, I don't have this installed and so this remains untested - let me know if it works for you!
Sample application
The download available below includes the CSS definition file and a sample application which will load in the definition files. Note that no binaries are included in the archive, you'll need to add a reference to a copy of the DigitalRune Text Editor control installed on your own system.
Update History
- 2011-07-08 - First published
- 2020-11-21 - Updated formatting
Downloads
Filename | Description | Version | Release Date | |
---|---|---|---|---|
DigitalRuneTextEditorCssHighlighting.zip
|
Sample project which shows how to create a definition ruleset to allow CSS formatting in the DigitalRune/SharpDevelop Text Editor Controls, and to load custom definition rulesets into the control. |
08/07/2011 | Download |
Leave a Comment
While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?