1 | // Scintilla source code edit control |
2 | // @file LexPowerPro.cxx |
3 | // PowerPro utility, written by Bruce Switzer, is available from http://powerpro.webeddie.com |
4 | // PowerPro lexer is written by Christopher Bean (cbean@cb-software.net) |
5 | // |
6 | // Lexer code heavily borrowed from: |
7 | // LexAU3.cxx by Jos van der Zande |
8 | // LexCPP.cxx by Neil Hodgson |
9 | // LexVB.cxx by Neil Hodgson |
10 | // |
11 | // Changes: |
12 | // 2008-10-25 - Initial release |
13 | // 2008-10-26 - Changed how <name> is hilighted in 'function <name>' so that |
14 | // local isFunction = "" and local functions = "" don't get falsely highlighted |
15 | // 2008-12-14 - Added bounds checking for szFirstWord and szDo |
16 | // - Replaced SetOfCharacters with CharacterSet |
17 | // - Made sure that CharacterSet::Contains is passed only positive values |
18 | // - Made sure that the return value of Accessor::SafeGetCharAt is positive before |
19 | // passing to functions that require positive values like isspacechar() |
20 | // - Removed unused visibleChars processing from ColourisePowerProDoc() |
21 | // - Fixed bug with folding logic where line continuations didn't end where |
22 | // they were supposed to |
23 | // - Moved all helper functions to the top of the file |
24 | // 2010-06-03 - Added onlySpaces variable to allow the @function and ;comment styles to be indented |
25 | // - Modified HasFunction function to be a bit more robust |
26 | // - Renamed HasFunction function to IsFunction |
27 | // - Cleanup |
28 | // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> |
29 | // The License.txt file describes the conditions under which this software may be distributed. |
30 | |
31 | #include <string.h> |
32 | #include <assert.h> |
33 | #include <ctype.h> |
34 | |
35 | #include <string> |
36 | #include <string_view> |
37 | |
38 | #include "ILexer.h" |
39 | #include "Scintilla.h" |
40 | #include "SciLexer.h" |
41 | |
42 | #include "WordList.h" |
43 | #include "LexAccessor.h" |
44 | #include "Accessor.h" |
45 | #include "StyleContext.h" |
46 | #include "CharacterSet.h" |
47 | #include "LexerModule.h" |
48 | |
49 | using namespace Lexilla; |
50 | |
51 | static inline bool (int style) { |
52 | return style == SCE_POWERPRO_COMMENTBLOCK; |
53 | } |
54 | |
55 | static inline bool IsLineEndChar(unsigned char ch) { |
56 | return ch == 0x0a //LF |
57 | || ch == 0x0c //FF |
58 | || ch == 0x0d; //CR |
59 | } |
60 | |
61 | static bool IsContinuationLine(Sci_PositionU szLine, Accessor &styler) |
62 | { |
63 | Sci_Position startPos = styler.LineStart(szLine); |
64 | Sci_Position endPos = styler.LineStart(szLine + 1) - 2; |
65 | while (startPos < endPos) |
66 | { |
67 | char stylech = styler.StyleAt(startPos); |
68 | if (!(stylech == SCE_POWERPRO_COMMENTBLOCK)) { |
69 | char ch = styler.SafeGetCharAt(endPos); |
70 | char chPrev = styler.SafeGetCharAt(endPos - 1); |
71 | char chPrevPrev = styler.SafeGetCharAt(endPos - 2); |
72 | if (ch > 0 && chPrev > 0 && chPrevPrev > 0 && !isspacechar(ch) && !isspacechar(chPrev) && !isspacechar(chPrevPrev) ) |
73 | return (chPrevPrev == ';' && chPrev == ';' && ch == '+'); |
74 | } |
75 | endPos--; // skip to next char |
76 | } |
77 | return false; |
78 | } |
79 | |
80 | // Routine to find first none space on the current line and return its Style |
81 | // needed for comment lines not starting on pos 1 |
82 | static int GetStyleFirstWord(Sci_Position szLine, Accessor &styler) |
83 | { |
84 | Sci_Position startPos = styler.LineStart(szLine); |
85 | Sci_Position endPos = styler.LineStart(szLine + 1) - 1; |
86 | char ch = styler.SafeGetCharAt(startPos); |
87 | |
88 | while (ch > 0 && isspacechar(ch) && startPos < endPos) |
89 | { |
90 | startPos++; // skip to next char |
91 | ch = styler.SafeGetCharAt(startPos); |
92 | } |
93 | return styler.StyleAt(startPos); |
94 | } |
95 | |
96 | //returns true if there is a function to highlight |
97 | //used to highlight <name> in 'function <name>' |
98 | //note: |
99 | // sample line (without quotes): "\tfunction asdf() |
100 | // currentPos will be the position of 'a' |
101 | static bool IsFunction(Accessor &styler, Sci_PositionU currentPos) { |
102 | |
103 | const char function[10] = "function " ; //10 includes \0 |
104 | unsigned int numberOfCharacters = sizeof(function) - 1; |
105 | Sci_PositionU position = currentPos - numberOfCharacters; |
106 | |
107 | //compare each character with the letters in the function array |
108 | //return false if ALL don't match |
109 | for (Sci_PositionU i = 0; i < numberOfCharacters; i++) { |
110 | char c = styler.SafeGetCharAt(position++); |
111 | if (c != function[i]) |
112 | return false; |
113 | } |
114 | |
115 | //make sure that there are only spaces (or tabs) between the beginning |
116 | //of the line and the function declaration |
117 | position = currentPos - numberOfCharacters - 1; //-1 to move to char before 'function' |
118 | for (Sci_PositionU j = 0; j < 16; j++) { //check up to 16 preceeding characters |
119 | char c = styler.SafeGetCharAt(position--, '\0'); //if can't read char, return NUL (past beginning of document) |
120 | if (c <= 0) //reached beginning of document |
121 | return true; |
122 | if (c > 0 && IsLineEndChar(c)) |
123 | return true; |
124 | else if (c > 0 && !IsASpaceOrTab(c)) |
125 | return false; |
126 | } |
127 | |
128 | //fall-through |
129 | return false; |
130 | } |
131 | |
132 | static void ColourisePowerProDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], |
133 | Accessor &styler, bool caseSensitive) { |
134 | |
135 | WordList &keywords = *keywordlists[0]; |
136 | WordList &keywords2 = *keywordlists[1]; |
137 | WordList &keywords3 = *keywordlists[2]; |
138 | WordList &keywords4 = *keywordlists[3]; |
139 | |
140 | //define the character sets |
141 | CharacterSet setWordStart(CharacterSet::setAlpha, "_@" , 0x80, true); |
142 | CharacterSet setWord(CharacterSet::setAlphaNum, "._" , 0x80, true); |
143 | |
144 | StyleContext sc(startPos, length, initStyle, styler); |
145 | char s_save[100]; //for last line highlighting |
146 | |
147 | //are there only spaces between the first letter of the line and the beginning of the line |
148 | bool onlySpaces = true; |
149 | |
150 | for (; sc.More(); sc.Forward()) { |
151 | |
152 | // save the total current word for eof processing |
153 | char s[100]; |
154 | sc.GetCurrentLowered(s, sizeof(s)); |
155 | |
156 | if ((sc.ch > 0) && setWord.Contains(sc.ch)) |
157 | { |
158 | strcpy(s_save,s); |
159 | int tp = static_cast<int>(strlen(s_save)); |
160 | if (tp < 99) { |
161 | s_save[tp] = static_cast<char>(tolower(sc.ch)); |
162 | s_save[tp+1] = '\0'; |
163 | } |
164 | } |
165 | |
166 | if (sc.atLineStart) { |
167 | if (sc.state == SCE_POWERPRO_DOUBLEQUOTEDSTRING) { |
168 | // Prevent SCE_POWERPRO_STRINGEOL from leaking back to previous line which |
169 | // ends with a line continuation by locking in the state upto this position. |
170 | sc.SetState(SCE_POWERPRO_DOUBLEQUOTEDSTRING); |
171 | } |
172 | } |
173 | |
174 | // Determine if the current state should terminate. |
175 | switch (sc.state) { |
176 | case SCE_POWERPRO_OPERATOR: |
177 | sc.SetState(SCE_POWERPRO_DEFAULT); |
178 | break; |
179 | |
180 | case SCE_POWERPRO_NUMBER: |
181 | |
182 | if (!IsADigit(sc.ch)) |
183 | sc.SetState(SCE_POWERPRO_DEFAULT); |
184 | |
185 | break; |
186 | |
187 | case SCE_POWERPRO_IDENTIFIER: |
188 | //if ((sc.ch > 0) && !setWord.Contains(sc.ch) || (sc.ch == '.')) { // use this line if don't want to match keywords with . in them. ie: win.debug will match both win and debug so win debug will also be colorized |
189 | if ((sc.ch > 0) && !setWord.Contains(sc.ch)){ // || (sc.ch == '.')) { // use this line if you want to match keywords with a . ie: win.debug will only match win.debug neither win nor debug will be colorized separately |
190 | char s[1000]; |
191 | if (caseSensitive) { |
192 | sc.GetCurrent(s, sizeof(s)); |
193 | } else { |
194 | sc.GetCurrentLowered(s, sizeof(s)); |
195 | } |
196 | |
197 | if (keywords.InList(s)) { |
198 | sc.ChangeState(SCE_POWERPRO_WORD); |
199 | } else if (keywords2.InList(s)) { |
200 | sc.ChangeState(SCE_POWERPRO_WORD2); |
201 | } else if (keywords3.InList(s)) { |
202 | sc.ChangeState(SCE_POWERPRO_WORD3); |
203 | } else if (keywords4.InList(s)) { |
204 | sc.ChangeState(SCE_POWERPRO_WORD4); |
205 | } |
206 | sc.SetState(SCE_POWERPRO_DEFAULT); |
207 | } |
208 | break; |
209 | |
210 | case SCE_POWERPRO_LINECONTINUE: |
211 | if (sc.atLineStart) { |
212 | sc.SetState(SCE_POWERPRO_DEFAULT); |
213 | } else if (sc.Match('/', '*') || sc.Match('/', '/')) { |
214 | sc.SetState(SCE_POWERPRO_DEFAULT); |
215 | } |
216 | break; |
217 | |
218 | case SCE_POWERPRO_COMMENTBLOCK: |
219 | if (sc.Match('*', '/')) { |
220 | sc.Forward(); |
221 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); |
222 | } |
223 | break; |
224 | |
225 | case SCE_POWERPRO_COMMENTLINE: |
226 | if (sc.atLineStart) { |
227 | sc.SetState(SCE_POWERPRO_DEFAULT); |
228 | } |
229 | break; |
230 | |
231 | case SCE_POWERPRO_DOUBLEQUOTEDSTRING: |
232 | if (sc.atLineEnd) { |
233 | sc.ChangeState(SCE_POWERPRO_STRINGEOL); |
234 | } else if (sc.ch == '\\') { |
235 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
236 | sc.Forward(); |
237 | } |
238 | } else if (sc.ch == '\"') { |
239 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); |
240 | } |
241 | break; |
242 | |
243 | case SCE_POWERPRO_SINGLEQUOTEDSTRING: |
244 | if (sc.atLineEnd) { |
245 | sc.ChangeState(SCE_POWERPRO_STRINGEOL); |
246 | } else if (sc.ch == '\\') { |
247 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
248 | sc.Forward(); |
249 | } |
250 | } else if (sc.ch == '\'') { |
251 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); |
252 | } |
253 | break; |
254 | |
255 | case SCE_POWERPRO_STRINGEOL: |
256 | if (sc.atLineStart) { |
257 | sc.SetState(SCE_POWERPRO_DEFAULT); |
258 | } |
259 | break; |
260 | |
261 | case SCE_POWERPRO_VERBATIM: |
262 | if (sc.ch == '\"') { |
263 | if (sc.chNext == '\"') { |
264 | sc.Forward(); |
265 | } else { |
266 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); |
267 | } |
268 | } |
269 | break; |
270 | |
271 | case SCE_POWERPRO_ALTQUOTE: |
272 | if (sc.ch == '#') { |
273 | if (sc.chNext == '#') { |
274 | sc.Forward(); |
275 | } else { |
276 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); |
277 | } |
278 | } |
279 | break; |
280 | |
281 | case SCE_POWERPRO_FUNCTION: |
282 | if (isspacechar(sc.ch) || sc.ch == '(') { |
283 | sc.SetState(SCE_POWERPRO_DEFAULT); |
284 | } |
285 | break; |
286 | } |
287 | |
288 | // Determine if a new state should be entered. |
289 | if (sc.state == SCE_POWERPRO_DEFAULT) { |
290 | if (sc.Match('?', '\"')) { |
291 | sc.SetState(SCE_POWERPRO_VERBATIM); |
292 | sc.Forward(); |
293 | } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { |
294 | sc.SetState(SCE_POWERPRO_NUMBER); |
295 | }else if (sc.Match('?','#')) { |
296 | if (sc.ch == '?' && sc.chNext == '#') { |
297 | sc.SetState(SCE_POWERPRO_ALTQUOTE); |
298 | sc.Forward(); |
299 | } |
300 | } else if (IsFunction(styler, sc.currentPos)) { //highlight <name> in 'function <name>' |
301 | sc.SetState(SCE_POWERPRO_FUNCTION); |
302 | } else if (onlySpaces && sc.ch == '@') { //alternate function definition [label] |
303 | sc.SetState(SCE_POWERPRO_FUNCTION); |
304 | } else if ((sc.ch > 0) && (setWordStart.Contains(sc.ch) || (sc.ch == '?'))) { |
305 | sc.SetState(SCE_POWERPRO_IDENTIFIER); |
306 | } else if (sc.Match(";;+" )) { |
307 | sc.SetState(SCE_POWERPRO_LINECONTINUE); |
308 | } else if (sc.Match('/', '*')) { |
309 | sc.SetState(SCE_POWERPRO_COMMENTBLOCK); |
310 | sc.Forward(); // Eat the * so it isn't used for the end of the comment |
311 | } else if (sc.Match('/', '/')) { |
312 | sc.SetState(SCE_POWERPRO_COMMENTLINE); |
313 | } else if (onlySpaces && sc.ch == ';') { //legacy comment that can only have blank space in front of it |
314 | sc.SetState(SCE_POWERPRO_COMMENTLINE); |
315 | } else if (sc.Match(";;" )) { |
316 | sc.SetState(SCE_POWERPRO_COMMENTLINE); |
317 | } else if (sc.ch == '\"') { |
318 | sc.SetState(SCE_POWERPRO_DOUBLEQUOTEDSTRING); |
319 | } else if (sc.ch == '\'') { |
320 | sc.SetState(SCE_POWERPRO_SINGLEQUOTEDSTRING); |
321 | } else if (isoperator(static_cast<char>(sc.ch))) { |
322 | sc.SetState(SCE_POWERPRO_OPERATOR); |
323 | } |
324 | } |
325 | |
326 | //maintain a record of whether or not all the preceding characters on |
327 | //a line are space characters |
328 | if (onlySpaces && !IsASpaceOrTab(sc.ch)) |
329 | onlySpaces = false; |
330 | |
331 | //reset when starting a new line |
332 | if (sc.atLineEnd) |
333 | onlySpaces = true; |
334 | } |
335 | |
336 | //************************************* |
337 | // Colourize the last word correctly |
338 | //************************************* |
339 | if (sc.state == SCE_POWERPRO_IDENTIFIER) |
340 | { |
341 | if (keywords.InList(s_save)) { |
342 | sc.ChangeState(SCE_POWERPRO_WORD); |
343 | sc.SetState(SCE_POWERPRO_DEFAULT); |
344 | } |
345 | else if (keywords2.InList(s_save)) { |
346 | sc.ChangeState(SCE_POWERPRO_WORD2); |
347 | sc.SetState(SCE_POWERPRO_DEFAULT); |
348 | } |
349 | else if (keywords3.InList(s_save)) { |
350 | sc.ChangeState(SCE_POWERPRO_WORD3); |
351 | sc.SetState(SCE_POWERPRO_DEFAULT); |
352 | } |
353 | else if (keywords4.InList(s_save)) { |
354 | sc.ChangeState(SCE_POWERPRO_WORD4); |
355 | sc.SetState(SCE_POWERPRO_DEFAULT); |
356 | } |
357 | else { |
358 | sc.SetState(SCE_POWERPRO_DEFAULT); |
359 | } |
360 | } |
361 | sc.Complete(); |
362 | } |
363 | |
364 | static void FoldPowerProDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) |
365 | { |
366 | //define the character sets |
367 | CharacterSet setWordStart(CharacterSet::setAlpha, "_@" , 0x80, true); |
368 | CharacterSet setWord(CharacterSet::setAlphaNum, "._" , 0x80, true); |
369 | |
370 | //used to tell if we're recursively folding the whole document, or just a small piece (ie: if statement or 1 function) |
371 | bool isFoldingAll = true; |
372 | |
373 | Sci_Position endPos = startPos + length; |
374 | Sci_Position lastLine = styler.GetLine(styler.Length()); //used to help fold the last line correctly |
375 | |
376 | // get settings from the config files for folding comments and preprocessor lines |
377 | bool = styler.GetPropertyInt("fold.comment" ) != 0; |
378 | bool = styler.GetPropertyInt("fold.comment" ) == 2; |
379 | bool foldCompact = true; |
380 | |
381 | // Backtrack to previous line in case need to fix its fold status |
382 | Sci_Position lineCurrent = styler.GetLine(startPos); |
383 | if (startPos > 0) { |
384 | isFoldingAll = false; |
385 | if (lineCurrent > 0) { |
386 | lineCurrent--; |
387 | startPos = styler.LineStart(lineCurrent); |
388 | } |
389 | } |
390 | // vars for style of previous/current/next lines |
391 | int style = GetStyleFirstWord(lineCurrent,styler); |
392 | int stylePrev = 0; |
393 | |
394 | // find the first previous line without continuation character at the end |
395 | while ((lineCurrent > 0 && IsContinuationLine(lineCurrent, styler)) |
396 | || (lineCurrent > 1 && IsContinuationLine(lineCurrent - 1, styler))) { |
397 | lineCurrent--; |
398 | startPos = styler.LineStart(lineCurrent); |
399 | } |
400 | |
401 | if (lineCurrent > 0) { |
402 | stylePrev = GetStyleFirstWord(lineCurrent-1,styler); |
403 | } |
404 | |
405 | // vars for getting first word to check for keywords |
406 | bool isFirstWordStarted = false; |
407 | bool isFirstWordEnded = false; |
408 | |
409 | const unsigned int FIRST_WORD_MAX_LEN = 10; |
410 | char szFirstWord[FIRST_WORD_MAX_LEN] = "" ; |
411 | unsigned int firstWordLen = 0; |
412 | |
413 | char szDo[3]="" ; |
414 | int szDolen = 0; |
415 | bool isDoLastWord = false; |
416 | |
417 | // var for indentlevel |
418 | int levelCurrent = SC_FOLDLEVELBASE; |
419 | if (lineCurrent > 0) |
420 | levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; |
421 | int levelNext = levelCurrent; |
422 | |
423 | int visibleChars = 0; |
424 | int functionCount = 0; |
425 | |
426 | char chNext = styler.SafeGetCharAt(startPos); |
427 | char chPrev = '\0'; |
428 | char chPrevPrev = '\0'; |
429 | char chPrevPrevPrev = '\0'; |
430 | |
431 | for (Sci_Position i = startPos; i < endPos; i++) { |
432 | |
433 | char ch = chNext; |
434 | chNext = styler.SafeGetCharAt(i + 1); |
435 | |
436 | if ((ch > 0) && setWord.Contains(ch)) |
437 | visibleChars++; |
438 | |
439 | // get the syle for the current character neede to check in comment |
440 | int stylech = styler.StyleAt(i); |
441 | |
442 | // start the capture of the first word |
443 | if (!isFirstWordStarted && (ch > 0)) { |
444 | if (setWord.Contains(ch) || setWordStart.Contains(ch) || ch == ';' || ch == '/') { |
445 | isFirstWordStarted = true; |
446 | if (firstWordLen < FIRST_WORD_MAX_LEN - 1) { |
447 | szFirstWord[firstWordLen++] = static_cast<char>(tolower(ch)); |
448 | szFirstWord[firstWordLen] = '\0'; |
449 | } |
450 | } |
451 | } // continue capture of the first word on the line |
452 | else if (isFirstWordStarted && !isFirstWordEnded && (ch > 0)) { |
453 | if (!setWord.Contains(ch)) { |
454 | isFirstWordEnded = true; |
455 | } |
456 | else if (firstWordLen < (FIRST_WORD_MAX_LEN - 1)) { |
457 | szFirstWord[firstWordLen++] = static_cast<char>(tolower(ch)); |
458 | szFirstWord[firstWordLen] = '\0'; |
459 | } |
460 | } |
461 | |
462 | if (stylech != SCE_POWERPRO_COMMENTLINE) { |
463 | |
464 | //reset isDoLastWord if we find a character(ignoring spaces) after 'do' |
465 | if (isDoLastWord && (ch > 0) && setWord.Contains(ch)) |
466 | isDoLastWord = false; |
467 | |
468 | // --find out if the word "do" is the last on a "if" line-- |
469 | // collect each letter and put it into a buffer 2 chars long |
470 | // if we end up with "do" in the buffer when we reach the end of |
471 | // the line, "do" was the last word on the line |
472 | if ((ch > 0) && isFirstWordEnded && strcmp(szFirstWord, "if" ) == 0) { |
473 | if (szDolen == 2) { |
474 | szDo[0] = szDo[1]; |
475 | szDo[1] = static_cast<char>(tolower(ch)); |
476 | szDo[2] = '\0'; |
477 | |
478 | if (strcmp(szDo, "do" ) == 0) |
479 | isDoLastWord = true; |
480 | |
481 | } else if (szDolen < 2) { |
482 | szDo[szDolen++] = static_cast<char>(tolower(ch)); |
483 | szDo[szDolen] = '\0'; |
484 | } |
485 | } |
486 | } |
487 | |
488 | // End of Line found so process the information |
489 | if ((ch == '\r' && chNext != '\n') // \r\n |
490 | || ch == '\n' // \n |
491 | || i == endPos) { // end of selection |
492 | |
493 | // ************************** |
494 | // Folding logic for Keywords |
495 | // ************************** |
496 | |
497 | // if a keyword is found on the current line and the line doesn't end with ;;+ (continuation) |
498 | // and we are not inside a commentblock. |
499 | if (firstWordLen > 0 |
500 | && chPrev != '+' && chPrevPrev != ';' && chPrevPrevPrev !=';' |
501 | && (!IsStreamCommentStyle(style) || foldInComment) ) { |
502 | |
503 | // only fold "if" last keyword is "then" (else its a one line if) |
504 | if (strcmp(szFirstWord, "if" ) == 0 && isDoLastWord) |
505 | levelNext++; |
506 | |
507 | // create new fold for these words |
508 | if (strcmp(szFirstWord, "for" ) == 0) |
509 | levelNext++; |
510 | |
511 | //handle folding for functions/labels |
512 | //Note: Functions and labels don't have an explicit end like [end function] |
513 | // 1. functions/labels end at the start of another function |
514 | // 2. functions/labels end at the end of the file |
515 | if ((strcmp(szFirstWord, "function" ) == 0) || (firstWordLen > 0 && szFirstWord[0] == '@')) { |
516 | if (isFoldingAll) { //if we're folding the whole document (recursivly by lua script) |
517 | |
518 | if (functionCount > 0) { |
519 | levelCurrent--; |
520 | } else { |
521 | levelNext++; |
522 | } |
523 | functionCount++; |
524 | |
525 | } else { //if just folding a small piece (by clicking on the minus sign next to the word) |
526 | levelCurrent--; |
527 | } |
528 | } |
529 | |
530 | // end the fold for these words before the current line |
531 | if (strcmp(szFirstWord, "endif" ) == 0 || strcmp(szFirstWord, "endfor" ) == 0) { |
532 | levelNext--; |
533 | levelCurrent--; |
534 | } |
535 | |
536 | // end the fold for these words before the current line and Start new fold |
537 | if (strcmp(szFirstWord, "else" ) == 0 || strcmp(szFirstWord, "elseif" ) == 0 ) |
538 | levelCurrent--; |
539 | |
540 | } |
541 | // Preprocessor and Comment folding |
542 | int styleNext = GetStyleFirstWord(lineCurrent + 1,styler); |
543 | |
544 | // ********************************* |
545 | // Folding logic for Comment blocks |
546 | // ********************************* |
547 | if (foldComment && IsStreamCommentStyle(style)) { |
548 | |
549 | // Start of a comment block |
550 | if (stylePrev != style && IsStreamCommentStyle(styleNext) && styleNext == style) { |
551 | levelNext++; |
552 | } // fold till the last line for normal comment lines |
553 | else if (IsStreamCommentStyle(stylePrev) |
554 | && styleNext != SCE_POWERPRO_COMMENTLINE |
555 | && stylePrev == SCE_POWERPRO_COMMENTLINE |
556 | && style == SCE_POWERPRO_COMMENTLINE) { |
557 | levelNext--; |
558 | } // fold till the one but last line for Blockcomment lines |
559 | else if (IsStreamCommentStyle(stylePrev) |
560 | && styleNext != SCE_POWERPRO_COMMENTBLOCK |
561 | && style == SCE_POWERPRO_COMMENTBLOCK) { |
562 | levelNext--; |
563 | levelCurrent--; |
564 | } |
565 | } |
566 | |
567 | int levelUse = levelCurrent; |
568 | int lev = levelUse | levelNext << 16; |
569 | if (visibleChars == 0 && foldCompact) |
570 | lev |= SC_FOLDLEVELWHITEFLAG; |
571 | if (levelUse < levelNext) |
572 | lev |= SC_FOLDLEVELHEADERFLAG; |
573 | if (lev != styler.LevelAt(lineCurrent)) |
574 | styler.SetLevel(lineCurrent, lev); |
575 | |
576 | // reset values for the next line |
577 | lineCurrent++; |
578 | stylePrev = style; |
579 | style = styleNext; |
580 | levelCurrent = levelNext; |
581 | visibleChars = 0; |
582 | |
583 | // if the last characters are ;;+ then don't reset since the line continues on the next line. |
584 | if (chPrev != '+' && chPrevPrev != ';' && chPrevPrevPrev != ';') { |
585 | firstWordLen = 0; |
586 | szDolen = 0; |
587 | isFirstWordStarted = false; |
588 | isFirstWordEnded = false; |
589 | isDoLastWord = false; |
590 | |
591 | //blank out first word |
592 | for (unsigned int i = 0; i < FIRST_WORD_MAX_LEN; i++) |
593 | szFirstWord[i] = '\0'; |
594 | } |
595 | } |
596 | |
597 | // save the last processed characters |
598 | if ((ch > 0) && !isspacechar(ch)) { |
599 | chPrevPrevPrev = chPrevPrev; |
600 | chPrevPrev = chPrev; |
601 | chPrev = ch; |
602 | } |
603 | } |
604 | |
605 | //close folds on the last line - without this a 'phantom' |
606 | //fold can appear when an open fold is on the last line |
607 | //this can occur because functions and labels don't have an explicit end |
608 | if (lineCurrent >= lastLine) { |
609 | int lev = 0; |
610 | lev |= SC_FOLDLEVELWHITEFLAG; |
611 | styler.SetLevel(lineCurrent, lev); |
612 | } |
613 | |
614 | } |
615 | |
616 | static const char * const powerProWordLists[] = { |
617 | "Keyword list 1" , |
618 | "Keyword list 2" , |
619 | "Keyword list 3" , |
620 | "Keyword list 4" , |
621 | 0, |
622 | }; |
623 | |
624 | static void ColourisePowerProDocWrapper(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], |
625 | Accessor &styler) { |
626 | ColourisePowerProDoc(startPos, length, initStyle, keywordlists, styler, false); |
627 | } |
628 | |
629 | LexerModule lmPowerPro(SCLEX_POWERPRO, ColourisePowerProDocWrapper, "powerpro" , FoldPowerProDoc, powerProWordLists); |
630 | |
631 | |
632 | |