1// Scintilla source code edit control
2/** @file LexDataflex.cxx
3 ** Lexer for DataFlex.
4 ** Based on LexPascal.cxx
5 ** Written by Wil van Antwerpen, June 2019
6 **/
7
8/*
9// The License.txt file describes the conditions under which this software may be distributed.
10
11A few words about features of LexDataflex...
12
13Generally speaking LexDataflex tries to support all available DataFlex features (up
14to DataFlex 19.1 at this time).
15
16~ FOLDING:
17
18Folding is supported in the following cases:
19
20- Folding of stream-like comments
21- Folding of groups of consecutive line comments
22- Folding of preprocessor blocks (the following preprocessor blocks are
23supported: #IFDEF, #IFNDEF, #ENDIF and #HEADER / #ENDHEADER
24blocks),
25- Folding of code blocks on appropriate keywords (the following code blocks are
26supported: "begin, struct, type, case / end" blocks, class & object
27declarations and interface declarations)
28
29Remarks:
30
31- We pass 4 arrays to the lexer:
321. The DataFlex keyword list, these are normal DataFlex keywords
332. The Scope Open list, for example, begin / procedure / while
343. The Scope Close list, for example, end / end_procedure / loop
354. Operator list, for ex. + / - / * / Lt / iand
36These lists are all mutually exclusive, scope open words should not be in the keyword list and vice versa
37
38- Folding of code blocks tries to handle all special cases in which folding
39should not occur.
40
41~ KEYWORDS:
42
43The list of keywords that can be used in dataflex.properties file (up to DataFlex
4419.1):
45
46- Keywords: .. snipped .. see dataflex.properties file.
47
48*/
49
50#include <stdlib.h>
51#include <string.h>
52#include <stdio.h>
53#include <stdarg.h>
54#include <assert.h>
55#include <ctype.h>
56
57#include <string>
58#include <string_view>
59
60#include "ILexer.h"
61#include "Scintilla.h"
62#include "SciLexer.h"
63
64#include "WordList.h"
65#include "LexAccessor.h"
66#include "Accessor.h"
67#include "StyleContext.h"
68#include "CharacterSet.h"
69#include "LexerModule.h"
70
71using namespace Lexilla;
72
73
74static void GetRangeLowered(Sci_PositionU start,
75 Sci_PositionU end,
76 Accessor &styler,
77 char *s,
78 Sci_PositionU len) {
79 Sci_PositionU i = 0;
80 while ((i < end - start + 1) && (i < len-1)) {
81 s[i] = static_cast<char>(tolower(styler[start + i]));
82 i++;
83 }
84 s[i] = '\0';
85}
86
87static void GetForwardRangeLowered(Sci_PositionU start,
88 CharacterSet &charSet,
89 Accessor &styler,
90 char *s,
91 Sci_PositionU len) {
92 Sci_PositionU i = 0;
93 while ((i < len-1) && charSet.Contains(styler.SafeGetCharAt(start + i))) {
94 s[i] = static_cast<char>(tolower(styler.SafeGetCharAt(start + i)));
95 i++;
96 }
97 s[i] = '\0';
98
99}
100
101enum {
102 stateInICode = 0x1000,
103 stateSingleQuoteOpen = 0x2000,
104 stateDoubleQuoteOpen = 0x4000,
105 stateFoldInPreprocessor = 0x0100,
106 stateFoldInCaseStatement = 0x0200,
107 stateFoldInPreprocessorLevelMask = 0x00FF,
108 stateFoldMaskAll = 0x0FFF
109};
110
111
112static bool IsFirstDataFlexWord(Sci_Position pos, Accessor &styler) {
113 Sci_Position line = styler.GetLine(pos);
114 Sci_Position start_pos = styler.LineStart(line);
115 for (Sci_Position i = start_pos; i < pos; i++) {
116 char ch = styler.SafeGetCharAt(i);
117 if (!(ch == ' ' || ch == '\t'))
118 return false;
119 }
120 return true;
121}
122
123
124inline bool IsADataFlexField(int ch) {
125 return (ch == '.');
126}
127
128
129static void ClassifyDataFlexWord(WordList *keywordlists[], StyleContext &sc, Accessor &styler) {
130 WordList& keywords = *keywordlists[0];
131 WordList& scopeOpen = *keywordlists[1];
132 WordList& scopeClosed = *keywordlists[2];
133 WordList& operators = *keywordlists[3];
134
135 char s[100];
136 int oldState;
137 int newState;
138 size_t tokenlen;
139
140 oldState = sc.state;
141 newState = oldState;
142 sc.GetCurrentLowered(s, sizeof(s));
143 tokenlen = strnlen(s,sizeof(s));
144 if (keywords.InList(s)) {
145 // keywords in DataFlex can be used as table column names (file.field) and as such they
146 // should not be characterized as a keyword. So test for that.
147 // for ex. somebody using date as field name.
148 if (!IsADataFlexField(sc.GetRelative(-static_cast<int>(tokenlen+1)))) {
149 newState = SCE_DF_WORD;
150 }
151 }
152 if (oldState == newState) {
153 if ((scopeOpen.InList(s) || scopeClosed.InList(s)) && (strcmp(s, "for") != 0) && (strcmp(s, "repeat") != 0)) {
154 // scope words in DataFlex can be used as table column names (file.field) and as such they
155 // should not be characterized as a scope word. So test for that.
156 // for ex. somebody using procedure for field name.
157 if (!IsADataFlexField(sc.GetRelative(-static_cast<int>(tokenlen+1)))) {
158 newState = SCE_DF_SCOPEWORD;
159 }
160 }
161 // no code folding on the next words, but just want to paint them like keywords (as they are) (??? doesn't the code to the opposite?)
162 if (strcmp(s, "if") == 0 ||
163 strcmp(s, "ifnot") == 0 ||
164 strcmp(s, "case") == 0 ||
165 strcmp(s, "else") == 0 ) {
166 newState = SCE_DF_SCOPEWORD;
167 }
168 }
169 if (oldState != newState && newState == SCE_DF_WORD) {
170 // a for loop must have for at the start of the line, for is also used in "define abc for 123"
171 if ( (strcmp(s, "for") == 0) && (IsFirstDataFlexWord(sc.currentPos-3, styler)) ) {
172 newState = SCE_DF_SCOPEWORD;
173 }
174 }
175 if (oldState != newState && newState == SCE_DF_WORD) {
176 // a repeat loop must have repeat at the start of the line, repeat is also used in 'move (repeat("d",5)) to sFoo'
177 if ( (strcmp(s, "repeat") == 0) && (IsFirstDataFlexWord(sc.currentPos-6, styler)) ) {
178 newState = SCE_DF_SCOPEWORD;
179 }
180 }
181 if (oldState == newState) {
182 if (operators.InList(s)) {
183 newState = SCE_DF_OPERATOR;
184 }
185 }
186
187 if (oldState != newState) {
188 sc.ChangeState(newState);
189 }
190 sc.SetState(SCE_DF_DEFAULT);
191}
192
193static void ColouriseDataFlexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[],
194 Accessor &styler) {
195// bool bSmartHighlighting = styler.GetPropertyInt("lexer.dataflex.smart.highlighting", 1) != 0;
196
197 CharacterSet setWordStart(CharacterSet::setAlpha, "_$#@", 0x80, true);
198 CharacterSet setWord(CharacterSet::setAlphaNum, "_$#@", 0x80, true);
199 CharacterSet setNumber(CharacterSet::setDigits, ".-+eE");
200 CharacterSet setHexNumber(CharacterSet::setDigits, "abcdefABCDEF");
201 CharacterSet setOperator(CharacterSet::setNone, "*+-/<=>^");
202
203 Sci_Position curLine = styler.GetLine(startPos);
204 int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0;
205
206 StyleContext sc(startPos, length, initStyle, styler);
207
208 for (; sc.More(); sc.Forward()) {
209 if (sc.atLineEnd) {
210 // Update the line state, so it can be seen by next line
211 curLine = styler.GetLine(sc.currentPos);
212 styler.SetLineState(curLine, curLineState);
213 }
214
215 // Determine if the current state should terminate.
216 switch (sc.state) {
217 case SCE_DF_NUMBER:
218 if (!setNumber.Contains(sc.ch) || (sc.ch == '.' && sc.chNext == '.')) {
219 sc.SetState(SCE_DF_DEFAULT);
220 } else if (sc.ch == '-' || sc.ch == '+') {
221 if (sc.chPrev != 'E' && sc.chPrev != 'e') {
222 sc.SetState(SCE_DF_DEFAULT);
223 }
224 }
225 break;
226 case SCE_DF_IDENTIFIER:
227 if (!setWord.Contains(sc.ch)) {
228 ClassifyDataFlexWord(keywordlists, sc, styler);
229 }
230 break;
231 case SCE_DF_HEXNUMBER:
232 if (!(setHexNumber.Contains(sc.ch) || sc.ch == 'I') ) { // in |CI$22a we also want to color the "I"
233 sc.SetState(SCE_DF_DEFAULT);
234 }
235 break;
236 case SCE_DF_METATAG:
237 if (sc.atLineStart || sc.chPrev == '}') {
238 sc.SetState(SCE_DF_DEFAULT);
239 }
240 break;
241 case SCE_DF_PREPROCESSOR:
242 if (sc.atLineStart || IsASpaceOrTab(sc.ch)) {
243 sc.SetState(SCE_DF_DEFAULT);
244 }
245 break;
246 case SCE_DF_IMAGE:
247 if (sc.atLineStart && sc.Match("/*")) {
248 sc.Forward(); // these characters are still part of the DF Image
249 sc.ForwardSetState(SCE_DF_DEFAULT);
250 }
251 break;
252 case SCE_DF_PREPROCESSOR2:
253 // we don't have inline comments or preprocessor2 commands
254 //if (sc.Match('*', ')')) {
255 // sc.Forward();
256 // sc.ForwardSetState(SCE_DF_DEFAULT);
257 //}
258 break;
259 case SCE_DF_COMMENTLINE:
260 if (sc.atLineStart) {
261 sc.SetState(SCE_DF_DEFAULT);
262 }
263 break;
264 case SCE_DF_STRING:
265 if (sc.atLineEnd) {
266 sc.ChangeState(SCE_DF_STRINGEOL);
267 } else if (sc.ch == '\'' && sc.chNext == '\'') {
268 sc.Forward();
269 } else if (sc.ch == '\"' && sc.chNext == '\"') {
270 sc.Forward();
271 } else if (sc.ch == '\'' || sc.ch == '\"') {
272 if (sc.ch == '\'' && (curLineState & stateSingleQuoteOpen) ) {
273 curLineState &= ~(stateSingleQuoteOpen);
274 sc.ForwardSetState(SCE_DF_DEFAULT);
275 }
276 else if (sc.ch == '\"' && (curLineState & stateDoubleQuoteOpen) ) {
277 curLineState &= ~(stateDoubleQuoteOpen);
278 sc.ForwardSetState(SCE_DF_DEFAULT);
279 }
280 }
281 break;
282 case SCE_DF_STRINGEOL:
283 if (sc.atLineStart) {
284 sc.SetState(SCE_DF_DEFAULT);
285 }
286 break;
287 case SCE_DF_SCOPEWORD:
288 //if (!setHexNumber.Contains(sc.ch) && sc.ch != '$') {
289 // sc.SetState(SCE_DF_DEFAULT);
290 //}
291 break;
292 case SCE_DF_OPERATOR:
293// if (bSmartHighlighting && sc.chPrev == ';') {
294// curLineState &= ~(stateInProperty | stateInExport);
295// }
296 sc.SetState(SCE_DF_DEFAULT);
297 break;
298 case SCE_DF_ICODE:
299 if (sc.atLineStart || IsASpace(sc.ch) || isoperator(sc.ch)) {
300 sc.SetState(SCE_DF_DEFAULT);
301 }
302 break;
303 }
304
305 // Determine if a new state should be entered.
306 if (sc.state == SCE_DF_DEFAULT) {
307 if (IsADigit(sc.ch)) {
308 sc.SetState(SCE_DF_NUMBER);
309 } else if (sc.Match('/', '/') || sc.Match("#REM")) {
310 sc.SetState(SCE_DF_COMMENTLINE);
311 } else if ((sc.ch == '#' && !sc.Match("#REM")) && IsFirstDataFlexWord(sc.currentPos, styler)) {
312 sc.SetState(SCE_DF_PREPROCESSOR);
313 // || (sc.ch == '|' && sc.chNext == 'C' && sc.GetRelativeCharacter(2) == 'I' && sc.GetRelativeCharacter(3) == '$') ) {
314 } else if ((sc.ch == '$' && ((!setWord.Contains(sc.chPrev)) || sc.chPrev == 'I' ) ) || (sc.Match("|CI$")) ) {
315 sc.SetState(SCE_DF_HEXNUMBER); // start with $ and previous character not in a..zA..Z0..9 excluding "I" OR start with |CI$
316 } else if (setWordStart.Contains(sc.ch)) {
317 sc.SetState(SCE_DF_IDENTIFIER);
318 } else if (sc.ch == '{') {
319 sc.SetState(SCE_DF_METATAG);
320 //} else if (sc.Match("(*$")) {
321 // sc.SetState(SCE_DF_PREPROCESSOR2);
322 } else if (sc.ch == '/' && setWord.Contains(sc.chNext) && sc.atLineStart) {
323 sc.SetState(SCE_DF_IMAGE);
324 // sc.Forward(); // Eat the * so it isn't used for the end of the comment
325 } else if (sc.ch == '\'' || sc.ch == '\"') {
326 if (sc.ch == '\'' && !(curLineState & stateDoubleQuoteOpen)) {
327 curLineState |= stateSingleQuoteOpen;
328 } else if (sc.ch == '\"' && !(curLineState & stateSingleQuoteOpen)) {
329 curLineState |= stateDoubleQuoteOpen;
330 }
331 sc.SetState(SCE_DF_STRING);
332 } else if (setOperator.Contains(sc.ch)) {
333 sc.SetState(SCE_DF_OPERATOR);
334// } else if (curLineState & stateInICode) {
335 // ICode start ! in a string followed by close string mark is not icode
336 } else if ((sc.ch == '!') && !(sc.ch == '!' && ((sc.chNext == '\"') || (sc.ch == '\'')) )) {
337 sc.SetState(SCE_DF_ICODE);
338 }
339 }
340 }
341
342 if (sc.state == SCE_DF_IDENTIFIER && setWord.Contains(sc.chPrev)) {
343 ClassifyDataFlexWord(keywordlists, sc, styler);
344 }
345
346 sc.Complete();
347}
348
349static bool IsStreamCommentStyle(int style) {
350 return style == SCE_DF_IMAGE;
351}
352
353static bool IsCommentLine(Sci_Position line, Accessor &styler) {
354 Sci_Position pos = styler.LineStart(line);
355 Sci_Position eolPos = styler.LineStart(line + 1) - 1;
356 for (Sci_Position i = pos; i < eolPos; i++) {
357 char ch = styler[i];
358 char chNext = styler.SafeGetCharAt(i + 1);
359 int style = styler.StyleAt(i);
360 if (ch == '/' && chNext == '/' && style == SCE_DF_COMMENTLINE) {
361 return true;
362 } else if (!IsASpaceOrTab(ch)) {
363 return false;
364 }
365 }
366 return false;
367}
368
369
370
371static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) {
372 return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask;
373}
374
375static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) {
376 lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask;
377 lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask;
378}
379
380static int ClassifyDataFlexPreprocessorFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,
381 Sci_PositionU startPos, Accessor &styler) {
382 CharacterSet setWord(CharacterSet::setAlpha);
383
384 char s[100]; // Size of the longest possible keyword + one additional character + null
385 GetForwardRangeLowered(startPos, setWord, styler, s, sizeof(s));
386 size_t iLen = strnlen(s,sizeof(s));
387 size_t iWordSize = 0;
388
389 unsigned int nestLevel = GetFoldInPreprocessorLevelFlag(lineFoldStateCurrent);
390
391 if (strcmp(s, "command") == 0 ||
392 // The #if/#ifdef etcetera commands are not currently foldable as it is easy to write code that
393 // breaks the collaps logic, so we keep things simple and not include that for now.
394 strcmp(s, "header") == 0) {
395 nestLevel++;
396 SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel);
397 lineFoldStateCurrent |= stateFoldInPreprocessor;
398 levelCurrent++;
399 iWordSize = iLen;
400 } else if (strcmp(s, "endcommand") == 0 ||
401 strcmp(s, "endheader") == 0) {
402 nestLevel--;
403 SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel);
404 if (nestLevel == 0) {
405 lineFoldStateCurrent &= ~stateFoldInPreprocessor;
406 }
407 levelCurrent--;
408 iWordSize = iLen;
409 if (levelCurrent < SC_FOLDLEVELBASE) {
410 levelCurrent = SC_FOLDLEVELBASE;
411 }
412 }
413 return static_cast<int>(iWordSize);
414}
415
416
417static void ClassifyDataFlexWordFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,
418 Sci_PositionU lastStart, Sci_PositionU currentPos, WordList *[], Accessor &styler) {
419 char s[100];
420
421 // property fold.dataflex.compilerlist
422 // Set to 1 for enabling the code folding feature in *.prn files
423 bool foldPRN = styler.GetPropertyInt("fold.dataflex.compilerlist",0) != 0;
424
425 GetRangeLowered(lastStart, currentPos, styler, s, sizeof(s));
426
427 if (strcmp(s, "case") == 0) {
428 lineFoldStateCurrent |= stateFoldInCaseStatement;
429 } else if (strcmp(s, "begin") == 0) {
430 levelCurrent++;
431 } else if (strcmp(s, "for") == 0 ||
432 strcmp(s, "while") == 0 ||
433 strcmp(s, "repeat") == 0 ||
434 strcmp(s, "for_all") == 0 ||
435 strcmp(s, "struct") == 0 ||
436 strcmp(s, "type") == 0 ||
437 strcmp(s, "begin_row") == 0 ||
438 strcmp(s, "item_list") == 0 ||
439 strcmp(s, "begin_constraints") == 0 ||
440 strcmp(s, "begin_transaction") == 0 ||
441 strcmp(s, "enum_list") == 0 ||
442 strcmp(s, "class") == 0 ||
443 strcmp(s, "object") == 0 ||
444 strcmp(s, "cd_popup_object") == 0 ||
445 strcmp(s, "procedure") == 0 ||
446 strcmp(s, "procedure_section") == 0 ||
447 strcmp(s, "function") == 0 ) {
448 if ((IsFirstDataFlexWord(lastStart, styler )) || foldPRN) {
449 levelCurrent++;
450 }
451 } else if (strcmp(s, "end") == 0) { // end is not always the first keyword, for example "case end"
452 levelCurrent--;
453 if (levelCurrent < SC_FOLDLEVELBASE) {
454 levelCurrent = SC_FOLDLEVELBASE;
455 }
456 } else if (strcmp(s, "loop") == 0 ||
457 strcmp(s, "until") == 0 ||
458 strcmp(s, "end_class") == 0 ||
459 strcmp(s, "end_object") == 0 ||
460 strcmp(s, "cd_end_object") == 0 ||
461 strcmp(s, "end_procedure") == 0 ||
462 strcmp(s, "end_function") == 0 ||
463 strcmp(s, "end_for_all") == 0 ||
464 strcmp(s, "end_struct") == 0 ||
465 strcmp(s, "end_type") == 0 ||
466 strcmp(s, "end_row") == 0 ||
467 strcmp(s, "end_item_list") == 0 ||
468 strcmp(s, "end_constraints") == 0 ||
469 strcmp(s, "end_transaction") == 0 ||
470 strcmp(s, "end_enum_list") == 0 ) {
471 // lineFoldStateCurrent &= ~stateFoldInRecord;
472 if ((IsFirstDataFlexWord(lastStart, styler )) || foldPRN) {
473 levelCurrent--;
474 if (levelCurrent < SC_FOLDLEVELBASE) {
475 levelCurrent = SC_FOLDLEVELBASE;
476 }
477 }
478 }
479
480}
481
482
483static void ClassifyDataFlexMetaDataFoldPoint(int &levelCurrent,
484 Sci_PositionU lastStart, Sci_PositionU currentPos, WordList *[], Accessor &styler) {
485 char s[100];
486
487 GetRangeLowered(lastStart, currentPos, styler, s, sizeof(s));
488
489 if (strcmp(s, "#beginsection") == 0) {
490 levelCurrent++;
491 } else if (strcmp(s, "#endsection") == 0) {
492 levelCurrent--;
493 if (levelCurrent < SC_FOLDLEVELBASE) {
494 levelCurrent = SC_FOLDLEVELBASE;
495 }
496 }
497
498}
499
500static void FoldDataFlexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[],
501 Accessor &styler) {
502 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
503 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
504 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
505 Sci_PositionU endPos = startPos + length;
506 int visibleChars = 0;
507 Sci_Position lineCurrent = styler.GetLine(startPos);
508 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
509 int levelCurrent = levelPrev;
510 int lineFoldStateCurrent = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) & stateFoldMaskAll : 0;
511 char chNext = styler[startPos];
512 int styleNext = styler.StyleAt(startPos);
513 int style = initStyle;
514 int iWordSize;
515
516 Sci_Position lastStart = 0;
517 CharacterSet setWord(CharacterSet::setAlphaNum, "_$#@", 0x80, true);
518
519 for (Sci_PositionU i = startPos; i < endPos; i++) {
520 char ch = chNext;
521 chNext = styler.SafeGetCharAt(i + 1);
522 int stylePrev = style;
523 style = styleNext;
524 styleNext = styler.StyleAt(i + 1);
525 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
526
527 if (foldComment && IsStreamCommentStyle(style)) {
528 if (!IsStreamCommentStyle(stylePrev)) {
529 levelCurrent++;
530 } else if (!IsStreamCommentStyle(styleNext)) {
531 levelCurrent--;
532 }
533 }
534 if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
535 {
536 if (!IsCommentLine(lineCurrent - 1, styler)
537 && IsCommentLine(lineCurrent + 1, styler))
538 levelCurrent++;
539 else if (IsCommentLine(lineCurrent - 1, styler)
540 && !IsCommentLine(lineCurrent+1, styler))
541 levelCurrent--;
542 }
543 if (foldPreprocessor) {
544 if (style == SCE_DF_PREPROCESSOR) {
545 iWordSize = ClassifyDataFlexPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 1, styler);
546 //} else if (style == SCE_DF_PREPROCESSOR2 && ch == '(' && chNext == '*'
547 // && styler.SafeGetCharAt(i + 2) == '$') {
548 // ClassifyDataFlexPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 3, styler);
549 i = i + iWordSize;
550 }
551 }
552
553 if (stylePrev != SCE_DF_SCOPEWORD && style == SCE_DF_SCOPEWORD)
554 {
555 // Store last word start point.
556 lastStart = i;
557 }
558 if (stylePrev == SCE_DF_SCOPEWORD) {
559 if(setWord.Contains(ch) && !setWord.Contains(chNext)) {
560 ClassifyDataFlexWordFoldPoint(levelCurrent, lineFoldStateCurrent, lastStart, i, keywordlists, styler);
561 }
562 }
563
564 if (stylePrev == SCE_DF_METATAG && ch == '#')
565 {
566 // Store last word start point.
567 lastStart = i;
568 }
569 if (stylePrev == SCE_DF_METATAG) {
570 if(setWord.Contains(ch) && !setWord.Contains(chNext)) {
571 ClassifyDataFlexMetaDataFoldPoint(levelCurrent, lastStart, i, keywordlists, styler);
572 }
573 }
574
575 if (!IsASpace(ch))
576 visibleChars++;
577
578 if (atEOL) {
579 int lev = levelPrev;
580 if (visibleChars == 0 && foldCompact)
581 lev |= SC_FOLDLEVELWHITEFLAG;
582 if ((levelCurrent > levelPrev) && (visibleChars > 0))
583 lev |= SC_FOLDLEVELHEADERFLAG;
584 if (lev != styler.LevelAt(lineCurrent)) {
585 styler.SetLevel(lineCurrent, lev);
586 }
587 int newLineState = (styler.GetLineState(lineCurrent) & ~stateFoldMaskAll) | lineFoldStateCurrent;
588 styler.SetLineState(lineCurrent, newLineState);
589 lineCurrent++;
590 levelPrev = levelCurrent;
591 visibleChars = 0;
592 }
593 }
594
595 // If we didn't reach the EOL in previous loop, store line level and whitespace information.
596 // The rest will be filled in later...
597 int lev = levelPrev;
598 if (visibleChars == 0 && foldCompact)
599 lev |= SC_FOLDLEVELWHITEFLAG;
600 styler.SetLevel(lineCurrent, lev);
601}
602
603static const char * const dataflexWordListDesc[] = {
604 "Keywords",
605 "Scope open",
606 "Scope close",
607 "Operators",
608 0
609};
610
611LexerModule lmDataflex(SCLEX_DATAFLEX, ColouriseDataFlexDoc, "dataflex", FoldDataFlexDoc, dataflexWordListDesc);
612