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 <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 | |
71 | using namespace Lexilla; |
72 | |
73 | |
74 | static 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 | |
87 | static 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 | |
101 | enum { |
102 | stateInICode = 0x1000, |
103 | stateSingleQuoteOpen = 0x2000, |
104 | stateDoubleQuoteOpen = 0x4000, |
105 | stateFoldInPreprocessor = 0x0100, |
106 | stateFoldInCaseStatement = 0x0200, |
107 | stateFoldInPreprocessorLevelMask = 0x00FF, |
108 | stateFoldMaskAll = 0x0FFF |
109 | }; |
110 | |
111 | |
112 | static 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 | |
124 | inline bool IsADataFlexField(int ch) { |
125 | return (ch == '.'); |
126 | } |
127 | |
128 | |
129 | static 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 | |
193 | static 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 | |
349 | static bool (int style) { |
350 | return style == SCE_DF_IMAGE; |
351 | } |
352 | |
353 | static bool (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 | |
371 | static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) { |
372 | return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask; |
373 | } |
374 | |
375 | static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) { |
376 | lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask; |
377 | lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask; |
378 | } |
379 | |
380 | static 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 | |
417 | static 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 | |
483 | static 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 | |
500 | static void FoldDataFlexDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], |
501 | Accessor &styler) { |
502 | bool = 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 | |
603 | static const char * const dataflexWordListDesc[] = { |
604 | "Keywords" , |
605 | "Scope open" , |
606 | "Scope close" , |
607 | "Operators" , |
608 | 0 |
609 | }; |
610 | |
611 | LexerModule lmDataflex(SCLEX_DATAFLEX, ColouriseDataFlexDoc, "dataflex" , FoldDataFlexDoc, dataflexWordListDesc); |
612 | |