How to create a SynEdit highlighter

Document history
Contributor Date Changes
Kirys 2003/ 09 / 10 HTML Conversion
Pieter Polak 2001/ 12 / 27 Add the new syntax to specify the default attribute styles
Pieter Polak 2001/ 12 / 27 Add the new KEYS section syntax
Pieter Polak 2001/ 12 / 27 Update the document for the new TOKENTYPES section
Pieter Polak 2001/ 10 / 04 Include the new ‘SAMPLESOURCE’ section in the grammar file
Pieter Polak 2001/ 08 / 27 Initial setup of document

Todo:

Introduction

This document tries to give a explanation, on how to create your own custom highlighter for the TSynEdit component. This document starts with a step-by-step explanation on how to create a simple highlighter, but this will be expanded to cover advanced highlighting techniques.

Preparation

Creation of a new highlighter is best started with the creation of a grammar file. This grammar file (.msg file), is then used by the program SynGen to generate a basic skeleton of the highlighter source. For most highlighters this generated source is nothing more then a point to begin with, but for simple keyword highlighters, the result is directly usable.

The layout of the grammar file, is rather straight forward (you can insert empty lines if you like. Text between { and } is considered as comment, and thus is ignored):

Based on this .msg file, we can run the SynGen program, which will generate the basic highlighter source for us. After that we can take the generated source file, and fine tune it for our special needs.

Example: creating a basic highlighter

In our first example we will attempt to create a new highlighter, which will highlight just one simple keyword ‘Hello’. This highlighter is not case sensitive, and will be based on a SynGen grammar file. This grammar file should look like this:


TSynSampleSyn {first Identifier is considered to be the Class Name }

tk {second Identifier is considered to be the Identifier Prefix }

IdentStart '_', 'a'..'z', 'A'..'Z':: '_', '0'..'9', 'a'..'z', 'A'..'Z'::

TOKENTYPES

Identifier

Key

|><|

KEYS { all between KEYS and |><| is considered to be a keyword }

Hello

|><|

CHARS

'A'..'Z', 'a'..'z', '_':: Ident

BeginProc

fTokenID := IdentKind((fLine + Run));

inc(Run, fStringLen);

while Identifiers[fLine[Run]] do

Inc(Run);

EndProc

|><|

Once we have created this file (as sample.msg), we can startup SynGen. On startup, SynGen will prompt us to select the grammar file we have just created. After that, we are presented with a four-page window, which allows us to do some customizations:

After having filled in all fields in SynGen, we can press the ‘Start’ button. After that SynGen will generate a Delphi unit, called sample.pas, which contains the implementation of the highlighter. You can use this highlighter in SynEdit, and it will only highlight the keyword ‘Hello’ in bold.

If we now want to add more keywords to this highlighter, we simply add them in the grammar file, as new lines in the KEYS section. After regenerating the Pascal unit using SynGen, we will have the additional keywords being highlighted in SynEdit.

Adding support for comments and strings

After creating a simple highlighter like this, we want to add support for comments to the highlighter. In our sample language, comments are started with a ‘{‘ (brace open) character, and closed with a ‘}’ character (Pascal style comments).

To achieve this we must make two modifications to our grammar file:

Based on this modification, our grammar file will now look like (with two keywords: ‘Hello’ and ‘World’):


TSynSampleSyn {first Identifier is considered to be the Class Name }

tk {second Identifier is considered to be the Identifier Prefix }

IdentStart '_', 'a'..'z', 'A'..'Z':: '_', '0'..'9', 'a'..'z', 'A'..'Z'::

TOKENTYPES

Identifier

Comment

Space

Key

|><|

KEYS { all between KEYS and |><| is considered to be a keyword }

Hello

World

|><|

CHARS

'A'..'Z', 'a'..'z', '_':: Ident

BeginProc

fTokenID := IdentKind((fLine + Run));

inc(Run, fStringLen);

while Identifiers[fLine[Run]] do

Inc(Run);

EndProc

|><|

ENCLOSEDBY

Comment,BraceComment,{,},MultiLine

|><|

The ENCLOSEDBY section, can contain 1 or multiple lines to specifies token types which are recognized by a starting and ending sequence of characters. The syntax of each line is:

<Token name> , <Procedure name> , <starting sequence> , <ending sequence> [ , MultiLine]

The “Token name” should be an already defined token name in the higher part of the grammar file.

The “Procesdure name” should be unique for each line. This name is used to generate procedure names for each of the lines in the source code of the highlighter.

After you have generated the source code via the SynGen utility, you will see that you have a highlighter supporting two keywords (hello and world), and support for comments starting with { and ending with }.

Based on this logic we can now simply add support for /* .. */ C-style comments, by adding this line to the ENCLOSEDBY section:

Comment,CstyleComment,/*,*/,MultiLine

We can also add support for strings delimited by “ and “. For this we add a new token kind “String”, and then this line to the ENCLOSEDBY section (note that strings are not allowed to cross multiple lines):

String,String,”,”

So now our grammar file looks like this:

TSynSampleSyn {first Identifier is considered to be the Class Name }

tk {second Identifier is considered to be the Identifier Prefix }

IdentStart '_', 'a'..'z', 'A'..'Z':: '_', '0'..'9', 'a'..'z', 'A'..'Z'::


TOKENTYPES

Identifier

Comment

Space

String

Key

|><|


KEYS { all between KEYS and |><| is considered to be a keyword }

Hello

World

|><|


CHARS

'A'..'Z', 'a'..'z', '_':: Ident


BeginProc

fTokenID := IdentKind((fLine + Run));

inc(Run, fStringLen);

while Identifiers[fLine[Run]] do

Inc(Run);

EndProc

|><|


ENCLOSEDBY

Comment,BraceComment,{,},MultiLine

Comment,CStyleComment,/*,*/,MultiLine

String,String,","

|><|

Once you have generated the source code for this highlighter, and use it in your application, you can test it to see if it suits your needs. Once you have the generated source code from SynGen, you can further enhance it by modifying this code in Delphi.

Highlighting different kind of keywords

In our example till now, the highlighter will always recognize all keywords in the same way. All keywords are considered to be equal, and this will be highlighted with the same highlighter attribute. In practice you will often want to create a highlighter that will highlight e.g. data types in a different color then the other keywords. So now we will expand our grammar file, to add a new keyword ‘SynEdit’, which will be highlighted as a new token type ‘Test’. Our grammar file will become to look like this:

TSynSampleSyn {first Identifier is considered to be the Class Name }

tk {second Identifier is considered to be the Identifier Prefix }

IdentStart '_', 'a'..'z', 'A'..'Z':: '_', '0'..'9', 'a'..'z', 'A'..'Z'::


TOKENTYPES

Identifier

Comment

Space

String

Key

Test { ß Add the new token type here }

|><|


KEYS { all between KEYS and |><| is considered to be a keyword }

Hello

World

|><|


KEYS Test { ß Create a new KEYS section, and specify the token type }

SynEdit { So now this keyword will be highlighted with the Test token }

|><|


CHARS


'A'..'Z', 'a'..'z', '_':: Ident

BeginProc

fTokenID := IdentKind((fLine + Run));

inc(Run, fStringLen);

while Identifiers[fLine[Run]] do

Inc(Run);

EndProc


|><|


ENCLOSEDBY


Comment,BraceComment,{,},MultiLine

Comment,CStyleComment,/*,*/,MultiLine

String,String,","


><|

As you’ll notice, that there is an extension to the KEYS section, which allows you to specify the token type to use for the keywords in that section. If you don’t specify a token type, it will consider the token type to be ‘Keys’. You can create as many KEYS section as you, which in the grammar file, and even multiple sections with the same (or no) token type are allowed. The SynGen program will merge them automatically.

After you have run this grammar file through the SynGen utility you can test your highlighter, and you’ll notice that the SynEdit keyword is highlighted using a different attribute than the Hello and World keywords.

Setting the default highlighter attribute settings

One annoying thing so far about the generated highlighter is, that we should always modify the source code of the highlighter, if we want to provide default attribute styles for the different token types. Suppose we want to provide the user with the default settings of red strings, Italic and blue comments, and bold keywords, we now must modify our highlighter source code. This is not very nice, since this means that we might loose changes, once we want to regenerate the source code from the grammar file (e.g. because we added new keywords).

So what we will do now, is that we’ll modify our TOKENTYPES section, so that it includes the default styles for the different attributes. The section will then look like this:

TOKENTYPES

Identifier

Comment Style=[fsItalic]|Foreground=clNavy

Space

String Foreground=clRed

Key Style=[fsBold]

Test Background=clSilver|Foreground=clBlue|Style=[fsUnderline, fsItalic]

|><|

As you’ll notice, each token type can have 0 to 3 additional parameters, which specify the foreground, background and font style of the highlighter attribute. The different parameters are divided by a pipeline character (‘|’), and the order in which the parameters are mentioned is irrelevant. You are also free to leave out any of the parameters, and then the highlighter attribute will keep it’s default value for that parameter.

The value at the right sign of the equal operator, is considered to be valid Delphi code. The SynGen utility will assign this value directly to the associated attribute, and thus will not take any attempts to interpret it. This means that any of the following assignments are valid:

Background=clWhite

Background=$00FF00FF

Background=RGB(255,255,255)

etc.

Adding sample source to the highlighter

Many highlighters in the SynEdit package, have implemented the GetSampleSource function. This function should return a code snippet, which demonstrates all the features of the highlighter. You can add sample source to your highlighter, by adding a SAMPLESOURCE section to your grammar file. The source you will enclose between the keyword ‘SAMPLESOURCE’ and the terminator |><|, will be taken literally by your new highlighter as the result of the GetSampleSource function. For our sample, our grammar file will be extended with this section (at the end of the file):

SAMPLESOURCE

{ Sample source for the demo highlighter }


This highlighter will recognize the words Hello and

World as keywords. It will also highlight “Strings”.


And a special keyword type: SynEdit


/* This style of comments is also highlighted */

|><|

Contributing your highlighter to the SynEdit package

Once you have finalized your highlighter, and want to contribute it to the SynEdit package, it should meet the following requirements:

Sample sources

The sample sources from this tutorial are available in the SynEdit demos folder, as ‘HighlighterDemo’. This demo contains the grammar (.msg) file + a demo program showing the generated highlighter in use.