1 | // Scintilla source code edit control |
2 | /** @file LexPascal.cxx |
3 | ** Lexer for Pascal. |
4 | ** Written by Laurent le Tynevez |
5 | ** Updated by Simon Steele <s.steele@pnotepad.org> September 2002 |
6 | ** Updated by Mathias Rauen <scite@madshi.net> May 2003 (Delphi adjustments) |
7 | ** Completely rewritten by Marko Njezic <sf@maxempire.com> October 2008 |
8 | **/ |
9 | |
10 | /* |
11 | |
12 | A few words about features of the new completely rewritten LexPascal... |
13 | |
14 | Generally speaking LexPascal tries to support all available Delphi features (up |
15 | to Delphi XE4 at this time). |
16 | |
17 | ~ HIGHLIGHTING: |
18 | |
19 | If you enable "lexer.pascal.smart.highlighting" property, some keywords will |
20 | only be highlighted in appropriate context. As implemented those are keywords |
21 | related to property and DLL exports declarations (similar to how Delphi IDE |
22 | works). |
23 | |
24 | For example, keywords "read" and "write" will only be highlighted if they are in |
25 | property declaration: |
26 | |
27 | property MyProperty: boolean read FMyProperty write FMyProperty; |
28 | |
29 | ~ FOLDING: |
30 | |
31 | Folding is supported in the following cases: |
32 | |
33 | - Folding of stream-like comments |
34 | - Folding of groups of consecutive line comments |
35 | - Folding of preprocessor blocks (the following preprocessor blocks are |
36 | supported: IF / IFEND; IFDEF, IFNDEF, IFOPT / ENDIF and REGION / ENDREGION |
37 | blocks), including nesting of preprocessor blocks up to 255 levels |
38 | - Folding of code blocks on appropriate keywords (the following code blocks are |
39 | supported: "begin, asm, record, try, case / end" blocks, class & object |
40 | declarations and interface declarations) |
41 | |
42 | Remarks: |
43 | |
44 | - Folding of code blocks tries to handle all special cases in which folding |
45 | should not occur. As implemented those are: |
46 | |
47 | 1. Structure "record case / end" (there's only one "end" statement and "case" is |
48 | ignored as fold point) |
49 | 2. Forward class declarations ("type TMyClass = class;") and object method |
50 | declarations ("TNotifyEvent = procedure(Sender: TObject) of object;") are |
51 | ignored as fold points |
52 | 3. Simplified complete class declarations ("type TMyClass = class(TObject);") |
53 | are ignored as fold points |
54 | 4. Every other situation when class keyword doesn't actually start class |
55 | declaration ("class procedure", "class function", "class of", "class var", |
56 | "class property" and "class operator") |
57 | 5. Forward (disp)interface declarations ("type IMyInterface = interface;") are |
58 | ignored as fold points |
59 | |
60 | - Folding of code blocks inside preprocessor blocks is disabled (any comments |
61 | inside them will be folded fine) because there is no guarantee that complete |
62 | code block will be contained inside folded preprocessor block in which case |
63 | folded code block could end prematurely at the end of preprocessor block if |
64 | there is no closing statement inside. This was done in order to properly process |
65 | document that may contain something like this: |
66 | |
67 | type |
68 | {$IFDEF UNICODE} |
69 | TMyClass = class(UnicodeAncestor) |
70 | {$ELSE} |
71 | TMyClass = class(AnsiAncestor) |
72 | {$ENDIF} |
73 | private |
74 | ... |
75 | public |
76 | ... |
77 | published |
78 | ... |
79 | end; |
80 | |
81 | If class declarations were folded, then the second class declaration would end |
82 | at "$ENDIF" statement, first class statement would end at "end;" statement and |
83 | preprocessor "$IFDEF" block would go all the way to the end of document. |
84 | However, having in mind all this, if you want to enable folding of code blocks |
85 | inside preprocessor blocks, you can disable folding of preprocessor blocks by |
86 | changing "fold.preprocessor" property, in which case everything inside them |
87 | would be folded. |
88 | |
89 | ~ KEYWORDS: |
90 | |
91 | The list of keywords that can be used in pascal.properties file (up to Delphi |
92 | XE4): |
93 | |
94 | - Keywords: absolute abstract and array as asm assembler automated begin case |
95 | cdecl class const constructor delayed deprecated destructor dispid dispinterface |
96 | div do downto dynamic else end except experimental export exports external far |
97 | file final finalization finally for forward function goto helper if |
98 | implementation in inherited initialization inline interface is label library |
99 | message mod near nil not object of on operator or out overload override packed |
100 | pascal platform private procedure program property protected public published |
101 | raise record reference register reintroduce repeat resourcestring safecall |
102 | sealed set shl shr static stdcall strict string then threadvar to try type unit |
103 | unsafe until uses var varargs virtual while winapi with xor |
104 | |
105 | - Keywords related to the "smart highlithing" feature: add default implements |
106 | index name nodefault read readonly remove stored write writeonly |
107 | |
108 | - Keywords related to Delphi packages (in addition to all above): package |
109 | contains requires |
110 | |
111 | */ |
112 | |
113 | #include <stdlib.h> |
114 | #include <string.h> |
115 | #include <stdio.h> |
116 | #include <stdarg.h> |
117 | #include <assert.h> |
118 | #include <ctype.h> |
119 | |
120 | #include <string> |
121 | #include <string_view> |
122 | |
123 | #include "ILexer.h" |
124 | #include "Scintilla.h" |
125 | #include "SciLexer.h" |
126 | |
127 | #include "WordList.h" |
128 | #include "LexAccessor.h" |
129 | #include "Accessor.h" |
130 | #include "StyleContext.h" |
131 | #include "CharacterSet.h" |
132 | #include "LexerModule.h" |
133 | |
134 | using namespace Lexilla; |
135 | |
136 | static void GetRangeLowered(Sci_PositionU start, |
137 | Sci_PositionU end, |
138 | Accessor &styler, |
139 | char *s, |
140 | Sci_PositionU len) { |
141 | Sci_PositionU i = 0; |
142 | while ((i < end - start + 1) && (i < len-1)) { |
143 | s[i] = static_cast<char>(tolower(styler[start + i])); |
144 | i++; |
145 | } |
146 | s[i] = '\0'; |
147 | } |
148 | |
149 | static void GetForwardRangeLowered(Sci_PositionU start, |
150 | CharacterSet &charSet, |
151 | Accessor &styler, |
152 | char *s, |
153 | Sci_PositionU len) { |
154 | Sci_PositionU i = 0; |
155 | while ((i < len-1) && charSet.Contains(styler.SafeGetCharAt(start + i))) { |
156 | s[i] = static_cast<char>(tolower(styler.SafeGetCharAt(start + i))); |
157 | i++; |
158 | } |
159 | s[i] = '\0'; |
160 | |
161 | } |
162 | |
163 | enum { |
164 | stateInAsm = 0x1000, |
165 | stateInProperty = 0x2000, |
166 | stateInExport = 0x4000, |
167 | stateFoldInPreprocessor = 0x0100, |
168 | stateFoldInRecord = 0x0200, |
169 | stateFoldInPreprocessorLevelMask = 0x00FF, |
170 | stateFoldMaskAll = 0x0FFF |
171 | }; |
172 | |
173 | static void ClassifyPascalWord(WordList *keywordlists[], StyleContext &sc, int &curLineState, bool bSmartHighlighting) { |
174 | WordList& keywords = *keywordlists[0]; |
175 | |
176 | char s[100]; |
177 | sc.GetCurrentLowered(s, sizeof(s)); |
178 | if (keywords.InList(s)) { |
179 | if (curLineState & stateInAsm) { |
180 | if (strcmp(s, "end" ) == 0 && sc.GetRelative(-4) != '@') { |
181 | curLineState &= ~stateInAsm; |
182 | sc.ChangeState(SCE_PAS_WORD); |
183 | } else { |
184 | sc.ChangeState(SCE_PAS_ASM); |
185 | } |
186 | } else { |
187 | bool ignoreKeyword = false; |
188 | if (strcmp(s, "asm" ) == 0) { |
189 | curLineState |= stateInAsm; |
190 | } else if (bSmartHighlighting) { |
191 | if (strcmp(s, "property" ) == 0) { |
192 | curLineState |= stateInProperty; |
193 | } else if (strcmp(s, "exports" ) == 0) { |
194 | curLineState |= stateInExport; |
195 | } else if (!(curLineState & (stateInProperty | stateInExport)) && strcmp(s, "index" ) == 0) { |
196 | ignoreKeyword = true; |
197 | } else if (!(curLineState & stateInExport) && strcmp(s, "name" ) == 0) { |
198 | ignoreKeyword = true; |
199 | } else if (!(curLineState & stateInProperty) && |
200 | (strcmp(s, "read" ) == 0 || strcmp(s, "write" ) == 0 || |
201 | strcmp(s, "default" ) == 0 || strcmp(s, "nodefault" ) == 0 || |
202 | strcmp(s, "stored" ) == 0 || strcmp(s, "implements" ) == 0 || |
203 | strcmp(s, "readonly" ) == 0 || strcmp(s, "writeonly" ) == 0 || |
204 | strcmp(s, "add" ) == 0 || strcmp(s, "remove" ) == 0)) { |
205 | ignoreKeyword = true; |
206 | } |
207 | } |
208 | if (!ignoreKeyword) { |
209 | sc.ChangeState(SCE_PAS_WORD); |
210 | } |
211 | } |
212 | } else if (curLineState & stateInAsm) { |
213 | sc.ChangeState(SCE_PAS_ASM); |
214 | } |
215 | sc.SetState(SCE_PAS_DEFAULT); |
216 | } |
217 | |
218 | static void ColourisePascalDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], |
219 | Accessor &styler) { |
220 | bool bSmartHighlighting = styler.GetPropertyInt("lexer.pascal.smart.highlighting" , 1) != 0; |
221 | |
222 | CharacterSet setWordStart(CharacterSet::setAlpha, "_" , 0x80, true); |
223 | CharacterSet setWord(CharacterSet::setAlphaNum, "_" , 0x80, true); |
224 | CharacterSet setNumber(CharacterSet::setDigits, ".-+eE" ); |
225 | CharacterSet setHexNumber(CharacterSet::setDigits, "abcdefABCDEF" ); |
226 | CharacterSet setOperator(CharacterSet::setNone, "#$&'()*+,-./:;<=>@[]^{}" ); |
227 | |
228 | Sci_Position curLine = styler.GetLine(startPos); |
229 | int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0; |
230 | |
231 | StyleContext sc(startPos, length, initStyle, styler); |
232 | |
233 | for (; sc.More(); sc.Forward()) { |
234 | if (sc.atLineEnd) { |
235 | // Update the line state, so it can be seen by next line |
236 | curLine = styler.GetLine(sc.currentPos); |
237 | styler.SetLineState(curLine, curLineState); |
238 | } |
239 | |
240 | // Determine if the current state should terminate. |
241 | switch (sc.state) { |
242 | case SCE_PAS_NUMBER: |
243 | if (!setNumber.Contains(sc.ch) || (sc.ch == '.' && sc.chNext == '.')) { |
244 | sc.SetState(SCE_PAS_DEFAULT); |
245 | } else if (sc.ch == '-' || sc.ch == '+') { |
246 | if (sc.chPrev != 'E' && sc.chPrev != 'e') { |
247 | sc.SetState(SCE_PAS_DEFAULT); |
248 | } |
249 | } |
250 | break; |
251 | case SCE_PAS_IDENTIFIER: |
252 | if (!setWord.Contains(sc.ch)) { |
253 | ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting); |
254 | } |
255 | break; |
256 | case SCE_PAS_HEXNUMBER: |
257 | if (!setHexNumber.Contains(sc.ch)) { |
258 | sc.SetState(SCE_PAS_DEFAULT); |
259 | } |
260 | break; |
261 | case SCE_PAS_COMMENT: |
262 | case SCE_PAS_PREPROCESSOR: |
263 | if (sc.ch == '}') { |
264 | sc.ForwardSetState(SCE_PAS_DEFAULT); |
265 | } |
266 | break; |
267 | case SCE_PAS_COMMENT2: |
268 | case SCE_PAS_PREPROCESSOR2: |
269 | if (sc.Match('*', ')')) { |
270 | sc.Forward(); |
271 | sc.ForwardSetState(SCE_PAS_DEFAULT); |
272 | } |
273 | break; |
274 | case SCE_PAS_COMMENTLINE: |
275 | if (sc.atLineStart) { |
276 | sc.SetState(SCE_PAS_DEFAULT); |
277 | } |
278 | break; |
279 | case SCE_PAS_STRING: |
280 | if (sc.atLineEnd) { |
281 | sc.ChangeState(SCE_PAS_STRINGEOL); |
282 | } else if (sc.ch == '\'' && sc.chNext == '\'') { |
283 | sc.Forward(); |
284 | } else if (sc.ch == '\'') { |
285 | sc.ForwardSetState(SCE_PAS_DEFAULT); |
286 | } |
287 | break; |
288 | case SCE_PAS_STRINGEOL: |
289 | if (sc.atLineStart) { |
290 | sc.SetState(SCE_PAS_DEFAULT); |
291 | } |
292 | break; |
293 | case SCE_PAS_CHARACTER: |
294 | if (!setHexNumber.Contains(sc.ch) && sc.ch != '$') { |
295 | sc.SetState(SCE_PAS_DEFAULT); |
296 | } |
297 | break; |
298 | case SCE_PAS_OPERATOR: |
299 | if (bSmartHighlighting && sc.chPrev == ';') { |
300 | curLineState &= ~(stateInProperty | stateInExport); |
301 | } |
302 | sc.SetState(SCE_PAS_DEFAULT); |
303 | break; |
304 | case SCE_PAS_ASM: |
305 | sc.SetState(SCE_PAS_DEFAULT); |
306 | break; |
307 | } |
308 | |
309 | // Determine if a new state should be entered. |
310 | if (sc.state == SCE_PAS_DEFAULT) { |
311 | if (IsADigit(sc.ch) && !(curLineState & stateInAsm)) { |
312 | sc.SetState(SCE_PAS_NUMBER); |
313 | } else if (setWordStart.Contains(sc.ch)) { |
314 | sc.SetState(SCE_PAS_IDENTIFIER); |
315 | } else if (sc.ch == '$' && !(curLineState & stateInAsm)) { |
316 | sc.SetState(SCE_PAS_HEXNUMBER); |
317 | } else if (sc.Match('{', '$')) { |
318 | sc.SetState(SCE_PAS_PREPROCESSOR); |
319 | } else if (sc.ch == '{') { |
320 | sc.SetState(SCE_PAS_COMMENT); |
321 | } else if (sc.Match("(*$" )) { |
322 | sc.SetState(SCE_PAS_PREPROCESSOR2); |
323 | } else if (sc.Match('(', '*')) { |
324 | sc.SetState(SCE_PAS_COMMENT2); |
325 | sc.Forward(); // Eat the * so it isn't used for the end of the comment |
326 | } else if (sc.Match('/', '/')) { |
327 | sc.SetState(SCE_PAS_COMMENTLINE); |
328 | } else if (sc.ch == '\'') { |
329 | sc.SetState(SCE_PAS_STRING); |
330 | } else if (sc.ch == '#') { |
331 | sc.SetState(SCE_PAS_CHARACTER); |
332 | } else if (setOperator.Contains(sc.ch) && !(curLineState & stateInAsm)) { |
333 | sc.SetState(SCE_PAS_OPERATOR); |
334 | } else if (curLineState & stateInAsm) { |
335 | sc.SetState(SCE_PAS_ASM); |
336 | } |
337 | } |
338 | } |
339 | |
340 | if (sc.state == SCE_PAS_IDENTIFIER && setWord.Contains(sc.chPrev)) { |
341 | ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting); |
342 | } |
343 | |
344 | sc.Complete(); |
345 | } |
346 | |
347 | static bool (int style) { |
348 | return style == SCE_PAS_COMMENT || style == SCE_PAS_COMMENT2; |
349 | } |
350 | |
351 | static bool (Sci_Position line, Accessor &styler) { |
352 | Sci_Position pos = styler.LineStart(line); |
353 | Sci_Position eolPos = styler.LineStart(line + 1) - 1; |
354 | for (Sci_Position i = pos; i < eolPos; i++) { |
355 | char ch = styler[i]; |
356 | char chNext = styler.SafeGetCharAt(i + 1); |
357 | int style = styler.StyleAt(i); |
358 | if (ch == '/' && chNext == '/' && style == SCE_PAS_COMMENTLINE) { |
359 | return true; |
360 | } else if (!IsASpaceOrTab(ch)) { |
361 | return false; |
362 | } |
363 | } |
364 | return false; |
365 | } |
366 | |
367 | static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) { |
368 | return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask; |
369 | } |
370 | |
371 | static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) { |
372 | lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask; |
373 | lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask; |
374 | } |
375 | |
376 | static void ClassifyPascalPreprocessorFoldPoint(int &levelCurrent, int &lineFoldStateCurrent, |
377 | Sci_PositionU startPos, Accessor &styler) { |
378 | CharacterSet setWord(CharacterSet::setAlpha); |
379 | |
380 | char s[11]; // Size of the longest possible keyword + one additional character + null |
381 | GetForwardRangeLowered(startPos, setWord, styler, s, sizeof(s)); |
382 | |
383 | unsigned int nestLevel = GetFoldInPreprocessorLevelFlag(lineFoldStateCurrent); |
384 | |
385 | if (strcmp(s, "if" ) == 0 || |
386 | strcmp(s, "ifdef" ) == 0 || |
387 | strcmp(s, "ifndef" ) == 0 || |
388 | strcmp(s, "ifopt" ) == 0 || |
389 | strcmp(s, "region" ) == 0) { |
390 | nestLevel++; |
391 | SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel); |
392 | lineFoldStateCurrent |= stateFoldInPreprocessor; |
393 | levelCurrent++; |
394 | } else if (strcmp(s, "endif" ) == 0 || |
395 | strcmp(s, "ifend" ) == 0 || |
396 | strcmp(s, "endregion" ) == 0) { |
397 | nestLevel--; |
398 | SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel); |
399 | if (nestLevel == 0) { |
400 | lineFoldStateCurrent &= ~stateFoldInPreprocessor; |
401 | } |
402 | levelCurrent--; |
403 | if (levelCurrent < SC_FOLDLEVELBASE) { |
404 | levelCurrent = SC_FOLDLEVELBASE; |
405 | } |
406 | } |
407 | } |
408 | |
409 | static Sci_PositionU SkipWhiteSpace(Sci_PositionU currentPos, Sci_PositionU endPos, |
410 | Accessor &styler, bool includeChars = false) { |
411 | CharacterSet setWord(CharacterSet::setAlphaNum, "_" ); |
412 | Sci_PositionU j = currentPos + 1; |
413 | char ch = styler.SafeGetCharAt(j); |
414 | while ((j < endPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n' || |
415 | IsStreamCommentStyle(styler.StyleAt(j)) || (includeChars && setWord.Contains(ch)))) { |
416 | j++; |
417 | ch = styler.SafeGetCharAt(j); |
418 | } |
419 | return j; |
420 | } |
421 | |
422 | static void ClassifyPascalWordFoldPoint(int &levelCurrent, int &lineFoldStateCurrent, |
423 | Sci_Position startPos, Sci_PositionU endPos, |
424 | Sci_PositionU lastStart, Sci_PositionU currentPos, Accessor &styler) { |
425 | char s[100]; |
426 | GetRangeLowered(lastStart, currentPos, styler, s, sizeof(s)); |
427 | |
428 | if (strcmp(s, "record" ) == 0) { |
429 | lineFoldStateCurrent |= stateFoldInRecord; |
430 | levelCurrent++; |
431 | } else if (strcmp(s, "begin" ) == 0 || |
432 | strcmp(s, "asm" ) == 0 || |
433 | strcmp(s, "try" ) == 0 || |
434 | (strcmp(s, "case" ) == 0 && !(lineFoldStateCurrent & stateFoldInRecord))) { |
435 | levelCurrent++; |
436 | } else if (strcmp(s, "class" ) == 0 || strcmp(s, "object" ) == 0) { |
437 | // "class" & "object" keywords require special handling... |
438 | bool ignoreKeyword = false; |
439 | Sci_PositionU j = SkipWhiteSpace(currentPos, endPos, styler); |
440 | if (j < endPos) { |
441 | CharacterSet setWordStart(CharacterSet::setAlpha, "_" ); |
442 | CharacterSet setWord(CharacterSet::setAlphaNum, "_" ); |
443 | |
444 | if (styler.SafeGetCharAt(j) == ';') { |
445 | // Handle forward class declarations ("type TMyClass = class;") |
446 | // and object method declarations ("TNotifyEvent = procedure(Sender: TObject) of object;") |
447 | ignoreKeyword = true; |
448 | } else if (strcmp(s, "class" ) == 0) { |
449 | // "class" keyword has a few more special cases... |
450 | if (styler.SafeGetCharAt(j) == '(') { |
451 | // Handle simplified complete class declarations ("type TMyClass = class(TObject);") |
452 | j = SkipWhiteSpace(j, endPos, styler, true); |
453 | if (j < endPos && styler.SafeGetCharAt(j) == ')') { |
454 | j = SkipWhiteSpace(j, endPos, styler); |
455 | if (j < endPos && styler.SafeGetCharAt(j) == ';') { |
456 | ignoreKeyword = true; |
457 | } |
458 | } |
459 | } else if (setWordStart.Contains(styler.SafeGetCharAt(j))) { |
460 | char s2[11]; // Size of the longest possible keyword + one additional character + null |
461 | GetForwardRangeLowered(j, setWord, styler, s2, sizeof(s2)); |
462 | |
463 | if (strcmp(s2, "procedure" ) == 0 || |
464 | strcmp(s2, "function" ) == 0 || |
465 | strcmp(s2, "of" ) == 0 || |
466 | strcmp(s2, "var" ) == 0 || |
467 | strcmp(s2, "property" ) == 0 || |
468 | strcmp(s2, "operator" ) == 0) { |
469 | ignoreKeyword = true; |
470 | } |
471 | } |
472 | } |
473 | } |
474 | if (!ignoreKeyword) { |
475 | levelCurrent++; |
476 | } |
477 | } else if (strcmp(s, "interface" ) == 0) { |
478 | // "interface" keyword requires special handling... |
479 | bool ignoreKeyword = true; |
480 | Sci_Position j = lastStart - 1; |
481 | char ch = styler.SafeGetCharAt(j); |
482 | while ((j >= startPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n' || |
483 | IsStreamCommentStyle(styler.StyleAt(j)))) { |
484 | j--; |
485 | ch = styler.SafeGetCharAt(j); |
486 | } |
487 | if (j >= startPos && styler.SafeGetCharAt(j) == '=') { |
488 | ignoreKeyword = false; |
489 | } |
490 | if (!ignoreKeyword) { |
491 | Sci_PositionU k = SkipWhiteSpace(currentPos, endPos, styler); |
492 | if (k < endPos && styler.SafeGetCharAt(k) == ';') { |
493 | // Handle forward interface declarations ("type IMyInterface = interface;") |
494 | ignoreKeyword = true; |
495 | } |
496 | } |
497 | if (!ignoreKeyword) { |
498 | levelCurrent++; |
499 | } |
500 | } else if (strcmp(s, "dispinterface" ) == 0) { |
501 | // "dispinterface" keyword requires special handling... |
502 | bool ignoreKeyword = false; |
503 | Sci_PositionU j = SkipWhiteSpace(currentPos, endPos, styler); |
504 | if (j < endPos && styler.SafeGetCharAt(j) == ';') { |
505 | // Handle forward dispinterface declarations ("type IMyInterface = dispinterface;") |
506 | ignoreKeyword = true; |
507 | } |
508 | if (!ignoreKeyword) { |
509 | levelCurrent++; |
510 | } |
511 | } else if (strcmp(s, "end" ) == 0) { |
512 | lineFoldStateCurrent &= ~stateFoldInRecord; |
513 | levelCurrent--; |
514 | if (levelCurrent < SC_FOLDLEVELBASE) { |
515 | levelCurrent = SC_FOLDLEVELBASE; |
516 | } |
517 | } |
518 | } |
519 | |
520 | static void FoldPascalDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], |
521 | Accessor &styler) { |
522 | bool = styler.GetPropertyInt("fold.comment" ) != 0; |
523 | bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor" ) != 0; |
524 | bool foldCompact = styler.GetPropertyInt("fold.compact" , 1) != 0; |
525 | Sci_PositionU endPos = startPos + length; |
526 | int visibleChars = 0; |
527 | Sci_Position lineCurrent = styler.GetLine(startPos); |
528 | int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; |
529 | int levelCurrent = levelPrev; |
530 | int lineFoldStateCurrent = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) & stateFoldMaskAll : 0; |
531 | char chNext = styler[startPos]; |
532 | int styleNext = styler.StyleAt(startPos); |
533 | int style = initStyle; |
534 | |
535 | Sci_Position lastStart = 0; |
536 | CharacterSet setWord(CharacterSet::setAlphaNum, "_" , 0x80, true); |
537 | |
538 | for (Sci_PositionU i = startPos; i < endPos; i++) { |
539 | char ch = chNext; |
540 | chNext = styler.SafeGetCharAt(i + 1); |
541 | int stylePrev = style; |
542 | style = styleNext; |
543 | styleNext = styler.StyleAt(i + 1); |
544 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
545 | |
546 | if (foldComment && IsStreamCommentStyle(style)) { |
547 | if (!IsStreamCommentStyle(stylePrev)) { |
548 | levelCurrent++; |
549 | } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { |
550 | // Comments don't end at end of line and the next character may be unstyled. |
551 | levelCurrent--; |
552 | } |
553 | } |
554 | if (foldComment && atEOL && IsCommentLine(lineCurrent, styler)) |
555 | { |
556 | if (!IsCommentLine(lineCurrent - 1, styler) |
557 | && IsCommentLine(lineCurrent + 1, styler)) |
558 | levelCurrent++; |
559 | else if (IsCommentLine(lineCurrent - 1, styler) |
560 | && !IsCommentLine(lineCurrent+1, styler)) |
561 | levelCurrent--; |
562 | } |
563 | if (foldPreprocessor) { |
564 | if (style == SCE_PAS_PREPROCESSOR && ch == '{' && chNext == '$') { |
565 | ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 2, styler); |
566 | } else if (style == SCE_PAS_PREPROCESSOR2 && ch == '(' && chNext == '*' |
567 | && styler.SafeGetCharAt(i + 2) == '$') { |
568 | ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 3, styler); |
569 | } |
570 | } |
571 | |
572 | if (stylePrev != SCE_PAS_WORD && style == SCE_PAS_WORD) |
573 | { |
574 | // Store last word start point. |
575 | lastStart = i; |
576 | } |
577 | if (stylePrev == SCE_PAS_WORD && !(lineFoldStateCurrent & stateFoldInPreprocessor)) { |
578 | if(setWord.Contains(ch) && !setWord.Contains(chNext)) { |
579 | ClassifyPascalWordFoldPoint(levelCurrent, lineFoldStateCurrent, startPos, endPos, lastStart, i, styler); |
580 | } |
581 | } |
582 | |
583 | if (!IsASpace(ch)) |
584 | visibleChars++; |
585 | |
586 | if (atEOL) { |
587 | int lev = levelPrev; |
588 | if (visibleChars == 0 && foldCompact) |
589 | lev |= SC_FOLDLEVELWHITEFLAG; |
590 | if ((levelCurrent > levelPrev) && (visibleChars > 0)) |
591 | lev |= SC_FOLDLEVELHEADERFLAG; |
592 | if (lev != styler.LevelAt(lineCurrent)) { |
593 | styler.SetLevel(lineCurrent, lev); |
594 | } |
595 | int newLineState = (styler.GetLineState(lineCurrent) & ~stateFoldMaskAll) | lineFoldStateCurrent; |
596 | styler.SetLineState(lineCurrent, newLineState); |
597 | lineCurrent++; |
598 | levelPrev = levelCurrent; |
599 | visibleChars = 0; |
600 | } |
601 | } |
602 | |
603 | // If we didn't reach the EOL in previous loop, store line level and whitespace information. |
604 | // The rest will be filled in later... |
605 | int lev = levelPrev; |
606 | if (visibleChars == 0 && foldCompact) |
607 | lev |= SC_FOLDLEVELWHITEFLAG; |
608 | styler.SetLevel(lineCurrent, lev); |
609 | } |
610 | |
611 | static const char * const pascalWordListDesc[] = { |
612 | "Keywords" , |
613 | 0 |
614 | }; |
615 | |
616 | LexerModule lmPascal(SCLEX_PASCAL, ColourisePascalDoc, "pascal" , FoldPascalDoc, pascalWordListDesc); |
617 | |