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
49using namespace Lexilla;
50
51static inline bool IsStreamCommentStyle(int style) {
52 return style == SCE_POWERPRO_COMMENTBLOCK;
53}
54
55static inline bool IsLineEndChar(unsigned char ch) {
56 return ch == 0x0a //LF
57 || ch == 0x0c //FF
58 || ch == 0x0d; //CR
59}
60
61static 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
82static 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'
101static 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
132static 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
364static 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 foldComment = styler.GetPropertyInt("fold.comment") != 0;
378 bool foldInComment = 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
616static const char * const powerProWordLists[] = {
617 "Keyword list 1",
618 "Keyword list 2",
619 "Keyword list 3",
620 "Keyword list 4",
621 0,
622 };
623
624static 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
629LexerModule lmPowerPro(SCLEX_POWERPRO, ColourisePowerProDocWrapper, "powerpro", FoldPowerProDoc, powerProWordLists);
630
631
632