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