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
12A few words about features of the new completely rewritten LexPascal...
13
14Generally speaking LexPascal tries to support all available Delphi features (up
15to Delphi XE4 at this time).
16
17~ HIGHLIGHTING:
18
19If you enable "lexer.pascal.smart.highlighting" property, some keywords will
20only be highlighted in appropriate context. As implemented those are keywords
21related to property and DLL exports declarations (similar to how Delphi IDE
22works).
23
24For example, keywords "read" and "write" will only be highlighted if they are in
25property declaration:
26
27property MyProperty: boolean read FMyProperty write FMyProperty;
28
29~ FOLDING:
30
31Folding 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
36supported: IF / IFEND; IFDEF, IFNDEF, IFOPT / ENDIF and REGION / ENDREGION
37blocks), including nesting of preprocessor blocks up to 255 levels
38- Folding of code blocks on appropriate keywords (the following code blocks are
39supported: "begin, asm, record, try, case / end" blocks, class & object
40declarations and interface declarations)
41
42Remarks:
43
44- Folding of code blocks tries to handle all special cases in which folding
45should not occur. As implemented those are:
46
471. Structure "record case / end" (there's only one "end" statement and "case" is
48ignored as fold point)
492. Forward class declarations ("type TMyClass = class;") and object method
50declarations ("TNotifyEvent = procedure(Sender: TObject) of object;") are
51ignored as fold points
523. Simplified complete class declarations ("type TMyClass = class(TObject);")
53are ignored as fold points
544. Every other situation when class keyword doesn't actually start class
55declaration ("class procedure", "class function", "class of", "class var",
56"class property" and "class operator")
575. Forward (disp)interface declarations ("type IMyInterface = interface;") are
58ignored as fold points
59
60- Folding of code blocks inside preprocessor blocks is disabled (any comments
61inside them will be folded fine) because there is no guarantee that complete
62code block will be contained inside folded preprocessor block in which case
63folded code block could end prematurely at the end of preprocessor block if
64there is no closing statement inside. This was done in order to properly process
65document that may contain something like this:
66
67type
68{$IFDEF UNICODE}
69 TMyClass = class(UnicodeAncestor)
70{$ELSE}
71 TMyClass = class(AnsiAncestor)
72{$ENDIF}
73 private
74 ...
75 public
76 ...
77 published
78 ...
79end;
80
81If class declarations were folded, then the second class declaration would end
82at "$ENDIF" statement, first class statement would end at "end;" statement and
83preprocessor "$IFDEF" block would go all the way to the end of document.
84However, having in mind all this, if you want to enable folding of code blocks
85inside preprocessor blocks, you can disable folding of preprocessor blocks by
86changing "fold.preprocessor" property, in which case everything inside them
87would be folded.
88
89~ KEYWORDS:
90
91The list of keywords that can be used in pascal.properties file (up to Delphi
92XE4):
93
94- Keywords: absolute abstract and array as asm assembler automated begin case
95cdecl class const constructor delayed deprecated destructor dispid dispinterface
96div do downto dynamic else end except experimental export exports external far
97file final finalization finally for forward function goto helper if
98implementation in inherited initialization inline interface is label library
99message mod near nil not object of on operator or out overload override packed
100pascal platform private procedure program property protected public published
101raise record reference register reintroduce repeat resourcestring safecall
102sealed set shl shr static stdcall strict string then threadvar to try type unit
103unsafe until uses var varargs virtual while winapi with xor
104
105- Keywords related to the "smart highlithing" feature: add default implements
106index name nodefault read readonly remove stored write writeonly
107
108- Keywords related to Delphi packages (in addition to all above): package
109contains 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
134using namespace Lexilla;
135
136static 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
149static 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
163enum {
164 stateInAsm = 0x1000,
165 stateInProperty = 0x2000,
166 stateInExport = 0x4000,
167 stateFoldInPreprocessor = 0x0100,
168 stateFoldInRecord = 0x0200,
169 stateFoldInPreprocessorLevelMask = 0x00FF,
170 stateFoldMaskAll = 0x0FFF
171};
172
173static 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
218static 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
347static bool IsStreamCommentStyle(int style) {
348 return style == SCE_PAS_COMMENT || style == SCE_PAS_COMMENT2;
349}
350
351static bool IsCommentLine(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
367static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) {
368 return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask;
369}
370
371static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) {
372 lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask;
373 lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask;
374}
375
376static 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
409static 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
422static 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
520static void FoldPascalDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[],
521 Accessor &styler) {
522 bool foldComment = 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
611static const char * const pascalWordListDesc[] = {
612 "Keywords",
613 0
614};
615
616LexerModule lmPascal(SCLEX_PASCAL, ColourisePascalDoc, "pascal", FoldPascalDoc, pascalWordListDesc);
617