~efargaspro/+junk/codeblocks-16.01-release

« back to all changes in this revision

Viewing changes to src/sdk/wxscintilla/src/scintilla/lexers/LexPascal.cxx

  • Committer: damienlmoore at gmail
  • Date: 2016-02-02 02:43:22 UTC
  • Revision ID: damienlmoore@gmail.com-20160202024322-yql5qmtbwdyamdwd
Code::BlocksĀ 16.01

Show diffs side-by-side

added added

removed removed

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