updated Scintilla to 2.29
[TortoiseGit.git] / ext / scintilla / lexers / LexPascal.cxx
blob9c55bdeb01238204b11d0ade9a28338416d33a2e
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 **/
12 A few words about features of the new completely rewritten LexPascal...
14 Generally speaking LexPascal tries to support all available Delphi features (up
15 to Delphi 2009 at this time), including .NET specific features.
17 ~ HIGHLIGHTING:
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).
24 For example, keywords "read" and "write" will only be highlighted if they are in
25 property declaration:
27 property MyProperty: boolean read FMyProperty write FMyProperty;
29 ~ FOLDING:
31 Folding is supported in the following cases:
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)
42 Remarks:
44 - Folding of code blocks tries to handle all special cases in which folding
45 should not occur. As implemented those are:
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")
58 - Folding of code blocks inside preprocessor blocks is disabled (any comments
59 inside them will be folded fine) because there is no guarantee that complete
60 code block will be contained inside folded preprocessor block in which case
61 folded code block could end prematurely at the end of preprocessor block if
62 there is no closing statement inside. This was done in order to properly process
63 document that may contain something like this:
65 type
66 {$IFDEF UNICODE}
67 TMyClass = class(UnicodeAncestor)
68 {$ELSE}
69 TMyClass = class(AnsiAncestor)
70 {$ENDIF}
71 private
72 ...
73 public
74 ...
75 published
76 ...
77 end;
79 If class declarations were folded, then the second class declaration would end
80 at "$ENDIF" statement, first class statement would end at "end;" statement and
81 preprocessor "$IFDEF" block would go all the way to the end of document.
82 However, having in mind all this, if you want to enable folding of code blocks
83 inside preprocessor blocks, you can disable folding of preprocessor blocks by
84 changing "fold.preprocessor" property, in which case everything inside them
85 would be folded.
87 ~ KEYWORDS:
89 The list of keywords that can be used in pascal.properties file (up to Delphi
90 2009):
92 - Keywords: absolute abstract and array as asm assembler automated begin case
93 cdecl class const constructor deprecated destructor dispid dispinterface div do
94 downto dynamic else end except export exports external far file final
95 finalization finally for forward function goto if implementation in inherited
96 initialization inline interface is label library message mod near nil not object
97 of on or out overload override packed pascal platform private procedure program
98 property protected public published raise record register reintroduce repeat
99 resourcestring safecall sealed set shl shr static stdcall strict string then
100 threadvar to try type unit unsafe until uses var varargs virtual while with xor
102 - Keywords related to the "smart highlithing" feature: add default implements
103 index name nodefault read readonly remove stored write writeonly
105 - Keywords related to Delphi packages (in addition to all above): package
106 contains requires
110 #include <stdlib.h>
111 #include <string.h>
112 #include <stdio.h>
113 #include <stdarg.h>
114 #include <assert.h>
115 #include <ctype.h>
117 #include "ILexer.h"
118 #include "Scintilla.h"
119 #include "SciLexer.h"
121 #include "WordList.h"
122 #include "LexAccessor.h"
123 #include "Accessor.h"
124 #include "StyleContext.h"
125 #include "CharacterSet.h"
126 #include "LexerModule.h"
128 #ifdef SCI_NAMESPACE
129 using namespace Scintilla;
130 #endif
132 static void GetRangeLowered(unsigned int start,
133 unsigned int end,
134 Accessor &styler,
135 char *s,
136 unsigned int len) {
137 unsigned int i = 0;
138 while ((i < end - start + 1) && (i < len-1)) {
139 s[i] = static_cast<char>(tolower(styler[start + i]));
140 i++;
142 s[i] = '\0';
145 static void GetForwardRangeLowered(unsigned int start,
146 CharacterSet &charSet,
147 Accessor &styler,
148 char *s,
149 unsigned int len) {
150 unsigned int i = 0;
151 while ((i < len-1) && charSet.Contains(styler.SafeGetCharAt(start + i))) {
152 s[i] = static_cast<char>(tolower(styler.SafeGetCharAt(start + i)));
153 i++;
155 s[i] = '\0';
159 enum {
160 stateInAsm = 0x1000,
161 stateInProperty = 0x2000,
162 stateInExport = 0x4000,
163 stateFoldInPreprocessor = 0x0100,
164 stateFoldInRecord = 0x0200,
165 stateFoldInPreprocessorLevelMask = 0x00FF,
166 stateFoldMaskAll = 0x0FFF
169 static void ClassifyPascalWord(WordList *keywordlists[], StyleContext &sc, int &curLineState, bool bSmartHighlighting) {
170 WordList& keywords = *keywordlists[0];
172 char s[100];
173 sc.GetCurrentLowered(s, sizeof(s));
174 if (keywords.InList(s)) {
175 if (curLineState & stateInAsm) {
176 if (strcmp(s, "end") == 0 && sc.GetRelative(-4) != '@') {
177 curLineState &= ~stateInAsm;
178 sc.ChangeState(SCE_PAS_WORD);
179 } else {
180 sc.ChangeState(SCE_PAS_ASM);
182 } else {
183 bool ignoreKeyword = false;
184 if (strcmp(s, "asm") == 0) {
185 curLineState |= stateInAsm;
186 } else if (bSmartHighlighting) {
187 if (strcmp(s, "property") == 0) {
188 curLineState |= stateInProperty;
189 } else if (strcmp(s, "exports") == 0) {
190 curLineState |= stateInExport;
191 } else if (!(curLineState & (stateInProperty | stateInExport)) && strcmp(s, "index") == 0) {
192 ignoreKeyword = true;
193 } else if (!(curLineState & stateInExport) && strcmp(s, "name") == 0) {
194 ignoreKeyword = true;
195 } else if (!(curLineState & stateInProperty) &&
196 (strcmp(s, "read") == 0 || strcmp(s, "write") == 0 ||
197 strcmp(s, "default") == 0 || strcmp(s, "nodefault") == 0 ||
198 strcmp(s, "stored") == 0 || strcmp(s, "implements") == 0 ||
199 strcmp(s, "readonly") == 0 || strcmp(s, "writeonly") == 0 ||
200 strcmp(s, "add") == 0 || strcmp(s, "remove") == 0)) {
201 ignoreKeyword = true;
204 if (!ignoreKeyword) {
205 sc.ChangeState(SCE_PAS_WORD);
208 } else if (curLineState & stateInAsm) {
209 sc.ChangeState(SCE_PAS_ASM);
211 sc.SetState(SCE_PAS_DEFAULT);
214 static void ColourisePascalDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
215 Accessor &styler) {
216 bool bSmartHighlighting = styler.GetPropertyInt("lexer.pascal.smart.highlighting", 1) != 0;
218 CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
219 CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true);
220 CharacterSet setNumber(CharacterSet::setDigits, ".-+eE");
221 CharacterSet setHexNumber(CharacterSet::setDigits, "abcdefABCDEF");
222 CharacterSet setOperator(CharacterSet::setNone, "#$&'()*+,-./:;<=>@[]^{}");
224 int curLine = styler.GetLine(startPos);
225 int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0;
227 StyleContext sc(startPos, length, initStyle, styler);
229 for (; sc.More(); sc.Forward()) {
230 if (sc.atLineEnd) {
231 // Update the line state, so it can be seen by next line
232 curLine = styler.GetLine(sc.currentPos);
233 styler.SetLineState(curLine, curLineState);
236 // Determine if the current state should terminate.
237 switch (sc.state) {
238 case SCE_PAS_NUMBER:
239 if (!setNumber.Contains(sc.ch) || (sc.ch == '.' && sc.chNext == '.')) {
240 sc.SetState(SCE_PAS_DEFAULT);
241 } else if (sc.ch == '-' || sc.ch == '+') {
242 if (sc.chPrev != 'E' && sc.chPrev != 'e') {
243 sc.SetState(SCE_PAS_DEFAULT);
246 break;
247 case SCE_PAS_IDENTIFIER:
248 if (!setWord.Contains(sc.ch)) {
249 ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting);
251 break;
252 case SCE_PAS_HEXNUMBER:
253 if (!setHexNumber.Contains(sc.ch)) {
254 sc.SetState(SCE_PAS_DEFAULT);
256 break;
257 case SCE_PAS_COMMENT:
258 case SCE_PAS_PREPROCESSOR:
259 if (sc.ch == '}') {
260 sc.ForwardSetState(SCE_PAS_DEFAULT);
262 break;
263 case SCE_PAS_COMMENT2:
264 case SCE_PAS_PREPROCESSOR2:
265 if (sc.Match('*', ')')) {
266 sc.Forward();
267 sc.ForwardSetState(SCE_PAS_DEFAULT);
269 break;
270 case SCE_PAS_COMMENTLINE:
271 if (sc.atLineStart) {
272 sc.SetState(SCE_PAS_DEFAULT);
274 break;
275 case SCE_PAS_STRING:
276 if (sc.atLineEnd) {
277 sc.ChangeState(SCE_PAS_STRINGEOL);
278 } else if (sc.ch == '\'' && sc.chNext == '\'') {
279 sc.Forward();
280 } else if (sc.ch == '\'') {
281 sc.ForwardSetState(SCE_PAS_DEFAULT);
283 break;
284 case SCE_PAS_STRINGEOL:
285 if (sc.atLineStart) {
286 sc.SetState(SCE_PAS_DEFAULT);
288 break;
289 case SCE_PAS_CHARACTER:
290 if (!setHexNumber.Contains(sc.ch) && sc.ch != '$') {
291 sc.SetState(SCE_PAS_DEFAULT);
293 break;
294 case SCE_PAS_OPERATOR:
295 if (bSmartHighlighting && sc.chPrev == ';') {
296 curLineState &= ~(stateInProperty | stateInExport);
298 sc.SetState(SCE_PAS_DEFAULT);
299 break;
300 case SCE_PAS_ASM:
301 sc.SetState(SCE_PAS_DEFAULT);
302 break;
305 // Determine if a new state should be entered.
306 if (sc.state == SCE_PAS_DEFAULT) {
307 if (IsADigit(sc.ch) && !(curLineState & stateInAsm)) {
308 sc.SetState(SCE_PAS_NUMBER);
309 } else if (setWordStart.Contains(sc.ch)) {
310 sc.SetState(SCE_PAS_IDENTIFIER);
311 } else if (sc.ch == '$' && !(curLineState & stateInAsm)) {
312 sc.SetState(SCE_PAS_HEXNUMBER);
313 } else if (sc.Match('{', '$')) {
314 sc.SetState(SCE_PAS_PREPROCESSOR);
315 } else if (sc.ch == '{') {
316 sc.SetState(SCE_PAS_COMMENT);
317 } else if (sc.Match("(*$")) {
318 sc.SetState(SCE_PAS_PREPROCESSOR2);
319 } else if (sc.Match('(', '*')) {
320 sc.SetState(SCE_PAS_COMMENT2);
321 sc.Forward(); // Eat the * so it isn't used for the end of the comment
322 } else if (sc.Match('/', '/')) {
323 sc.SetState(SCE_PAS_COMMENTLINE);
324 } else if (sc.ch == '\'') {
325 sc.SetState(SCE_PAS_STRING);
326 } else if (sc.ch == '#') {
327 sc.SetState(SCE_PAS_CHARACTER);
328 } else if (setOperator.Contains(sc.ch) && !(curLineState & stateInAsm)) {
329 sc.SetState(SCE_PAS_OPERATOR);
330 } else if (curLineState & stateInAsm) {
331 sc.SetState(SCE_PAS_ASM);
336 if (sc.state == SCE_PAS_IDENTIFIER && setWord.Contains(sc.chPrev)) {
337 ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting);
340 sc.Complete();
343 static bool IsStreamCommentStyle(int style) {
344 return style == SCE_PAS_COMMENT || style == SCE_PAS_COMMENT2;
347 static bool IsCommentLine(int line, Accessor &styler) {
348 int pos = styler.LineStart(line);
349 int eolPos = styler.LineStart(line + 1) - 1;
350 for (int i = pos; i < eolPos; i++) {
351 char ch = styler[i];
352 char chNext = styler.SafeGetCharAt(i + 1);
353 int style = styler.StyleAt(i);
354 if (ch == '/' && chNext == '/' && style == SCE_PAS_COMMENTLINE) {
355 return true;
356 } else if (!IsASpaceOrTab(ch)) {
357 return false;
360 return false;
363 static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) {
364 return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask;
367 static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) {
368 lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask;
369 lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask;
372 static void ClassifyPascalPreprocessorFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,
373 unsigned int startPos, Accessor &styler) {
374 CharacterSet setWord(CharacterSet::setAlpha);
376 char s[11]; // Size of the longest possible keyword + one additional character + null
377 GetForwardRangeLowered(startPos, setWord, styler, s, sizeof(s));
379 unsigned int nestLevel = GetFoldInPreprocessorLevelFlag(lineFoldStateCurrent);
381 if (strcmp(s, "if") == 0 ||
382 strcmp(s, "ifdef") == 0 ||
383 strcmp(s, "ifndef") == 0 ||
384 strcmp(s, "ifopt") == 0 ||
385 strcmp(s, "region") == 0) {
386 nestLevel++;
387 SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel);
388 lineFoldStateCurrent |= stateFoldInPreprocessor;
389 levelCurrent++;
390 } else if (strcmp(s, "endif") == 0 ||
391 strcmp(s, "ifend") == 0 ||
392 strcmp(s, "endregion") == 0) {
393 nestLevel--;
394 SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel);
395 if (nestLevel == 0) {
396 lineFoldStateCurrent &= ~stateFoldInPreprocessor;
398 levelCurrent--;
399 if (levelCurrent < SC_FOLDLEVELBASE) {
400 levelCurrent = SC_FOLDLEVELBASE;
405 static unsigned int SkipWhiteSpace(unsigned int currentPos, unsigned int endPos,
406 Accessor &styler, bool includeChars = false) {
407 CharacterSet setWord(CharacterSet::setAlphaNum, "_");
408 unsigned int j = currentPos + 1;
409 char ch = styler.SafeGetCharAt(j);
410 while ((j < endPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n' ||
411 IsStreamCommentStyle(styler.StyleAt(j)) || (includeChars && setWord.Contains(ch)))) {
412 j++;
413 ch = styler.SafeGetCharAt(j);
415 return j;
418 static void ClassifyPascalWordFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,
419 int startPos, unsigned int endPos,
420 unsigned int lastStart, unsigned int currentPos, Accessor &styler) {
421 char s[100];
422 GetRangeLowered(lastStart, currentPos, styler, s, sizeof(s));
424 if (strcmp(s, "record") == 0) {
425 lineFoldStateCurrent |= stateFoldInRecord;
426 levelCurrent++;
427 } else if (strcmp(s, "begin") == 0 ||
428 strcmp(s, "asm") == 0 ||
429 strcmp(s, "try") == 0 ||
430 (strcmp(s, "case") == 0 && !(lineFoldStateCurrent & stateFoldInRecord))) {
431 levelCurrent++;
432 } else if (strcmp(s, "class") == 0 || strcmp(s, "object") == 0) {
433 // "class" & "object" keywords require special handling...
434 bool ignoreKeyword = false;
435 unsigned int j = SkipWhiteSpace(currentPos, endPos, styler);
436 if (j < endPos) {
437 CharacterSet setWordStart(CharacterSet::setAlpha, "_");
438 CharacterSet setWord(CharacterSet::setAlphaNum, "_");
440 if (styler.SafeGetCharAt(j) == ';') {
441 // Handle forward class declarations ("type TMyClass = class;")
442 // and object method declarations ("TNotifyEvent = procedure(Sender: TObject) of object;")
443 ignoreKeyword = true;
444 } else if (strcmp(s, "class") == 0) {
445 // "class" keyword has a few more special cases...
446 if (styler.SafeGetCharAt(j) == '(') {
447 // Handle simplified complete class declarations ("type TMyClass = class(TObject);")
448 j = SkipWhiteSpace(j, endPos, styler, true);
449 if (j < endPos && styler.SafeGetCharAt(j) == ')') {
450 j = SkipWhiteSpace(j, endPos, styler);
451 if (j < endPos && styler.SafeGetCharAt(j) == ';') {
452 ignoreKeyword = true;
455 } else if (setWordStart.Contains(styler.SafeGetCharAt(j))) {
456 char s2[11]; // Size of the longest possible keyword + one additional character + null
457 GetForwardRangeLowered(j, setWord, styler, s2, sizeof(s2));
459 if (strcmp(s2, "procedure") == 0 ||
460 strcmp(s2, "function") == 0 ||
461 strcmp(s2, "of") == 0 ||
462 strcmp(s2, "var") == 0 ||
463 strcmp(s2, "property") == 0 ||
464 strcmp(s2, "operator") == 0) {
465 ignoreKeyword = true;
470 if (!ignoreKeyword) {
471 levelCurrent++;
473 } else if (strcmp(s, "interface") == 0) {
474 // "interface" keyword requires special handling...
475 bool ignoreKeyword = true;
476 int j = lastStart - 1;
477 char ch = styler.SafeGetCharAt(j);
478 while ((j >= startPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n' ||
479 IsStreamCommentStyle(styler.StyleAt(j)))) {
480 j--;
481 ch = styler.SafeGetCharAt(j);
483 if (j >= startPos && styler.SafeGetCharAt(j) == '=') {
484 ignoreKeyword = false;
486 if (!ignoreKeyword) {
487 levelCurrent++;
489 } else if (strcmp(s, "end") == 0) {
490 lineFoldStateCurrent &= ~stateFoldInRecord;
491 levelCurrent--;
492 if (levelCurrent < SC_FOLDLEVELBASE) {
493 levelCurrent = SC_FOLDLEVELBASE;
498 static void FoldPascalDoc(unsigned int startPos, int length, int initStyle, WordList *[],
499 Accessor &styler) {
500 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
501 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
502 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
503 unsigned int endPos = startPos + length;
504 int visibleChars = 0;
505 int lineCurrent = styler.GetLine(startPos);
506 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
507 int levelCurrent = levelPrev;
508 int lineFoldStateCurrent = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) & stateFoldMaskAll : 0;
509 char chNext = styler[startPos];
510 int styleNext = styler.StyleAt(startPos);
511 int style = initStyle;
513 int lastStart = 0;
514 CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true);
516 for (unsigned int i = startPos; i < endPos; i++) {
517 char ch = chNext;
518 chNext = styler.SafeGetCharAt(i + 1);
519 int stylePrev = style;
520 style = styleNext;
521 styleNext = styler.StyleAt(i + 1);
522 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
524 if (foldComment && IsStreamCommentStyle(style)) {
525 if (!IsStreamCommentStyle(stylePrev)) {
526 levelCurrent++;
527 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
528 // Comments don't end at end of line and the next character may be unstyled.
529 levelCurrent--;
532 if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
534 if (!IsCommentLine(lineCurrent - 1, styler)
535 && IsCommentLine(lineCurrent + 1, styler))
536 levelCurrent++;
537 else if (IsCommentLine(lineCurrent - 1, styler)
538 && !IsCommentLine(lineCurrent+1, styler))
539 levelCurrent--;
541 if (foldPreprocessor) {
542 if (style == SCE_PAS_PREPROCESSOR && ch == '{' && chNext == '$') {
543 ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 2, styler);
544 } else if (style == SCE_PAS_PREPROCESSOR2 && ch == '(' && chNext == '*'
545 && styler.SafeGetCharAt(i + 2) == '$') {
546 ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 3, styler);
550 if (stylePrev != SCE_PAS_WORD && style == SCE_PAS_WORD)
552 // Store last word start point.
553 lastStart = i;
555 if (stylePrev == SCE_PAS_WORD && !(lineFoldStateCurrent & stateFoldInPreprocessor)) {
556 if(setWord.Contains(ch) && !setWord.Contains(chNext)) {
557 ClassifyPascalWordFoldPoint(levelCurrent, lineFoldStateCurrent, startPos, endPos, lastStart, i, styler);
561 if (!IsASpace(ch))
562 visibleChars++;
564 if (atEOL) {
565 int lev = levelPrev;
566 if (visibleChars == 0 && foldCompact)
567 lev |= SC_FOLDLEVELWHITEFLAG;
568 if ((levelCurrent > levelPrev) && (visibleChars > 0))
569 lev |= SC_FOLDLEVELHEADERFLAG;
570 if (lev != styler.LevelAt(lineCurrent)) {
571 styler.SetLevel(lineCurrent, lev);
573 int newLineState = (styler.GetLineState(lineCurrent) & ~stateFoldMaskAll) | lineFoldStateCurrent;
574 styler.SetLineState(lineCurrent, newLineState);
575 lineCurrent++;
576 levelPrev = levelCurrent;
577 visibleChars = 0;
581 // If we didn't reach the EOL in previous loop, store line level and whitespace information.
582 // The rest will be filled in later...
583 int lev = levelPrev;
584 if (visibleChars == 0 && foldCompact)
585 lev |= SC_FOLDLEVELWHITEFLAG;
586 styler.SetLevel(lineCurrent, lev);
589 static const char * const pascalWordListDesc[] = {
590 "Keywords",
594 LexerModule lmPascal(SCLEX_PASCAL, ColourisePascalDoc, "pascal", FoldPascalDoc, pascalWordListDesc);