Click here to Skip to main content
15,884,986 members
Articles / Web Development / CSS

CSS Syntax Highlighting in the DigitalRune Text Editor Control

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
20 Jan 2012CPOL7 min read 24.2K   109   4   4
An article on how to apply CSS formatting rules to the DigitialRune 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
<?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 ruleset which references a named ruleset, 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 a 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 /*:

XML
<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 }.

XML
<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.)

XML
<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:

XML
<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.

XML
<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.

  1. Copy the definition file into the Resources folder of the control's project
  2. Set the Build Action to be Embedded Resource
  3. Open SyntaxModes.xml located in the same folder and add a mode tag which points to your definition, for example:
    XML
    <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 and extensions attributes match in both the syntax definition and the ruleset definition.
  4. 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.

  1. 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.
  2. 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
    <?xml version="1.0" encoding="utf-8"?>
    <SyntaxModes version="1.0">
      <Mode file="CSS-Mode.xshd" name="CSS" extensions=".css" />
    </SyntaxModes>
  3. The following line of code will load the definition file into the text editor control:
    C#
    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):

C#
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 above 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIt has a null referenece Exception Pin
Member 858171515-Feb-12 15:15
Member 858171515-Feb-12 15:15 
AnswerRe: It has a null referenece Exception Pin
Richard James Moss16-Feb-12 5:42
professionalRichard James Moss16-Feb-12 5:42 
Thanks for the comment. As CBL-Mode.xshd isn't a file I've created, I assume this is something custom that you have developed? Your best course of action (aside from checking your XSHD file for errors) would be to run the DigitalRune source code, trigger the crash, then work back through the stack trace in order to find the reason for the error. As I'm not the author of the DigitalRune component, I don't have a great deal of knowledge on it's inner workings - certainly the example I have provided in this article has been running flawlessly for 7 months in my own code.

Sorry I can't be of any more help;
Richard Moss
GeneralMy vote of 5 Pin
mazrabul13-Feb-12 5:38
mazrabul13-Feb-12 5:38 
GeneralRe: My vote of 5 Pin
Richard James Moss16-Feb-12 5:37
professionalRichard James Moss16-Feb-12 5:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.