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 "ILexer.h"
121 #include "Scintilla.h"
122 #include "SciLexer.h"
123
124 #include "WordList.h"
125 #include "LexAccessor.h"
126 #include "Accessor.h"
127 #include "StyleContext.h"
128 #include "CharacterSet.h"
129 #include "LexerModule.h"
130
131 using namespace Scintilla;
132
GetRangeLowered(Sci_PositionU start,Sci_PositionU end,Accessor & styler,char * s,Sci_PositionU len)133 static void GetRangeLowered(Sci_PositionU start,
134 Sci_PositionU end,
135 Accessor &styler,
136 char *s,
137 Sci_PositionU len) {
138 Sci_PositionU i = 0;
139 while ((i < end - start + 1) && (i < len-1)) {
140 s[i] = static_cast<char>(tolower(styler[start + i]));
141 i++;
142 }
143 s[i] = '\0';
144 }
145
GetForwardRangeLowered(Sci_PositionU start,CharacterSet & charSet,Accessor & styler,char * s,Sci_PositionU len)146 static void GetForwardRangeLowered(Sci_PositionU start,
147 CharacterSet &charSet,
148 Accessor &styler,
149 char *s,
150 Sci_PositionU len) {
151 Sci_PositionU i = 0;
152 while ((i < len-1) && charSet.Contains(styler.SafeGetCharAt(start + i))) {
153 s[i] = static_cast<char>(tolower(styler.SafeGetCharAt(start + i)));
154 i++;
155 }
156 s[i] = '\0';
157
158 }
159
160 enum {
161 stateInAsm = 0x1000,
162 stateInProperty = 0x2000,
163 stateInExport = 0x4000,
164 stateFoldInPreprocessor = 0x0100,
165 stateFoldInRecord = 0x0200,
166 stateFoldInPreprocessorLevelMask = 0x00FF,
167 stateFoldMaskAll = 0x0FFF
168 };
169
ClassifyPascalWord(WordList * keywordlists[],StyleContext & sc,int & curLineState,bool bSmartHighlighting)170 static void ClassifyPascalWord(WordList *keywordlists[], StyleContext &sc, int &curLineState, bool bSmartHighlighting) {
171 WordList& keywords = *keywordlists[0];
172
173 char s[100];
174 sc.GetCurrentLowered(s, sizeof(s));
175 if (keywords.InList(s)) {
176 if (curLineState & stateInAsm) {
177 if (strcmp(s, "end") == 0 && sc.GetRelative(-4) != '@') {
178 curLineState &= ~stateInAsm;
179 sc.ChangeState(SCE_PAS_WORD);
180 } else {
181 sc.ChangeState(SCE_PAS_ASM);
182 }
183 } else {
184 bool ignoreKeyword = false;
185 if (strcmp(s, "asm") == 0) {
186 curLineState |= stateInAsm;
187 } else if (bSmartHighlighting) {
188 if (strcmp(s, "property") == 0) {
189 curLineState |= stateInProperty;
190 } else if (strcmp(s, "exports") == 0) {
191 curLineState |= stateInExport;
192 } else if (!(curLineState & (stateInProperty | stateInExport)) && strcmp(s, "index") == 0) {
193 ignoreKeyword = true;
194 } else if (!(curLineState & stateInExport) && strcmp(s, "name") == 0) {
195 ignoreKeyword = true;
196 } else if (!(curLineState & stateInProperty) &&
197 (strcmp(s, "read") == 0 || strcmp(s, "write") == 0 ||
198 strcmp(s, "default") == 0 || strcmp(s, "nodefault") == 0 ||
199 strcmp(s, "stored") == 0 || strcmp(s, "implements") == 0 ||
200 strcmp(s, "readonly") == 0 || strcmp(s, "writeonly") == 0 ||
201 strcmp(s, "add") == 0 || strcmp(s, "remove") == 0)) {
202 ignoreKeyword = true;
203 }
204 }
205 if (!ignoreKeyword) {
206 sc.ChangeState(SCE_PAS_WORD);
207 }
208 }
209 } else if (curLineState & stateInAsm) {
210 sc.ChangeState(SCE_PAS_ASM);
211 }
212 sc.SetState(SCE_PAS_DEFAULT);
213 }
214
ColourisePascalDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)215 static void ColourisePascalDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[],
216 Accessor &styler) {
217 bool bSmartHighlighting = styler.GetPropertyInt("lexer.pascal.smart.highlighting", 1) != 0;
218
219 CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
220 CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true);
221 CharacterSet setNumber(CharacterSet::setDigits, ".-+eE");
222 CharacterSet setHexNumber(CharacterSet::setDigits, "abcdefABCDEF");
223 CharacterSet setOperator(CharacterSet::setNone, "#$&'()*+,-./:;<=>@[]^{}");
224
225 Sci_Position curLine = styler.GetLine(startPos);
226 int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0;
227
228 StyleContext sc(startPos, length, initStyle, styler);
229
230 for (; sc.More(); sc.Forward()) {
231 if (sc.atLineEnd) {
232 // Update the line state, so it can be seen by next line
233 curLine = styler.GetLine(sc.currentPos);
234 styler.SetLineState(curLine, curLineState);
235 }
236
237 // Determine if the current state should terminate.
238 switch (sc.state) {
239 case SCE_PAS_NUMBER:
240 if (!setNumber.Contains(sc.ch) || (sc.ch == '.' && sc.chNext == '.')) {
241 sc.SetState(SCE_PAS_DEFAULT);
242 } else if (sc.ch == '-' || sc.ch == '+') {
243 if (sc.chPrev != 'E' && sc.chPrev != 'e') {
244 sc.SetState(SCE_PAS_DEFAULT);
245 }
246 }
247 break;
248 case SCE_PAS_IDENTIFIER:
249 if (!setWord.Contains(sc.ch)) {
250 ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting);
251 }
252 break;
253 case SCE_PAS_HEXNUMBER:
254 if (!setHexNumber.Contains(sc.ch)) {
255 sc.SetState(SCE_PAS_DEFAULT);
256 }
257 break;
258 case SCE_PAS_COMMENT:
259 case SCE_PAS_PREPROCESSOR:
260 if (sc.ch == '}') {
261 sc.ForwardSetState(SCE_PAS_DEFAULT);
262 }
263 break;
264 case SCE_PAS_COMMENT2:
265 case SCE_PAS_PREPROCESSOR2:
266 if (sc.Match('*', ')')) {
267 sc.Forward();
268 sc.ForwardSetState(SCE_PAS_DEFAULT);
269 }
270 break;
271 case SCE_PAS_COMMENTLINE:
272 if (sc.atLineStart) {
273 sc.SetState(SCE_PAS_DEFAULT);
274 }
275 break;
276 case SCE_PAS_STRING:
277 if (sc.atLineEnd) {
278 sc.ChangeState(SCE_PAS_STRINGEOL);
279 } else if (sc.ch == '\'' && sc.chNext == '\'') {
280 sc.Forward();
281 } else if (sc.ch == '\'') {
282 sc.ForwardSetState(SCE_PAS_DEFAULT);
283 }
284 break;
285 case SCE_PAS_STRINGEOL:
286 if (sc.atLineStart) {
287 sc.SetState(SCE_PAS_DEFAULT);
288 }
289 break;
290 case SCE_PAS_CHARACTER:
291 if (!setHexNumber.Contains(sc.ch) && sc.ch != '$') {
292 sc.SetState(SCE_PAS_DEFAULT);
293 }
294 break;
295 case SCE_PAS_OPERATOR:
296 if (bSmartHighlighting && sc.chPrev == ';') {
297 curLineState &= ~(stateInProperty | stateInExport);
298 }
299 sc.SetState(SCE_PAS_DEFAULT);
300 break;
301 case SCE_PAS_ASM:
302 sc.SetState(SCE_PAS_DEFAULT);
303 break;
304 }
305
306 // Determine if a new state should be entered.
307 if (sc.state == SCE_PAS_DEFAULT) {
308 if (IsADigit(sc.ch) && !(curLineState & stateInAsm)) {
309 sc.SetState(SCE_PAS_NUMBER);
310 } else if (setWordStart.Contains(sc.ch)) {
311 sc.SetState(SCE_PAS_IDENTIFIER);
312 } else if (sc.ch == '$' && !(curLineState & stateInAsm)) {
313 sc.SetState(SCE_PAS_HEXNUMBER);
314 } else if (sc.Match('{', '$')) {
315 sc.SetState(SCE_PAS_PREPROCESSOR);
316 } else if (sc.ch == '{') {
317 sc.SetState(SCE_PAS_COMMENT);
318 } else if (sc.Match("(*$")) {
319 sc.SetState(SCE_PAS_PREPROCESSOR2);
320 } else if (sc.Match('(', '*')) {
321 sc.SetState(SCE_PAS_COMMENT2);
322 sc.Forward(); // Eat the * so it isn't used for the end of the comment
323 } else if (sc.Match('/', '/')) {
324 sc.SetState(SCE_PAS_COMMENTLINE);
325 } else if (sc.ch == '\'') {
326 sc.SetState(SCE_PAS_STRING);
327 } else if (sc.ch == '#') {
328 sc.SetState(SCE_PAS_CHARACTER);
329 } else if (setOperator.Contains(sc.ch) && !(curLineState & stateInAsm)) {
330 sc.SetState(SCE_PAS_OPERATOR);
331 } else if (curLineState & stateInAsm) {
332 sc.SetState(SCE_PAS_ASM);
333 }
334 }
335 }
336
337 if (sc.state == SCE_PAS_IDENTIFIER && setWord.Contains(sc.chPrev)) {
338 ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting);
339 }
340
341 sc.Complete();
342 }
343
IsStreamCommentStyle(int style)344 static bool IsStreamCommentStyle(int style) {
345 return style == SCE_PAS_COMMENT || style == SCE_PAS_COMMENT2;
346 }
347
IsCommentLine(Sci_Position line,Accessor & styler)348 static bool IsCommentLine(Sci_Position line, Accessor &styler) {
349 Sci_Position pos = styler.LineStart(line);
350 Sci_Position eolPos = styler.LineStart(line + 1) - 1;
351 for (Sci_Position i = pos; i < eolPos; i++) {
352 char ch = styler[i];
353 char chNext = styler.SafeGetCharAt(i + 1);
354 int style = styler.StyleAt(i);
355 if (ch == '/' && chNext == '/' && style == SCE_PAS_COMMENTLINE) {
356 return true;
357 } else if (!IsASpaceOrTab(ch)) {
358 return false;
359 }
360 }
361 return false;
362 }
363
GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent)364 static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) {
365 return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask;
366 }
367
SetFoldInPreprocessorLevelFlag(int & lineFoldStateCurrent,unsigned int nestLevel)368 static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) {
369 lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask;
370 lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask;
371 }
372
ClassifyPascalPreprocessorFoldPoint(int & levelCurrent,int & lineFoldStateCurrent,Sci_PositionU startPos,Accessor & styler)373 static void ClassifyPascalPreprocessorFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,
374 Sci_PositionU startPos, Accessor &styler) {
375 CharacterSet setWord(CharacterSet::setAlpha);
376
377 char s[11]; // Size of the longest possible keyword + one additional character + null
378 GetForwardRangeLowered(startPos, setWord, styler, s, sizeof(s));
379
380 unsigned int nestLevel = GetFoldInPreprocessorLevelFlag(lineFoldStateCurrent);
381
382 if (strcmp(s, "if") == 0 ||
383 strcmp(s, "ifdef") == 0 ||
384 strcmp(s, "ifndef") == 0 ||
385 strcmp(s, "ifopt") == 0 ||
386 strcmp(s, "region") == 0) {
387 nestLevel++;
388 SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel);
389 lineFoldStateCurrent |= stateFoldInPreprocessor;
390 levelCurrent++;
391 } else if (strcmp(s, "endif") == 0 ||
392 strcmp(s, "ifend") == 0 ||
393 strcmp(s, "endregion") == 0) {
394 nestLevel--;
395 SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel);
396 if (nestLevel == 0) {
397 lineFoldStateCurrent &= ~stateFoldInPreprocessor;
398 }
399 levelCurrent--;
400 if (levelCurrent < SC_FOLDLEVELBASE) {
401 levelCurrent = SC_FOLDLEVELBASE;
402 }
403 }
404 }
405
SkipWhiteSpace(Sci_PositionU currentPos,Sci_PositionU endPos,Accessor & styler,bool includeChars=false)406 static Sci_PositionU SkipWhiteSpace(Sci_PositionU currentPos, Sci_PositionU endPos,
407 Accessor &styler, bool includeChars = false) {
408 CharacterSet setWord(CharacterSet::setAlphaNum, "_");
409 Sci_PositionU j = currentPos + 1;
410 char ch = styler.SafeGetCharAt(j);
411 while ((j < endPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n' ||
412 IsStreamCommentStyle(styler.StyleAt(j)) || (includeChars && setWord.Contains(ch)))) {
413 j++;
414 ch = styler.SafeGetCharAt(j);
415 }
416 return j;
417 }
418
ClassifyPascalWordFoldPoint(int & levelCurrent,int & lineFoldStateCurrent,Sci_Position startPos,Sci_PositionU endPos,Sci_PositionU lastStart,Sci_PositionU currentPos,Accessor & styler)419 static void ClassifyPascalWordFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,
420 Sci_Position startPos, Sci_PositionU endPos,
421 Sci_PositionU lastStart, Sci_PositionU currentPos, Accessor &styler) {
422 char s[100];
423 GetRangeLowered(lastStart, currentPos, styler, s, sizeof(s));
424
425 if (strcmp(s, "record") == 0) {
426 lineFoldStateCurrent |= stateFoldInRecord;
427 levelCurrent++;
428 } else if (strcmp(s, "begin") == 0 ||
429 strcmp(s, "asm") == 0 ||
430 strcmp(s, "try") == 0 ||
431 (strcmp(s, "case") == 0 && !(lineFoldStateCurrent & stateFoldInRecord))) {
432 levelCurrent++;
433 } else if (strcmp(s, "class") == 0 || strcmp(s, "object") == 0) {
434 // "class" & "object" keywords require special handling...
435 bool ignoreKeyword = false;
436 Sci_PositionU j = SkipWhiteSpace(currentPos, endPos, styler);
437 if (j < endPos) {
438 CharacterSet setWordStart(CharacterSet::setAlpha, "_");
439 CharacterSet setWord(CharacterSet::setAlphaNum, "_");
440
441 if (styler.SafeGetCharAt(j) == ';') {
442 // Handle forward class declarations ("type TMyClass = class;")
443 // and object method declarations ("TNotifyEvent = procedure(Sender: TObject) of object;")
444 ignoreKeyword = true;
445 } else if (strcmp(s, "class") == 0) {
446 // "class" keyword has a few more special cases...
447 if (styler.SafeGetCharAt(j) == '(') {
448 // Handle simplified complete class declarations ("type TMyClass = class(TObject);")
449 j = SkipWhiteSpace(j, endPos, styler, true);
450 if (j < endPos && styler.SafeGetCharAt(j) == ')') {
451 j = SkipWhiteSpace(j, endPos, styler);
452 if (j < endPos && styler.SafeGetCharAt(j) == ';') {
453 ignoreKeyword = true;
454 }
455 }
456 } else if (setWordStart.Contains(styler.SafeGetCharAt(j))) {
457 char s2[11]; // Size of the longest possible keyword + one additional character + null
458 GetForwardRangeLowered(j, setWord, styler, s2, sizeof(s2));
459
460 if (strcmp(s2, "procedure") == 0 ||
461 strcmp(s2, "function") == 0 ||
462 strcmp(s2, "of") == 0 ||
463 strcmp(s2, "var") == 0 ||
464 strcmp(s2, "property") == 0 ||
465 strcmp(s2, "operator") == 0) {
466 ignoreKeyword = true;
467 }
468 }
469 }
470 }
471 if (!ignoreKeyword) {
472 levelCurrent++;
473 }
474 } else if (strcmp(s, "interface") == 0) {
475 // "interface" keyword requires special handling...
476 bool ignoreKeyword = true;
477 Sci_Position j = lastStart - 1;
478 char ch = styler.SafeGetCharAt(j);
479 while ((j >= startPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n' ||
480 IsStreamCommentStyle(styler.StyleAt(j)))) {
481 j--;
482 ch = styler.SafeGetCharAt(j);
483 }
484 if (j >= startPos && styler.SafeGetCharAt(j) == '=') {
485 ignoreKeyword = false;
486 }
487 if (!ignoreKeyword) {
488 Sci_PositionU k = SkipWhiteSpace(currentPos, endPos, styler);
489 if (k < endPos && styler.SafeGetCharAt(k) == ';') {
490 // Handle forward interface declarations ("type IMyInterface = interface;")
491 ignoreKeyword = true;
492 }
493 }
494 if (!ignoreKeyword) {
495 levelCurrent++;
496 }
497 } else if (strcmp(s, "dispinterface") == 0) {
498 // "dispinterface" keyword requires special handling...
499 bool ignoreKeyword = false;
500 Sci_PositionU j = SkipWhiteSpace(currentPos, endPos, styler);
501 if (j < endPos && styler.SafeGetCharAt(j) == ';') {
502 // Handle forward dispinterface declarations ("type IMyInterface = dispinterface;")
503 ignoreKeyword = true;
504 }
505 if (!ignoreKeyword) {
506 levelCurrent++;
507 }
508 } else if (strcmp(s, "end") == 0) {
509 lineFoldStateCurrent &= ~stateFoldInRecord;
510 levelCurrent--;
511 if (levelCurrent < SC_FOLDLEVELBASE) {
512 levelCurrent = SC_FOLDLEVELBASE;
513 }
514 }
515 }
516
FoldPascalDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * [],Accessor & styler)517 static void FoldPascalDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[],
518 Accessor &styler) {
519 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
520 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
521 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
522 Sci_PositionU endPos = startPos + length;
523 int visibleChars = 0;
524 Sci_Position lineCurrent = styler.GetLine(startPos);
525 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
526 int levelCurrent = levelPrev;
527 int lineFoldStateCurrent = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) & stateFoldMaskAll : 0;
528 char chNext = styler[startPos];
529 int styleNext = styler.StyleAt(startPos);
530 int style = initStyle;
531
532 Sci_Position lastStart = 0;
533 CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true);
534
535 for (Sci_PositionU i = startPos; i < endPos; i++) {
536 char ch = chNext;
537 chNext = styler.SafeGetCharAt(i + 1);
538 int stylePrev = style;
539 style = styleNext;
540 styleNext = styler.StyleAt(i + 1);
541 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
542
543 if (foldComment && IsStreamCommentStyle(style)) {
544 if (!IsStreamCommentStyle(stylePrev)) {
545 levelCurrent++;
546 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
547 // Comments don't end at end of line and the next character may be unstyled.
548 levelCurrent--;
549 }
550 }
551 if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
552 {
553 if (!IsCommentLine(lineCurrent - 1, styler)
554 && IsCommentLine(lineCurrent + 1, styler))
555 levelCurrent++;
556 else if (IsCommentLine(lineCurrent - 1, styler)
557 && !IsCommentLine(lineCurrent+1, styler))
558 levelCurrent--;
559 }
560 if (foldPreprocessor) {
561 if (style == SCE_PAS_PREPROCESSOR && ch == '{' && chNext == '$') {
562 ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 2, styler);
563 } else if (style == SCE_PAS_PREPROCESSOR2 && ch == '(' && chNext == '*'
564 && styler.SafeGetCharAt(i + 2) == '$') {
565 ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 3, styler);
566 }
567 }
568
569 if (stylePrev != SCE_PAS_WORD && style == SCE_PAS_WORD)
570 {
571 // Store last word start point.
572 lastStart = i;
573 }
574 if (stylePrev == SCE_PAS_WORD && !(lineFoldStateCurrent & stateFoldInPreprocessor)) {
575 if(setWord.Contains(ch) && !setWord.Contains(chNext)) {
576 ClassifyPascalWordFoldPoint(levelCurrent, lineFoldStateCurrent, startPos, endPos, lastStart, i, styler);
577 }
578 }
579
580 if (!IsASpace(ch))
581 visibleChars++;
582
583 if (atEOL) {
584 int lev = levelPrev;
585 if (visibleChars == 0 && foldCompact)
586 lev |= SC_FOLDLEVELWHITEFLAG;
587 if ((levelCurrent > levelPrev) && (visibleChars > 0))
588 lev |= SC_FOLDLEVELHEADERFLAG;
589 if (lev != styler.LevelAt(lineCurrent)) {
590 styler.SetLevel(lineCurrent, lev);
591 }
592 int newLineState = (styler.GetLineState(lineCurrent) & ~stateFoldMaskAll) | lineFoldStateCurrent;
593 styler.SetLineState(lineCurrent, newLineState);
594 lineCurrent++;
595 levelPrev = levelCurrent;
596 visibleChars = 0;
597 }
598 }
599
600 // If we didn't reach the EOL in previous loop, store line level and whitespace information.
601 // The rest will be filled in later...
602 int lev = levelPrev;
603 if (visibleChars == 0 && foldCompact)
604 lev |= SC_FOLDLEVELWHITEFLAG;
605 styler.SetLevel(lineCurrent, lev);
606 }
607
608 static const char * const pascalWordListDesc[] = {
609 "Keywords",
610 0
611 };
612
613 LexerModule lmPascal(SCLEX_PASCAL, ColourisePascalDoc, "pascal", FoldPascalDoc, pascalWordListDesc);
614