Sign in Join
Wiki copytemporary

SynEdit Highlighter: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11For more info on SynEdit go to: [[SynEdit]]\
22Also see [[SynEdit Markup]]
33
44------------------------------------------------------------------------
55
66# Understanding the SynEdit Highlighter
77
88## SynEdit - Highlighter relationship
99
1010**SynEdit to Highlighter** have an **N to 1** relationship.
1111
1212- One instance of a Highlighter can serve **N** (many) instances of SynEdits
1313- Each SynEdit only has one Highlighter
1414- But: one text (text-buffer) can have many highlighters, if shared by several SynEdit (each SynEdit will have one HL, but all HL will work on the same document)
1515
1616As a result of this:
1717
1818- no Highlighter Instance has a (fixed) reference to the SynEdit.
1919
2020
2121(Highlighters however keep a list of SynEditTextBuffers to which they are attached)
2222
2323- All data for the Highlighter is (and must be) stored on the SynEdit (actually on the TextBuffer of SynEdit (referred to as "Lines").
2424
2525However, before each call to the Highlighter, SynEdit ensures that Highlighter.CurrentLines is set to the current SynEdits Lines. This way the highlighter can access the data whenever needed. The Format of the data storage is determined by the highlighter (TSynCustomHighlighter.AttachToLines).
2626
2727## Scanning and Returning Highlight attributes
2828
2929The Highlighter is expected to work on a per Line base.
3030
3131If any text was modified, SynEdit will call (TSynCustomHighlighter.ScanFrom / Currently called from TSynEdit.ScanFrom) with the line range. The Highlighter should know the state of the previous line.
3232
3333If Highlight attributes are required, SynEdit will request them per Line too. SynEdit will loop through individual tokens on a line. This currently happens from nested proc PaintLines in SynEdit.PaintTextLines. It calls TSynCustomHighlighter.StartAtLineIndex, followed by HL.GetTokenEx/HL.GetTokenAttribute for as long as HL.GetEol is false.
3434
3535Also the BaseClass for the Highlighter's data (see AttachToLines) is based on per line storage, and SynEdit's TextBuffer (Lines) do maintenance on this data to keep it synchronized. That is: whenever lines of text are inserted or removed, so are entries inserted or removed from the highlighters data (hence it must have one entry per line).
3636
3737Usually Highlighters store the end-of-line-status in this field. So if the highlighter is going to work on a line, it will continue with the state-entry from the previous line.
3838
3939## Folding
4040
4141SynEdit's folding is handled by unit SynEditFoldedView and SynGutterCodeFolding. Highlighters that implement folding are to be based on TSynCustomFoldHighlighter.
4242
4343The basic information for communication between SynEditFoldedView and the HL requires 2 values stored for each line. (Of course the highlighter itself can store more information):
4444
4545- FoldLevel at the end of line
4646- Minimum FoldLevel encountered anywhere on the line
4747
4848The Foldlevel indicates how many (nested) folds exist. It goes up whenever a fold begins, and down when a fold ends:
4949
5050`                           EndLvl   MinLvl`\
5151` Procedure a;               1 -      0`\
5252` Begin                      2 --     1 -`\
5353`   b:= 1;                   2 --     2 --`\
5454`   if c > b then begin      3 ---    2 --`\
5555`     c:=b;                  3 ---    3 ---`\
5656`   end else begin           3 ---    2 --`\
5757`     b:=c;                  3 ---    3 ---`\
5858`   end;                     2 --     2 --`\
5959` end;                       0        0  // The end closes both: begin and procedure fold`
6060
6161In the line
6262
6363` Procedure a;               1 -      0`
6464
6565the MinLvl is 0, because the line started with a Level of 0 (and it never went down / no folds closed). Similar in all lines where there is only an opening fold keyword ("begin").
6666
6767But the line
6868
6969`   end else begin           3 ---    2 --`
7070
7171starts with a level of 3, and also ends with it (one close, one open). But since it went down first, the minimum level encountered anywhere on the line is 2.
7272
7373Without the MinLvl it would not be possible to tell that a fold ends in this line.
7474
7575There is no such thing as a MaxLvl, because folds that start and end on the same line can not be folded anyway. No need to detect them.
7676
7777` if a then begin b:=1; c:=2; end; // no fold on that line`
7878
7979# Creating a SynEdit Highlighter
8080
8181Since 0.9.31 Revision 35115 the fold-highlighter has changed. Implementing basic folding is now easier.
8282
8383All Sources can be found in the Lazarus installation directory under:
8484
8585:* Lazarus 2.3 and up: examples\Components\SynEdit\NewHighlighterTutorial
8686
8787:* Lazarus before 2.2.*: examples\SynEdit\NewHighlighterTutorial\\
8888
8989The project HighlighterTutorial contains 3 difference example highlighters:
9090
9191:* SimpleHl: used in Step 1 below
9292
9393:* ContextHl: used in Step 2 below
9494
9595:* FoldHl: used in Step 3 below
9696
9797SimpleHl and ContextHl will work with 0.9.30 too
9898
9999The folding highlighter in action:
100100
101-<figure>
102-<img src="SynEditFoldingHighlighterDemo.png" title="SynEditFoldingHighlighterDemo.png" />
103-<figcaption>SynEditFoldingHighlighterDemo.png</figcaption>
104-</figure>
101+![](/wiki-images/a/a2/SynEditFoldingHighlighterDemo.png)
105102
106103## The Basics: Returning Tokens and Attributes
107104
108105As indicated, the **SimpleHl** unit demonstrates this process.
109106
110107### What it does
111108
112109- It splits each line into words and spaces (or tabs)
113110 - The spaces are part of the text, and must be highlighted too
114111- This example allows specifying different colors for
115112
116113
117114
118115\- text (defaults to not-highlighted)
119116
120117\- spaces (defaults to silver frame)
121118
122119\- words, separated by spaces, that start with a,e,i,o,u (defaults to bold)
123120
124121\- the word "not" (defaults to red background)
125122
126123### How it works
127124
128125- Creation
129126
130127The Highlighter creates Attributes that it can return the Words and Spaces.
131128
132129- SetLine
133130
134131Is called by SynEdit before a line gets painted (or before highlight info is needed)
135132
136133- GetTokenEx, GetTokenAttribute, Next, GetEol
137134
138135Are used by SynEdit to iterate over the Line. Note that the first Token (Word or Spaces) must be ready after SetLine, without a call to Next.
139136
140137[[Important:]] The tokens returned for each line must represent the original line-text, and be returned in the correct order.
141138
142139- GetToken, GetTokenPos, GetTokenKind
143140
144141SynEdit uses them e.g for finding matching brackets. If tokenKind returns different values per Attribute, then brackets only match, if they are of the same kind (e.g. if there was a string attribute, brackets outside a string would not match brackets inside a string).
145142
146143### Other notes
147144
148145For readability, the highlighter has no optimization, so it may be very slow on larger texts. Many of the supplied highlighters use hash functions, to find what word (or any group of chars) is.
149146
150147## Step 2: Using Ranges
151148
152149As indicated, the **ContextHl** unit demonstrates this process
153150
154151The next example allows content of a line that influences other lines that follow. An example: a "(*" in Pascal makes all following lines a comment until a "*)" is found.
155152
156153This example extends the **SimpleHl** show above: The tokens -- and ++ (must be surrounded by space or line-begin/end to be a token of their own) will toggle words that start with a,e,i,o,u
157154
158155Multiple ++ and -- can be nested. Then for each -- a ++ must be given, before the words highlight again.
159156
160157Then we extend the scanner. The pre-scan to store the information calls the same functions as the highlighter. It is automatically called, if anything changes. (It is called for all lines below the changed line, until a line returns the same Range-value as it already had)
161158
162159The current amount of "--" is counted in
163160
164161``` pascal
165162 FCurRange: Integer;
166163```
167164
168165The amount is decreased by "++"
169166
170167To store the info we use:
171168
172169GetRange
173170Called after a line is completely scanned, to get the value at the end of the line. The value will be stored.
174171
175172SetRange
176173Called before a line gets scanned. Sets the value stored from the end of the previous line.
177174
178175ResetRange
179176Called before the 1st line is scanned (as there is no previous line).
180177
181178> **Note:** A scan is triggered by *every* change to a line (every keystroke). It scans the current line, and all lines below, until a line returns the same range that it already had. See: <http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139420.html#msg139420>
182179
183180### Important note on Ranges
184181
185182The purpose of the range is to allow the HL to start scanning in any line. The HL will not never need to look at a previous line. Any information needed to scan the current line can be derived from the ranges value.
186183
187184Example:
188185
189186``` pascal
190187 writeln; (*
191188 readln;
192189 *)
193190```
194191
195192when scanning the "readln" the HL knows from the range that it is in a comment, it does not need to look back at previous lines.
196193
197194Therefore scanning can start at any line.
198195
199196This also explains the note in the previous chapter. "until a line returns the same range that it already had". For even if the text of a line was not changed, if the value of the range at the lines start changed, then the scan result will change too.
200197
201198## Step 3: Add Folding
202199
203200As indicated, the **FoldHl** unit demonstrates this process
204201
205202For the example, the highlighter should fold everything between free-standing "-(-", "-)-".
206203
207204Change inheritance:
208205
209206``` pascal
210207 uses SynEditHighlighterFoldBase;
211208 ...
212209 TSynDemoHl = class(TSynCustomFoldHighlighter)
213210```
214211
215212Change the way range info is stored, since the base class uses it for fold-info:
216213
217214``` pascal
218215procedure TSynDemoHl.SetRange(Value: Pointer);
219216begin
220217 inherited;
221218 FCurRange := PtrInt(CodeFoldRange.RangeType);
222219end;
223220
224221procedure TSynDemoHl.ResetRange;
225222begin
226223 inherited;
227224 FCurRange := 0;
228225end;
229226
230227function TSynDemoHl.GetRange: Pointer;
231228begin
232229 CodeFoldRange.RangeType := Pointer(PtrInt(FCurRange));
233230 inherited;
234231end;
235232```
236233
237234Now add code to the scanner which tells the highlighter about opening and closing folds:
238235
239236``` pascal
240237procedure TSynDemoHl.FindTokenEnd;
241238begin
242239 ...
243240
244241 if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = '[') then
245242 StartCodeFoldBlock(nil);
246243 if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = ']') then
247244 EndCodeFoldBlock();
248245end;
249246```
250247
251248- **For 0.9.30**
252249
253250Please see the history of this page, if you are using a 0.9.30 version [[http://wiki.lazarus.freepascal.org/index.php?title=SynEdit Highlighter&amp;oldid=76020]]
254251
255252#### More info on StartCodeFoldBlock / EndCodeFoldBlock
256253
257254``` pascal
258255 function StartCodeFoldBlock(ABlockType: Pointer; IncreaseLevel: Boolean = true): TSynCustomCodeFoldBlock; virtual;
259256```
260257
261258ABlockType
262259Can be used to specify an ID for the block.\
263260
264261The field is not normally used as a pointer (though this is permitted). Normally IDs are a numeric enumeration.\
265262If you have different types of block (e.g. in Pascal: begin/end; repeat/until, ...), and you do not want a block being closed by the wrong keyword ("end" should not close "repeat"), then you can give them IDs:\
266-<b>StartCodeFoldBlock(PtrUInt(1))</b> // or other numbers\
263+StartCodeFoldBlock(PtrUInt(1)) // or other numbers\
267264
268265IncreaseLevel
269266If set to False, then a block will be inserted that can not be folded.\
270267Blocks like that can be used for internal tracking.
271268
272269NOTE: All folds must be nested, they can not overlap. That is, the last opened fold must be closed first.\
273270This refers to the "EndLvl" as shown in "Folding"(1.3) above\
274271Overlaps (like IFDEF and begin in the IDE) can not be done this way.
275272
276273``` pascal
277274 procedure EndCodeFoldBlock(DecreaseLevel: Boolean = True); virtual;
278275```
279276
280277DecreaseLevel
281278This *must* match IncreaseLevel, as it was given on the StartCodeFoldBlock\
282279
283280True means a fold ends; False means an internal block is ended. If mismatched then folds will either continue, or end early.\
284281TopCodeFoldBlockType can be used to indicate the ID of the innermost open block. One can use different IDs for internal blocks, and use this to set the value.
285282
286283``` pascal
287284 function TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;
288285```
289286
290287Returns the ID of the innermost block.
291288
292289DownIndex
293290can be used to get ID for the other block.
294291
295292DownIndex=1 means the block that is surrounding the innermost.
296293
297294# Configurable Highlighters (incl. 3rd party)
298295
299296## SynAnySyn
300297
301298A real simple Highlighter. More a reference implementation, than a real life useable Highlighter.
302299
303300## SynUniHighlighter
304301
305302Uses XML-based configurations files. [old official link](http://web.archive.org/web/20090610040212/http://www.delphist.com:80/UniHighlighter.html)
306303
307304## SynFacilSyn
308305
309306Flexible fully configurable Highlighter. <https://github.com/t-edson/SynFacilSyn>
310307
311308# References
312309
313310Threads on the forum:
314311
315312- "Making highligher for C syntaxis" [[http://www.lazarus.freepascal.org/index.php/topic,10260.0.html topic,10260]]
316313- "Creating a Syntax Highlighter" [[http://www.lazarus.freepascal.org/index.php/topic,10959.msg54714 topic,10959.msg54714]]
317314- obtaining highlight for printing [[http://www.lazarus.freepascal.org/index.php/topic,11384.msg57160.html|http://www.lazarus.freepascal.org/index.php/topic,11384.msg57160.html#msg57160 11384.msg57160]]
318315- Some details on ranges, and using objects to store more info [[http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139354.html|http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139354.html#msg139354 topic,21727.msg139354]] and [[http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139419.html|http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139419.html#msg139419 topic,21727.msg139419]]
319316- More on ranges, working copy vs immutable, foldblocks... [[http://forum.lazarus.freepascal.org/index.php/topic,30685.msg195464.html|http://forum.lazarus.freepascal.org/index.php/topic,30685.msg195464.html#msg195464 http://forum.lazarus.freepascal.org/index.php/topic,30685]]
320317- Markup (Highlight) all occurrences of a token (or regex) [[http://forum.lazarus.freepascal.org/index.php?topic=26833 topic=26833]]
321318- Markup and foldinfo: [[http://forum.lazarus.freepascal.org/index.php?topic=30122]]
322319- How to do invalidation in TSynEditMarkup [[http://forum.lazarus.freepascal.org/index.php/topic,30122.msg207931.html|http://forum.lazarus.freepascal.org/index.php/topic,30122.msg207931.html#msg207931]]
323320
324321- Folding
325322 - "SynEdit - improved highlighters to handle Code Folding?" [[http://www.lazarus.freepascal.org/index.php/topic,7879.0.html topic,7879]]
326323 - "SynEdit - Add support code folding for Java" [[http://www.lazarus.freepascal.org/index.php/topic,7338.0.html topic,7338]]
327324 - "CodeFolding" Config [[http://www.lazarus.freepascal.org/index.php/topic,11064 topic,11064]]
328325 - Fold blocks "end" keyword (end on the line before the next keyword) [[http://forum.lazarus.freepascal.org/index.php/topic,23411.msg139621.html|http://forum.lazarus.freepascal.org/index.php/topic,23411.msg139621.html#msg139621 topic,23411.msg139621]]
329326 - Folding selected text from code (user/application code): [[http://forum.lazarus.freepascal.org/index.php/topic,24473.msg147312.html|http://forum.lazarus.freepascal.org/index.php/topic,24473.msg147312.html#msg147312 24473.msg147312]]
330327 - Obtaining state of folding (save fold state with session) [[http://forum.lazarus.freepascal.org/index.php?topic=26748 topic=26748]]
331328
332329---
333330*From the Free Pascal wiki, page "SynEdit Highlighter", as it stood on 2022-05-04 after a change by Martin, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
334331Category: SynEdit

Back to history