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