]>
Commit | Line | Data |
---|---|---|
1 | // Scintilla source code edit control | |
2 | /** @file LexECL.cxx | |
3 | ** Lexer for ECL. | |
4 | **/ | |
5 | // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> | |
6 | // The License.txt file describes the conditions under which this software may be distributed. | |
7 | ||
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <ctype.h> | |
11 | #include <stdio.h> | |
12 | #include <stdarg.h> | |
13 | #include <assert.h> | |
14 | ||
15 | #ifdef _MSC_VER | |
16 | #pragma warning(disable: 4786) | |
17 | #endif | |
18 | #ifdef __BORLANDC__ | |
19 | // Borland C++ displays warnings in vector header without this | |
20 | #pragma option -w-ccc -w-rch | |
21 | #endif | |
22 | ||
23 | #include <string> | |
24 | #include <vector> | |
25 | #include <map> | |
26 | #include <algorithm> | |
27 | ||
28 | #include "ILexer.h" | |
29 | #include "Scintilla.h" | |
30 | #include "SciLexer.h" | |
31 | ||
32 | #include "PropSetSimple.h" | |
33 | #include "WordList.h" | |
34 | #include "LexAccessor.h" | |
35 | #include "Accessor.h" | |
36 | #include "StyleContext.h" | |
37 | #include "CharacterSet.h" | |
38 | #include "LexerModule.h" | |
39 | #include "OptionSet.h" | |
40 | ||
41 | #define SET_LOWER "abcdefghijklmnopqrstuvwxyz" | |
42 | #define SET_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
43 | #define SET_DIGITS "0123456789" | |
44 | ||
45 | #ifdef SCI_NAMESPACE | |
46 | using namespace Scintilla; | |
47 | #endif | |
48 | ||
49 | static bool IsSpaceEquiv(int state) { | |
50 | return (state <= SCE_ECL_COMMENTDOC) || | |
51 | // including SCE_ECL_DEFAULT, SCE_ECL_COMMENT, SCE_ECL_COMMENTLINE | |
52 | (state == SCE_ECL_COMMENTLINEDOC) || (state == SCE_ECL_COMMENTDOCKEYWORD) || | |
53 | (state == SCE_ECL_COMMENTDOCKEYWORDERROR); | |
54 | } | |
55 | ||
56 | static void ColouriseEclDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], | |
57 | Accessor &styler) { | |
58 | WordList &keywords0 = *keywordlists[0]; | |
59 | WordList &keywords1 = *keywordlists[1]; | |
60 | WordList &keywords2 = *keywordlists[2]; | |
61 | WordList &keywords3 = *keywordlists[3]; //Value Types | |
62 | WordList &keywords4 = *keywordlists[4]; | |
63 | WordList &keywords5 = *keywordlists[5]; | |
64 | WordList &keywords6 = *keywordlists[6]; //Javadoc Tags | |
65 | WordList cplusplus; | |
66 | cplusplus.Set("beginc endc"); | |
67 | ||
68 | bool stylingWithinPreprocessor = false; | |
69 | ||
70 | CharacterSet setOKBeforeRE(CharacterSet::setNone, "(=,"); | |
71 | CharacterSet setDoxygen(CharacterSet::setLower, "$@\\&<>#{}[]"); | |
72 | CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true); | |
73 | CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true); | |
74 | CharacterSet setQualified(CharacterSet::setNone, "uUxX"); | |
75 | ||
76 | int chPrevNonWhite = ' '; | |
77 | int visibleChars = 0; | |
78 | bool lastWordWasUUID = false; | |
79 | int styleBeforeDCKeyword = SCE_ECL_DEFAULT; | |
80 | bool continuationLine = false; | |
81 | ||
82 | if (initStyle == SCE_ECL_PREPROCESSOR) { | |
83 | // Set continuationLine if last character of previous line is '\' | |
84 | int lineCurrent = styler.GetLine(startPos); | |
85 | if (lineCurrent > 0) { | |
86 | int chBack = styler.SafeGetCharAt(startPos-1, 0); | |
87 | int chBack2 = styler.SafeGetCharAt(startPos-2, 0); | |
88 | int lineEndChar = '!'; | |
89 | if (chBack2 == '\r' && chBack == '\n') { | |
90 | lineEndChar = styler.SafeGetCharAt(startPos-3, 0); | |
91 | } else if (chBack == '\n' || chBack == '\r') { | |
92 | lineEndChar = chBack2; | |
93 | } | |
94 | continuationLine = lineEndChar == '\\'; | |
95 | } | |
96 | } | |
97 | ||
98 | // look back to set chPrevNonWhite properly for better regex colouring | |
99 | if (startPos > 0) { | |
100 | int back = startPos; | |
101 | while (--back && IsSpaceEquiv(styler.StyleAt(back))) | |
102 | ; | |
103 | if (styler.StyleAt(back) == SCE_ECL_OPERATOR) { | |
104 | chPrevNonWhite = styler.SafeGetCharAt(back); | |
105 | } | |
106 | } | |
107 | ||
108 | StyleContext sc(startPos, length, initStyle, styler); | |
109 | ||
110 | for (; sc.More(); sc.Forward()) { | |
111 | if (sc.atLineStart) { | |
112 | if (sc.state == SCE_ECL_STRING) { | |
113 | // Prevent SCE_ECL_STRINGEOL from leaking back to previous line which | |
114 | // ends with a line continuation by locking in the state upto this position. | |
115 | sc.SetState(SCE_ECL_STRING); | |
116 | } | |
117 | // Reset states to begining of colourise so no surprises | |
118 | // if different sets of lines lexed. | |
119 | visibleChars = 0; | |
120 | lastWordWasUUID = false; | |
121 | } | |
122 | ||
123 | // Handle line continuation generically. | |
124 | if (sc.ch == '\\') { | |
125 | if (sc.chNext == '\n' || sc.chNext == '\r') { | |
126 | sc.Forward(); | |
127 | if (sc.ch == '\r' && sc.chNext == '\n') { | |
128 | sc.Forward(); | |
129 | } | |
130 | continuationLine = true; | |
131 | continue; | |
132 | } | |
133 | } | |
134 | ||
135 | // Determine if the current state should terminate. | |
136 | switch (sc.state) { | |
137 | case SCE_ECL_ADDED: | |
138 | case SCE_ECL_DELETED: | |
139 | case SCE_ECL_CHANGED: | |
140 | case SCE_ECL_MOVED: | |
141 | if (sc.atLineStart) | |
142 | sc.SetState(SCE_ECL_DEFAULT); | |
143 | break; | |
144 | case SCE_ECL_OPERATOR: | |
145 | sc.SetState(SCE_ECL_DEFAULT); | |
146 | break; | |
147 | case SCE_ECL_NUMBER: | |
148 | // We accept almost anything because of hex. and number suffixes | |
149 | if (!setWord.Contains(sc.ch)) { | |
150 | sc.SetState(SCE_ECL_DEFAULT); | |
151 | } | |
152 | break; | |
153 | case SCE_ECL_IDENTIFIER: | |
154 | if (!setWord.Contains(sc.ch) || (sc.ch == '.')) { | |
155 | char s[1000]; | |
156 | sc.GetCurrentLowered(s, sizeof(s)); | |
157 | if (keywords0.InList(s)) { | |
158 | lastWordWasUUID = strcmp(s, "uuid") == 0; | |
159 | sc.ChangeState(SCE_ECL_WORD0); | |
160 | } else if (keywords1.InList(s)) { | |
161 | sc.ChangeState(SCE_ECL_WORD1); | |
162 | } else if (keywords2.InList(s)) { | |
163 | sc.ChangeState(SCE_ECL_WORD2); | |
164 | } else if (keywords4.InList(s)) { | |
165 | sc.ChangeState(SCE_ECL_WORD4); | |
166 | } else if (keywords5.InList(s)) { | |
167 | sc.ChangeState(SCE_ECL_WORD5); | |
168 | } | |
169 | else //Data types are of from KEYWORD## | |
170 | { | |
171 | int i = static_cast<int>(strlen(s)) - 1; | |
172 | while(i >= 0 && (isdigit(s[i]) || s[i] == '_')) | |
173 | --i; | |
174 | ||
175 | char s2[1000]; | |
176 | strncpy(s2, s, i + 1); | |
177 | s2[i + 1] = 0; | |
178 | if (keywords3.InList(s2)) { | |
179 | sc.ChangeState(SCE_ECL_WORD3); | |
180 | } | |
181 | } | |
182 | sc.SetState(SCE_ECL_DEFAULT); | |
183 | } | |
184 | break; | |
185 | case SCE_ECL_PREPROCESSOR: | |
186 | if (sc.atLineStart && !continuationLine) { | |
187 | sc.SetState(SCE_ECL_DEFAULT); | |
188 | } else if (stylingWithinPreprocessor) { | |
189 | if (IsASpace(sc.ch)) { | |
190 | sc.SetState(SCE_ECL_DEFAULT); | |
191 | } | |
192 | } else { | |
193 | if (sc.Match('/', '*') || sc.Match('/', '/')) { | |
194 | sc.SetState(SCE_ECL_DEFAULT); | |
195 | } | |
196 | } | |
197 | break; | |
198 | case SCE_ECL_COMMENT: | |
199 | if (sc.Match('*', '/')) { | |
200 | sc.Forward(); | |
201 | sc.ForwardSetState(SCE_ECL_DEFAULT); | |
202 | } | |
203 | break; | |
204 | case SCE_ECL_COMMENTDOC: | |
205 | if (sc.Match('*', '/')) { | |
206 | sc.Forward(); | |
207 | sc.ForwardSetState(SCE_ECL_DEFAULT); | |
208 | } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support | |
209 | // Verify that we have the conditions to mark a comment-doc-keyword | |
210 | if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) { | |
211 | styleBeforeDCKeyword = SCE_ECL_COMMENTDOC; | |
212 | sc.SetState(SCE_ECL_COMMENTDOCKEYWORD); | |
213 | } | |
214 | } | |
215 | break; | |
216 | case SCE_ECL_COMMENTLINE: | |
217 | if (sc.atLineStart) { | |
218 | sc.SetState(SCE_ECL_DEFAULT); | |
219 | } | |
220 | break; | |
221 | case SCE_ECL_COMMENTLINEDOC: | |
222 | if (sc.atLineStart) { | |
223 | sc.SetState(SCE_ECL_DEFAULT); | |
224 | } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support | |
225 | // Verify that we have the conditions to mark a comment-doc-keyword | |
226 | if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) { | |
227 | styleBeforeDCKeyword = SCE_ECL_COMMENTLINEDOC; | |
228 | sc.SetState(SCE_ECL_COMMENTDOCKEYWORD); | |
229 | } | |
230 | } | |
231 | break; | |
232 | case SCE_ECL_COMMENTDOCKEYWORD: | |
233 | if ((styleBeforeDCKeyword == SCE_ECL_COMMENTDOC) && sc.Match('*', '/')) { | |
234 | sc.ChangeState(SCE_ECL_COMMENTDOCKEYWORDERROR); | |
235 | sc.Forward(); | |
236 | sc.ForwardSetState(SCE_ECL_DEFAULT); | |
237 | } else if (!setDoxygen.Contains(sc.ch)) { | |
238 | char s[1000]; | |
239 | sc.GetCurrentLowered(s, sizeof(s)); | |
240 | if (!IsASpace(sc.ch) || !keywords6.InList(s+1)) { | |
241 | sc.ChangeState(SCE_ECL_COMMENTDOCKEYWORDERROR); | |
242 | } | |
243 | sc.SetState(styleBeforeDCKeyword); | |
244 | } | |
245 | break; | |
246 | case SCE_ECL_STRING: | |
247 | if (sc.atLineEnd) { | |
248 | sc.ChangeState(SCE_ECL_STRINGEOL); | |
249 | } else if (sc.ch == '\\') { | |
250 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | |
251 | sc.Forward(); | |
252 | } | |
253 | } else if (sc.ch == '\"') { | |
254 | sc.ForwardSetState(SCE_ECL_DEFAULT); | |
255 | } | |
256 | break; | |
257 | case SCE_ECL_CHARACTER: | |
258 | if (sc.atLineEnd) { | |
259 | sc.ChangeState(SCE_ECL_STRINGEOL); | |
260 | } else if (sc.ch == '\\') { | |
261 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | |
262 | sc.Forward(); | |
263 | } | |
264 | } else if (sc.ch == '\'') { | |
265 | sc.ForwardSetState(SCE_ECL_DEFAULT); | |
266 | } | |
267 | break; | |
268 | case SCE_ECL_REGEX: | |
269 | if (sc.atLineStart) { | |
270 | sc.SetState(SCE_ECL_DEFAULT); | |
271 | } else if (sc.ch == '/') { | |
272 | sc.Forward(); | |
273 | while ((sc.ch < 0x80) && islower(sc.ch)) | |
274 | sc.Forward(); // gobble regex flags | |
275 | sc.SetState(SCE_ECL_DEFAULT); | |
276 | } else if (sc.ch == '\\') { | |
277 | // Gobble up the quoted character | |
278 | if (sc.chNext == '\\' || sc.chNext == '/') { | |
279 | sc.Forward(); | |
280 | } | |
281 | } | |
282 | break; | |
283 | case SCE_ECL_STRINGEOL: | |
284 | if (sc.atLineStart) { | |
285 | sc.SetState(SCE_ECL_DEFAULT); | |
286 | } | |
287 | break; | |
288 | case SCE_ECL_VERBATIM: | |
289 | if (sc.ch == '\"') { | |
290 | if (sc.chNext == '\"') { | |
291 | sc.Forward(); | |
292 | } else { | |
293 | sc.ForwardSetState(SCE_ECL_DEFAULT); | |
294 | } | |
295 | } | |
296 | break; | |
297 | case SCE_ECL_UUID: | |
298 | if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') { | |
299 | sc.SetState(SCE_ECL_DEFAULT); | |
300 | } | |
301 | break; | |
302 | } | |
303 | ||
304 | // Determine if a new state should be entered. | |
305 | int lineCurrent = styler.GetLine(sc.currentPos); | |
306 | int lineState = styler.GetLineState(lineCurrent); | |
307 | if (sc.state == SCE_ECL_DEFAULT) { | |
308 | if (lineState) { | |
309 | sc.SetState(lineState); | |
310 | } | |
311 | else if (sc.Match('@', '\"')) { | |
312 | sc.SetState(SCE_ECL_VERBATIM); | |
313 | sc.Forward(); | |
314 | } else if (setQualified.Contains(sc.ch) && sc.chNext == '\'') { | |
315 | sc.SetState(SCE_ECL_CHARACTER); | |
316 | sc.Forward(); | |
317 | } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { | |
318 | if (lastWordWasUUID) { | |
319 | sc.SetState(SCE_ECL_UUID); | |
320 | lastWordWasUUID = false; | |
321 | } else { | |
322 | sc.SetState(SCE_ECL_NUMBER); | |
323 | } | |
324 | } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) { | |
325 | if (lastWordWasUUID) { | |
326 | sc.SetState(SCE_ECL_UUID); | |
327 | lastWordWasUUID = false; | |
328 | } else { | |
329 | sc.SetState(SCE_ECL_IDENTIFIER); | |
330 | } | |
331 | } else if (sc.Match('/', '*')) { | |
332 | if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style | |
333 | sc.SetState(SCE_ECL_COMMENTDOC); | |
334 | } else { | |
335 | sc.SetState(SCE_ECL_COMMENT); | |
336 | } | |
337 | sc.Forward(); // Eat the * so it isn't used for the end of the comment | |
338 | } else if (sc.Match('/', '/')) { | |
339 | if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!")) | |
340 | // Support of Qt/Doxygen doc. style | |
341 | sc.SetState(SCE_ECL_COMMENTLINEDOC); | |
342 | else | |
343 | sc.SetState(SCE_ECL_COMMENTLINE); | |
344 | } else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite)) { | |
345 | sc.SetState(SCE_ECL_REGEX); // JavaScript's RegEx | |
346 | // } else if (sc.ch == '\"') { | |
347 | // sc.SetState(SCE_ECL_STRING); | |
348 | } else if (sc.ch == '\'') { | |
349 | sc.SetState(SCE_ECL_CHARACTER); | |
350 | } else if (sc.ch == '#' && visibleChars == 0) { | |
351 | // Preprocessor commands are alone on their line | |
352 | sc.SetState(SCE_ECL_PREPROCESSOR); | |
353 | // Skip whitespace between # and preprocessor word | |
354 | do { | |
355 | sc.Forward(); | |
356 | } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More()); | |
357 | if (sc.atLineEnd) { | |
358 | sc.SetState(SCE_ECL_DEFAULT); | |
359 | } | |
360 | } else if (isoperator(static_cast<char>(sc.ch))) { | |
361 | sc.SetState(SCE_ECL_OPERATOR); | |
362 | } | |
363 | } | |
364 | ||
365 | if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) { | |
366 | chPrevNonWhite = sc.ch; | |
367 | visibleChars++; | |
368 | } | |
369 | continuationLine = false; | |
370 | } | |
371 | sc.Complete(); | |
372 | ||
373 | } | |
374 | ||
375 | static bool IsStreamCommentStyle(int style) { | |
376 | return style == SCE_ECL_COMMENT || | |
377 | style == SCE_ECL_COMMENTDOC || | |
378 | style == SCE_ECL_COMMENTDOCKEYWORD || | |
379 | style == SCE_ECL_COMMENTDOCKEYWORDERROR; | |
380 | } | |
381 | ||
382 | bool MatchNoCase(Accessor & styler, unsigned int & pos, const char *s) { | |
383 | int i=0; | |
384 | for (; *s; i++) { | |
385 | char compare_char = tolower(*s); | |
386 | char styler_char = tolower(styler.SafeGetCharAt(pos+i)); | |
387 | if (compare_char != styler_char) | |
388 | return false; | |
389 | s++; | |
390 | } | |
391 | pos+=i-1; | |
392 | return true; | |
393 | } | |
394 | ||
395 | ||
396 | // Store both the current line's fold level and the next lines in the | |
397 | // level store to make it easy to pick up with each increment | |
398 | // and to make it possible to fiddle the current level for "} else {". | |
399 | static void FoldEclDoc(unsigned int startPos, int length, int initStyle, | |
400 | WordList *[], Accessor &styler) { | |
401 | bool foldComment = true; | |
402 | bool foldPreprocessor = true; | |
403 | bool foldCompact = true; | |
404 | bool foldAtElse = true; | |
405 | unsigned int endPos = startPos + length; | |
406 | int visibleChars = 0; | |
407 | int lineCurrent = styler.GetLine(startPos); | |
408 | int levelCurrent = SC_FOLDLEVELBASE; | |
409 | if (lineCurrent > 0) | |
410 | levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; | |
411 | int levelMinCurrent = levelCurrent; | |
412 | int levelNext = levelCurrent; | |
413 | char chNext = styler[startPos]; | |
414 | int styleNext = styler.StyleAt(startPos); | |
415 | int style = initStyle; | |
416 | for (unsigned int i = startPos; i < endPos; i++) { | |
417 | char ch = chNext; | |
418 | chNext = styler.SafeGetCharAt(i + 1); | |
419 | int stylePrev = style; | |
420 | style = styleNext; | |
421 | styleNext = styler.StyleAt(i + 1); | |
422 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); | |
423 | if (foldComment && IsStreamCommentStyle(style)) { | |
424 | if (!IsStreamCommentStyle(stylePrev) && (stylePrev != SCE_ECL_COMMENTLINEDOC)) { | |
425 | levelNext++; | |
426 | } else if (!IsStreamCommentStyle(styleNext) && (styleNext != SCE_ECL_COMMENTLINEDOC) && !atEOL) { | |
427 | // Comments don't end at end of line and the next character may be unstyled. | |
428 | levelNext--; | |
429 | } | |
430 | } | |
431 | if (foldComment && (style == SCE_ECL_COMMENTLINE)) { | |
432 | if ((ch == '/') && (chNext == '/')) { | |
433 | char chNext2 = styler.SafeGetCharAt(i + 2); | |
434 | if (chNext2 == '{') { | |
435 | levelNext++; | |
436 | } else if (chNext2 == '}') { | |
437 | levelNext--; | |
438 | } | |
439 | } | |
440 | } | |
441 | if (foldPreprocessor && (style == SCE_ECL_PREPROCESSOR)) { | |
442 | if (ch == '#') { | |
443 | unsigned int j = i + 1; | |
444 | while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) { | |
445 | j++; | |
446 | } | |
447 | if (MatchNoCase(styler, j, "region") || MatchNoCase(styler, j, "if")) { | |
448 | levelNext++; | |
449 | } else if (MatchNoCase(styler, j, "endregion") || MatchNoCase(styler, j, "end")) { | |
450 | levelNext--; | |
451 | } | |
452 | } | |
453 | } | |
454 | if (style == SCE_ECL_OPERATOR) { | |
455 | if (ch == '{') { | |
456 | // Measure the minimum before a '{' to allow | |
457 | // folding on "} else {" | |
458 | if (levelMinCurrent > levelNext) { | |
459 | levelMinCurrent = levelNext; | |
460 | } | |
461 | levelNext++; | |
462 | } else if (ch == '}') { | |
463 | levelNext--; | |
464 | } | |
465 | } | |
466 | if (style == SCE_ECL_WORD2) { | |
467 | if (MatchNoCase(styler, i, "record") || MatchNoCase(styler, i, "transform") || MatchNoCase(styler, i, "type") || MatchNoCase(styler, i, "function") || | |
468 | MatchNoCase(styler, i, "module") || MatchNoCase(styler, i, "service") || MatchNoCase(styler, i, "interface") || MatchNoCase(styler, i, "ifblock") || | |
469 | MatchNoCase(styler, i, "macro") || MatchNoCase(styler, i, "beginc++")) { | |
470 | levelNext++; | |
471 | } else if (MatchNoCase(styler, i, "endmacro") || MatchNoCase(styler, i, "endc++") || MatchNoCase(styler, i, "end")) { | |
472 | levelNext--; | |
473 | } | |
474 | } | |
475 | if (atEOL || (i == endPos-1)) { | |
476 | int levelUse = levelCurrent; | |
477 | if (foldAtElse) { | |
478 | levelUse = levelMinCurrent; | |
479 | } | |
480 | int lev = levelUse | levelNext << 16; | |
481 | if (visibleChars == 0 && foldCompact) | |
482 | lev |= SC_FOLDLEVELWHITEFLAG; | |
483 | if (levelUse < levelNext) | |
484 | lev |= SC_FOLDLEVELHEADERFLAG; | |
485 | if (lev != styler.LevelAt(lineCurrent)) { | |
486 | styler.SetLevel(lineCurrent, lev); | |
487 | } | |
488 | lineCurrent++; | |
489 | levelCurrent = levelNext; | |
490 | levelMinCurrent = levelCurrent; | |
491 | if (atEOL && (i == static_cast<unsigned int>(styler.Length()-1))) { | |
492 | // There is an empty line at end of file so give it same level and empty | |
493 | styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); | |
494 | } | |
495 | visibleChars = 0; | |
496 | } | |
497 | if (!IsASpace(ch)) | |
498 | visibleChars++; | |
499 | } | |
500 | } | |
501 | ||
502 | static const char * const EclWordListDesc[] = { | |
503 | "Keywords", | |
504 | 0 | |
505 | }; | |
506 | ||
507 | LexerModule lmECL( | |
508 | SCLEX_ECL, | |
509 | ColouriseEclDoc, | |
510 | "ecl", | |
511 | FoldEclDoc, | |
512 | EclWordListDesc); |