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