]>
Commit | Line | Data |
---|---|---|
1dcf666d RD |
1 | // Scintilla\ source code edit control |
2 | /** @file LexTCMD.cxx | |
3 | ** Lexer for Take Command / TCC batch scripts (.bat, .btm, .cmd). | |
4 | **/ | |
5 | // Written by Rex Conn (rconn [at] jpsoft [dot] com) | |
6 | // based on the CMD lexer | |
7 | // The License.txt file describes the conditions under which this software may be distributed. | |
8 | ||
9 | #include <stdlib.h> | |
10 | #include <string.h> | |
11 | #include <stdio.h> | |
12 | #include <stdarg.h> | |
13 | #include <assert.h> | |
14 | #include <ctype.h> | |
15 | ||
16 | #include "ILexer.h" | |
17 | #include "Scintilla.h" | |
18 | #include "SciLexer.h" | |
19 | ||
20 | #include "WordList.h" | |
21 | #include "LexAccessor.h" | |
22 | #include "Accessor.h" | |
23 | #include "StyleContext.h" | |
24 | #include "CharacterSet.h" | |
25 | #include "LexerModule.h" | |
26 | ||
27 | #ifdef SCI_NAMESPACE | |
28 | using namespace Scintilla; | |
29 | #endif | |
30 | ||
31 | ||
32 | static bool IsAlphabetic(int ch) { | |
33 | return isascii(ch) && isalpha(ch); | |
34 | } | |
35 | ||
36 | static inline bool AtEOL(Accessor &styler, unsigned int i) { | |
37 | return (styler[i] == '\n') || ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n')); | |
38 | } | |
39 | ||
40 | // Tests for BATCH Operators | |
41 | static bool IsBOperator(char ch) { | |
42 | return (ch == '=') || (ch == '+') || (ch == '>') || (ch == '<') || (ch == '|') || (ch == '&') || (ch == '!') || (ch == '?') || (ch == '*') || (ch == '(') || (ch == ')'); | |
43 | } | |
44 | ||
45 | // Tests for BATCH Separators | |
46 | static bool IsBSeparator(char ch) { | |
47 | return (ch == '\\') || (ch == '.') || (ch == ';') || (ch == ' ') || (ch == '\t') || (ch == '[') || (ch == ']') || (ch == '\"') || (ch == '\'') || (ch == '/'); | |
48 | } | |
49 | ||
50 | // Tests for Environment Variable symbol | |
51 | static inline bool IsEnvironmentVar(char ch) { | |
52 | return isalpha(ch) || isdigit(ch) || (ch == '_') || (ch == '$'); | |
53 | } | |
54 | ||
55 | // Find length of CMD FOR variable with modifier (%~...) or return 0 | |
56 | static unsigned int GetBatchVarLen( char *wordBuffer ) | |
57 | { | |
58 | int nLength = 0; | |
59 | if ( wordBuffer[0] == '%' ) { | |
60 | ||
61 | if ( wordBuffer[1] == '~' ) | |
62 | nLength = 2; | |
63 | else if (( wordBuffer[1] == '%' ) && ( wordBuffer[2] == '~' )) | |
64 | nLength++; | |
65 | else | |
66 | return 0; | |
67 | ||
68 | for ( ; ( wordBuffer[nLength] ); nLength++ ) { | |
69 | ||
70 | switch ( toupper(wordBuffer[nLength]) ) { | |
71 | case 'A': | |
72 | // file attributes | |
73 | case 'D': | |
74 | // drive letter only | |
75 | case 'F': | |
76 | // fully qualified path name | |
77 | case 'N': | |
78 | // filename only | |
79 | case 'P': | |
80 | // path only | |
81 | case 'S': | |
82 | // short name | |
83 | case 'T': | |
84 | // date / time of file | |
85 | case 'X': | |
86 | // file extension only | |
87 | case 'Z': | |
88 | // file size | |
89 | break; | |
90 | default: | |
91 | return nLength; | |
92 | } | |
93 | } | |
94 | } | |
95 | ||
96 | return nLength; | |
97 | } | |
98 | ||
99 | ||
100 | static void ColouriseTCMDLine( char *lineBuffer, unsigned int lengthLine, unsigned int startLine, unsigned int endPos, WordList *keywordlists[], Accessor &styler) | |
101 | { | |
102 | unsigned int offset = 0; // Line Buffer Offset | |
103 | char wordBuffer[260]; // Word Buffer - large to catch long paths | |
104 | unsigned int wbl; // Word Buffer Length | |
105 | unsigned int wbo; // Word Buffer Offset - also Special Keyword Buffer Length | |
106 | WordList &keywords = *keywordlists[0]; // Internal Commands | |
107 | // WordList &keywords2 = *keywordlists[1]; // Aliases (optional) | |
108 | bool isDelayedExpansion = 1; // !var! | |
109 | ||
110 | bool continueProcessing = true; // Used to toggle Regular Keyword Checking | |
111 | // Special Keywords are those that allow certain characters without whitespace after the command | |
112 | // Examples are: cd. cd\ echo: echo. path= | |
113 | bool inString = false; // Used for processing while "" | |
114 | // Special Keyword Buffer used to determine if the first n characters is a Keyword | |
115 | char sKeywordBuffer[260]; // Special Keyword Buffer | |
116 | bool sKeywordFound; // Exit Special Keyword for-loop if found | |
117 | ||
118 | // Skip leading whitespace | |
119 | while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) { | |
120 | offset++; | |
121 | } | |
122 | // Colorize Default Text | |
123 | styler.ColourTo(startLine + offset - 1, SCE_TCMD_DEFAULT); | |
124 | ||
125 | if ( offset >= lengthLine ) | |
126 | return; | |
127 | ||
128 | // Check for Fake Label (Comment) or Real Label - return if found | |
129 | if (lineBuffer[offset] == ':') { | |
130 | if (lineBuffer[offset + 1] == ':') { | |
131 | // Colorize Fake Label (Comment) - :: is the same as REM | |
132 | styler.ColourTo(endPos, SCE_TCMD_COMMENT); | |
133 | } else { | |
134 | // Colorize Real Label | |
135 | styler.ColourTo(endPos, SCE_TCMD_LABEL); | |
136 | } | |
137 | return; | |
138 | ||
139 | // Check for Comment - return if found | |
140 | } else if (( CompareNCaseInsensitive(lineBuffer+offset, "rem", 3) == 0 ) && (( lineBuffer[offset+3] == 0 ) || ( isspace(lineBuffer[offset+3] )))) { | |
141 | styler.ColourTo(endPos, SCE_TCMD_COMMENT); | |
142 | return; | |
143 | ||
144 | // Check for Drive Change (Drive Change is internal command) - return if found | |
145 | } else if ((IsAlphabetic(lineBuffer[offset])) && | |
146 | (lineBuffer[offset + 1] == ':') && | |
147 | ((isspacechar(lineBuffer[offset + 2])) || | |
148 | (((lineBuffer[offset + 2] == '\\')) && | |
149 | (isspacechar(lineBuffer[offset + 3]))))) { | |
150 | // Colorize Regular Keyword | |
151 | styler.ColourTo(endPos, SCE_TCMD_WORD); | |
152 | return; | |
153 | } | |
154 | ||
155 | // Check for Hide Command (@ECHO OFF/ON) | |
156 | if (lineBuffer[offset] == '@') { | |
157 | styler.ColourTo(startLine + offset, SCE_TCMD_HIDE); | |
158 | offset++; | |
159 | } | |
160 | // Skip whitespace | |
161 | while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) { | |
162 | offset++; | |
163 | } | |
164 | ||
165 | // Read remainder of line word-at-a-time or remainder-of-word-at-a-time | |
166 | while (offset < lengthLine) { | |
167 | if (offset > startLine) { | |
168 | // Colorize Default Text | |
169 | styler.ColourTo(startLine + offset - 1, SCE_TCMD_DEFAULT); | |
170 | } | |
171 | // Copy word from Line Buffer into Word Buffer | |
172 | wbl = 0; | |
173 | for (; offset < lengthLine && ( wbl < 260 ) && !isspacechar(lineBuffer[offset]); wbl++, offset++) { | |
174 | wordBuffer[wbl] = static_cast<char>(tolower(lineBuffer[offset])); | |
175 | } | |
176 | wordBuffer[wbl] = '\0'; | |
177 | wbo = 0; | |
178 | ||
179 | // Check for Separator | |
180 | if (IsBSeparator(wordBuffer[0])) { | |
181 | ||
182 | // Reset Offset to re-process remainder of word | |
183 | offset -= (wbl - 1); | |
184 | // Colorize Default Text | |
185 | styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT); | |
186 | ||
187 | if (wordBuffer[0] == '"') | |
188 | inString = !inString; | |
189 | ||
190 | // Check for Regular expression | |
191 | } else if (( wordBuffer[0] == ':' ) && ( wordBuffer[1] == ':' ) && (continueProcessing)) { | |
192 | ||
193 | // Colorize Regular exoressuin | |
194 | styler.ColourTo(startLine + offset - 1, SCE_TCMD_DEFAULT); | |
195 | // No need to Reset Offset | |
196 | ||
197 | // Check for Labels in text (... :label) | |
198 | } else if (wordBuffer[0] == ':' && isspacechar(lineBuffer[offset - wbl - 1])) { | |
199 | // Colorize Default Text | |
200 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_TCMD_DEFAULT); | |
201 | // Colorize Label | |
202 | styler.ColourTo(startLine + offset - 1, SCE_TCMD_CLABEL); | |
203 | // No need to Reset Offset | |
204 | // Check for delayed expansion Variable (!x...!) | |
205 | } else if (isDelayedExpansion && wordBuffer[0] == '!') { | |
206 | // Colorize Default Text | |
207 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_TCMD_DEFAULT); | |
208 | wbo++; | |
209 | // Search to end of word for second ! | |
210 | while ((wbo < wbl) && (wordBuffer[wbo] != '!') && (!IsBOperator(wordBuffer[wbo])) && (!IsBSeparator(wordBuffer[wbo]))) { | |
211 | wbo++; | |
212 | } | |
213 | if (wordBuffer[wbo] == '!') { | |
214 | wbo++; | |
215 | // Colorize Environment Variable | |
216 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_TCMD_EXPANSION); | |
217 | } else { | |
218 | wbo = 1; | |
219 | // Colorize Symbol | |
220 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_TCMD_DEFAULT); | |
221 | } | |
222 | ||
223 | // Reset Offset to re-process remainder of word | |
224 | offset -= (wbl - wbo); | |
225 | ||
226 | // Check for Regular Keyword in list | |
227 | } else if ((keywords.InList(wordBuffer)) && (!inString) && (continueProcessing)) { | |
228 | ||
229 | // ECHO, PATH, and PROMPT require no further Regular Keyword Checking | |
230 | if ((CompareCaseInsensitive(wordBuffer, "echo") == 0) || | |
231 | (CompareCaseInsensitive(sKeywordBuffer, "echos") == 0) || | |
232 | (CompareCaseInsensitive(sKeywordBuffer, "echoerr") == 0) || | |
233 | (CompareCaseInsensitive(sKeywordBuffer, "echoserr") == 0) || | |
234 | (CompareCaseInsensitive(wordBuffer, "path") == 0) || | |
235 | (CompareCaseInsensitive(wordBuffer, "prompt") == 0)) { | |
236 | continueProcessing = false; | |
237 | } | |
238 | ||
239 | // Colorize Regular keyword | |
240 | styler.ColourTo(startLine + offset - 1, SCE_TCMD_WORD); | |
241 | // No need to Reset Offset | |
242 | ||
243 | } else if ((wordBuffer[0] != '%') && (wordBuffer[0] != '!') && (!IsBOperator(wordBuffer[0])) && (!inString) && (continueProcessing)) { | |
244 | ||
245 | // a few commands accept "illegal" syntax -- cd\, echo., etc. | |
246 | sscanf( wordBuffer, "%[^.<>|&=\\/]", sKeywordBuffer ); | |
247 | sKeywordFound = false; | |
248 | ||
249 | if ((CompareCaseInsensitive(sKeywordBuffer, "echo") == 0) || | |
250 | (CompareCaseInsensitive(sKeywordBuffer, "echos") == 0) || | |
251 | (CompareCaseInsensitive(sKeywordBuffer, "echoerr") == 0) || | |
252 | (CompareCaseInsensitive(sKeywordBuffer, "echoserr") == 0) || | |
253 | (CompareCaseInsensitive(sKeywordBuffer, "cd") == 0) || | |
254 | (CompareCaseInsensitive(sKeywordBuffer, "path") == 0) || | |
255 | (CompareCaseInsensitive(sKeywordBuffer, "prompt") == 0)) { | |
256 | ||
257 | // no further Regular Keyword Checking | |
258 | continueProcessing = false; | |
259 | sKeywordFound = true; | |
260 | wbo = (unsigned int)strlen( sKeywordBuffer ); | |
261 | ||
262 | // Colorize Special Keyword as Regular Keyword | |
263 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_TCMD_WORD); | |
264 | // Reset Offset to re-process remainder of word | |
265 | offset -= (wbl - wbo); | |
266 | } | |
267 | ||
268 | // Check for Default Text | |
269 | if (!sKeywordFound) { | |
270 | wbo = 0; | |
271 | // Read up to %, Operator or Separator | |
272 | while ((wbo < wbl) && (wordBuffer[wbo] != '%') && (!isDelayedExpansion || wordBuffer[wbo] != '!') && (!IsBOperator(wordBuffer[wbo])) && (!IsBSeparator(wordBuffer[wbo]))) { | |
273 | wbo++; | |
274 | } | |
275 | // Colorize Default Text | |
276 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_TCMD_DEFAULT); | |
277 | // Reset Offset to re-process remainder of word | |
278 | offset -= (wbl - wbo); | |
279 | } | |
280 | ||
281 | // Check for Argument (%n), Environment Variable (%x...%) or Local Variable (%%a) | |
282 | } else if (wordBuffer[0] == '%') { | |
283 | unsigned int varlen; | |
284 | unsigned int n = 1; | |
285 | // Colorize Default Text | |
286 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_TCMD_DEFAULT); | |
287 | wbo++; | |
288 | ||
289 | // check for %[nn] syntax | |
290 | if ( wordBuffer[1] == '[' ) { | |
291 | n++; | |
292 | while ((n < wbl) && (wordBuffer[n] != ']')) { | |
293 | n++; | |
294 | } | |
295 | if ( wordBuffer[n] == ']' ) | |
296 | n++; | |
297 | goto ColorizeArg; | |
298 | } | |
299 | ||
300 | // Search to end of word for second % or to the first terminator (can be a long path) | |
301 | while ((wbo < wbl) && (wordBuffer[wbo] != '%') && (!IsBOperator(wordBuffer[wbo])) && (!IsBSeparator(wordBuffer[wbo]))) { | |
302 | wbo++; | |
303 | } | |
304 | ||
305 | // Check for Argument (%n) or (%*) | |
306 | if (((isdigit(wordBuffer[1])) || (wordBuffer[1] == '*')) && (wordBuffer[wbo] != '%')) { | |
307 | while (( wordBuffer[n] ) && ( strchr( "%0123456789*#$", wordBuffer[n] ) != NULL )) | |
308 | n++; | |
309 | ColorizeArg: | |
310 | // Colorize Argument | |
311 | styler.ColourTo(startLine + offset - 1 - (wbl - n), SCE_TCMD_IDENTIFIER); | |
312 | // Reset Offset to re-process remainder of word | |
313 | offset -= (wbl - n); | |
314 | ||
315 | // Check for Variable with modifiers (%~...) | |
316 | } else if ((varlen = GetBatchVarLen(wordBuffer)) != 0) { | |
317 | ||
318 | // Colorize Variable | |
319 | styler.ColourTo(startLine + offset - 1 - (wbl - varlen), SCE_TCMD_IDENTIFIER); | |
320 | // Reset Offset to re-process remainder of word | |
321 | offset -= (wbl - varlen); | |
322 | ||
323 | // Check for Environment Variable (%x...%) | |
324 | } else if (( wordBuffer[1] ) && ( wordBuffer[1] != '%')) { | |
325 | if ( wordBuffer[wbo] == '%' ) | |
326 | wbo++; | |
327 | ||
328 | // Colorize Environment Variable | |
329 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_TCMD_ENVIRONMENT); | |
330 | // Reset Offset to re-process remainder of word | |
331 | offset -= (wbl - wbo); | |
332 | ||
333 | // Check for Local Variable (%%a) | |
334 | } else if ( (wbl > 2) && (wordBuffer[1] == '%') && (wordBuffer[2] != '%') && (!IsBOperator(wordBuffer[2])) && (!IsBSeparator(wordBuffer[2]))) { | |
335 | ||
336 | n = 2; | |
337 | while (( wordBuffer[n] ) && (!IsBOperator(wordBuffer[n])) && (!IsBSeparator(wordBuffer[n]))) | |
338 | n++; | |
339 | ||
340 | // Colorize Local Variable | |
341 | styler.ColourTo(startLine + offset - 1 - (wbl - n), SCE_TCMD_IDENTIFIER); | |
342 | // Reset Offset to re-process remainder of word | |
343 | offset -= (wbl - n); | |
344 | ||
345 | // Check for %% | |
346 | } else if ((wbl > 1) && (wordBuffer[1] == '%')) { | |
347 | ||
348 | // Colorize Symbols | |
349 | styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_TCMD_DEFAULT); | |
350 | // Reset Offset to re-process remainder of word | |
351 | offset -= (wbl - 2); | |
352 | } else { | |
353 | ||
354 | // Colorize Symbol | |
355 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_TCMD_DEFAULT); | |
356 | // Reset Offset to re-process remainder of word | |
357 | offset -= (wbl - 1); | |
358 | } | |
359 | ||
360 | // Check for Operator | |
361 | } else if (IsBOperator(wordBuffer[0])) { | |
362 | // Colorize Default Text | |
363 | styler.ColourTo(startLine + offset - 1 - wbl, SCE_TCMD_DEFAULT); | |
364 | ||
365 | // Check for Pipe, compound, or conditional Operator | |
366 | if ((wordBuffer[0] == '|') || (wordBuffer[0] == '&')) { | |
367 | ||
368 | // Colorize Pipe Operator | |
369 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_TCMD_OPERATOR); | |
370 | // Reset Offset to re-process remainder of word | |
371 | offset -= (wbl - 1); | |
372 | continueProcessing = true; | |
373 | ||
374 | // Check for Other Operator | |
375 | } else { | |
376 | // Check for > Operator | |
377 | if ((wordBuffer[0] == '>') || (wordBuffer[0] == '<')) { | |
378 | // Turn Keyword and External Command / Program checking back on | |
379 | continueProcessing = true; | |
380 | } | |
381 | // Colorize Other Operator | |
382 | if (!inString || !(wordBuffer[0] == '(' || wordBuffer[0] == ')')) | |
383 | styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_TCMD_OPERATOR); | |
384 | // Reset Offset to re-process remainder of word | |
385 | offset -= (wbl - 1); | |
386 | } | |
387 | ||
388 | // Check for Default Text | |
389 | } else { | |
390 | // Read up to %, Operator or Separator | |
391 | while ((wbo < wbl) && (wordBuffer[wbo] != '%') && (!isDelayedExpansion || wordBuffer[wbo] != '!') && (!IsBOperator(wordBuffer[wbo])) && (!IsBSeparator(wordBuffer[wbo]))) { | |
392 | wbo++; | |
393 | } | |
394 | // Colorize Default Text | |
395 | styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_TCMD_DEFAULT); | |
396 | // Reset Offset to re-process remainder of word | |
397 | offset -= (wbl - wbo); | |
398 | } | |
399 | ||
400 | // Skip whitespace - nothing happens if Offset was Reset | |
401 | while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) { | |
402 | offset++; | |
403 | } | |
404 | } | |
405 | // Colorize Default Text for remainder of line - currently not lexed | |
406 | styler.ColourTo(endPos, SCE_TCMD_DEFAULT); | |
407 | } | |
408 | ||
409 | static void ColouriseTCMDDoc( unsigned int startPos, int length, int /*initStyle*/, WordList *keywordlists[], Accessor &styler ) | |
410 | { | |
411 | char lineBuffer[16384]; | |
412 | ||
413 | styler.StartAt(startPos); | |
414 | styler.StartSegment(startPos); | |
415 | unsigned int linePos = 0; | |
416 | unsigned int startLine = startPos; | |
417 | for (unsigned int i = startPos; i < startPos + length; i++) { | |
418 | lineBuffer[linePos++] = styler[i]; | |
419 | if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) { | |
420 | // End of line (or of line buffer) met, colourise it | |
421 | lineBuffer[linePos] = '\0'; | |
422 | ColouriseTCMDLine(lineBuffer, linePos, startLine, i, keywordlists, styler); | |
423 | linePos = 0; | |
424 | startLine = i + 1; | |
425 | } | |
426 | } | |
427 | if (linePos > 0) { // Last line does not have ending characters | |
428 | lineBuffer[linePos] = '\0'; | |
429 | ColouriseTCMDLine(lineBuffer, linePos, startLine, startPos + length - 1, keywordlists, styler); | |
430 | } | |
431 | } | |
432 | ||
433 | // Convert string to upper case | |
434 | static void StrUpr(char *s) { | |
435 | while (*s) { | |
436 | *s = MakeUpperCase(*s); | |
437 | s++; | |
438 | } | |
439 | } | |
440 | ||
441 | // Folding support (for DO, IFF, SWITCH, TEXT, and command groups) | |
442 | static void FoldTCMDDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) | |
443 | { | |
444 | int line = styler.GetLine(startPos); | |
445 | int level = styler.LevelAt(line); | |
446 | int levelIndent = 0; | |
447 | unsigned int endPos = startPos + length; | |
448 | char s[16]; | |
449 | ||
450 | char chPrev = styler.SafeGetCharAt(startPos - 1); | |
451 | ||
452 | // Scan for ( and ) | |
453 | for (unsigned int i = startPos; i < endPos; i++) { | |
454 | ||
455 | int c = styler.SafeGetCharAt(i, '\n'); | |
456 | int style = styler.StyleAt(i); | |
457 | bool bLineStart = ((chPrev == '\r') || (chPrev == '\n')) || i == 0; | |
458 | ||
459 | if (style == SCE_TCMD_OPERATOR) { | |
460 | // CheckFoldPoint | |
461 | if (c == '(') { | |
462 | levelIndent += 1; | |
463 | } else if (c == ')') { | |
464 | levelIndent -= 1; | |
465 | } | |
466 | } | |
467 | ||
468 | if (( bLineStart ) && ( style == SCE_TCMD_WORD )) { | |
469 | for (unsigned int j = 0; j < 10; j++) { | |
470 | if (!iswordchar(styler[i + j])) { | |
471 | break; | |
472 | } | |
473 | s[j] = styler[i + j]; | |
474 | s[j + 1] = '\0'; | |
475 | } | |
476 | ||
477 | StrUpr( s ); | |
478 | if ((strcmp(s, "DO") == 0) || (strcmp(s, "IFF") == 0) || (strcmp(s, "SWITCH") == 0) || (strcmp(s, "TEXT") == 0)) { | |
479 | levelIndent++; | |
480 | } else if ((strcmp(s, "ENDDO") == 0) || (strcmp(s, "ENDIFF") == 0) || (strcmp(s, "ENDSWITCH") == 0) || (strcmp(s, "ENDTEXT") == 0)) { | |
481 | levelIndent--; | |
482 | } | |
483 | } | |
484 | ||
485 | if (c == '\n') { // line end | |
486 | if (levelIndent > 0) { | |
487 | level |= SC_FOLDLEVELHEADERFLAG; | |
488 | } | |
489 | if (level != styler.LevelAt(line)) | |
490 | styler.SetLevel(line, level); | |
491 | level += levelIndent; | |
492 | if ((level & SC_FOLDLEVELNUMBERMASK) < SC_FOLDLEVELBASE) | |
493 | level = SC_FOLDLEVELBASE; | |
494 | line++; | |
495 | // reset state | |
496 | levelIndent = 0; | |
497 | level &= ~SC_FOLDLEVELHEADERFLAG; | |
498 | level &= ~SC_FOLDLEVELWHITEFLAG; | |
499 | } | |
500 | ||
501 | chPrev = c; | |
502 | } | |
503 | } | |
504 | ||
505 | static const char *const tcmdWordListDesc[] = { | |
506 | "Internal Commands", | |
507 | "Aliases", | |
508 | 0 | |
509 | }; | |
510 | ||
511 | LexerModule lmTCMD(SCLEX_TCMD, ColouriseTCMDDoc, "tcmd", FoldTCMDDoc, tcmdWordListDesc); |