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