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