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