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