*** empty log message ***
[anjuta-git-plugin.git] / scintilla / LexAU3.cxx
blob31e4ee52d2d78be5a5ce611d5e1cd1702eca52ed
1 // Scintilla source code edit control
2 // @file LexAU3.cxx
3 // Lexer for AutoIt3 http://www.hiddensoft.com/autoit3
4 // by Jos van der Zande, jvdzande@yahoo.com
5 //
6 // Changes:
7 // March 28, 2004 - Added the standard Folding code
8 // April 21, 2004 - Added Preprosessor Table + Syntax Highlighting
9 // Fixed Number highlighting
10 // Changed default isoperator to IsAOperator to have a better match to AutoIt3
11 // Fixed "#comments_start" -> "#comments-start"
12 // Fixed "#comments_end" -> "#comments-end"
13 // Fixed Sendkeys in Strings when not terminated with }
14 // Added support for Sendkey strings that have second parameter e.g. {UP 5} or {a down}
15 // April 26, 2004 - Fixed # pre-processor statement inside of comment block would invalidly change the color.
16 // Added logic for #include <xyz.au3> to treat the <> as string
17 // Added underscore to IsAOperator.
18 // May 17, 2004 - Changed the folding logic from indent to keyword folding.
19 // Added Folding logic for blocks of single-commentlines or commentblock.
20 // triggered by: fold.comment=1
21 // Added Folding logic for preprocessor blocks triggered by fold.preprocessor=1
22 // Added Special for #region - #endregion syntax highlight and folding.
23 // May 30, 2004 - Fixed issue with continuation lines on If statements.
24 // June 5, 2004 - Added comma to Operators for better readability.
25 // Added fold.compact support set with fold.compact=1
26 // Changed folding inside of #cs-#ce. Default is no keyword folding inside comment blocks when fold.comment=1
27 // it will now only happen when fold.comment=2.
28 //
29 // Copyright for Scintilla: 1998-2001 by Neil Hodgson <neilh@scintilla.org>
30 // The License.txt file describes the conditions under which this software may be distributed.
31 // Scintilla source code edit control
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <stdio.h>
37 #include <stdarg.h>
39 #include "Platform.h"
41 #include "PropSet.h"
42 #include "Accessor.h"
43 #include "StyleContext.h"
44 #include "KeyWords.h"
45 #include "Scintilla.h"
46 #include "SciLexer.h"
48 static inline bool IsTypeCharacter(const int ch)
50 return ch == '$';
52 static inline bool IsAWordChar(const int ch)
54 return (ch < 0x80) && (isalnum(ch) || ch == '_');
57 static inline bool IsAWordStart(const int ch)
59 return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '@' || ch == '#' || ch == '$');
62 static inline bool IsAOperator(char ch) {
63 if (isascii(ch) && isalnum(ch))
64 return false;
65 if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
66 ch == '&' || ch == '^' || ch == '=' || ch == '<' || ch == '>' ||
67 ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == ',' )
68 return true;
69 return false;
72 ///////////////////////////////////////////////////////////////////////////////
73 // GetSendKey() filters the portion before and after a/multiple space(s)
74 // and return the first portion to be looked-up in the table
75 // also check if the second portion is valid... (up,down.on.off,toggle or a number)
76 ///////////////////////////////////////////////////////////////////////////////
78 static int GetSendKey(const char *szLine, char *szKey)
80 int nFlag = 0;
81 int nKeyPos = 0;
82 int nSpecPos= 0;
83 int nSpecNum= 1;
84 int nPos = 0;
85 char cTemp;
86 char szSpecial[100];
88 // split the portion of the sendkey in the part before and after the spaces
89 while ( ( (cTemp = szLine[nPos]) != '\0'))
91 if ((cTemp == ' ') && (nFlag == 0) ) // get the stuff till first space
93 nFlag = 1;
94 // Add } to the end of the first bit for table lookup later.
95 szKey[nKeyPos++] = '}';
97 else if (cTemp == ' ')
99 // skip other spaces
101 else if (nFlag == 0)
103 // save first portion into var till space or } is hit
104 szKey[nKeyPos++] = cTemp;
106 else if ((nFlag == 1) && (cTemp != '}'))
108 // Save second portion into var...
109 szSpecial[nSpecPos++] = cTemp;
110 // check if Second portion is all numbers for repeat fuction
111 if (isdigit(cTemp) == false) {nSpecNum = 0;}
113 nPos++; // skip to next char
115 } // End While
118 // Check if the second portion is either a number or one of these keywords
119 szKey[nKeyPos] = '\0';
120 szSpecial[nSpecPos] = '\0';
121 if (strcmp(szSpecial,"down")== 0 || strcmp(szSpecial,"up")== 0 ||
122 strcmp(szSpecial,"on")== 0 || strcmp(szSpecial,"off")== 0 ||
123 strcmp(szSpecial,"toggle")== 0 || nSpecNum == 1 )
125 nFlag = 0;
127 else
129 nFlag = 1;
131 return nFlag; // 1 is bad, 0 is good
133 } // GetSendKey()
135 static void ColouriseAU3Doc(unsigned int startPos,
136 int length, int initStyle,
137 WordList *keywordlists[],
138 Accessor &styler) {
140 WordList &keywords = *keywordlists[0];
141 WordList &keywords2 = *keywordlists[1];
142 WordList &keywords3 = *keywordlists[2];
143 WordList &keywords4 = *keywordlists[3];
144 WordList &keywords5 = *keywordlists[4];
145 WordList &keywords6 = *keywordlists[5];
146 styler.StartAt(startPos);
148 StyleContext sc(startPos, length, initStyle, styler);
149 char si; // string indicator "=1 '=2
150 si=0;
151 //$$$
152 for (; sc.More(); sc.Forward()) {
153 char s[100];
154 sc.GetCurrentLowered(s, sizeof(s));
155 switch (sc.state)
157 case SCE_AU3_COMMENTBLOCK:
159 if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && strcmp(s, "#comments") == 0)))
161 if ((strcmp(s, "#ce")== 0 || strcmp(s, "#comments-end")== 0))
162 {sc.SetState(SCE_AU3_COMMENT);} // set to comment line for the rest of the line
163 else
164 {sc.SetState(SCE_AU3_COMMENTBLOCK);}
166 break;
168 case SCE_AU3_COMMENT:
170 if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
171 break;
173 case SCE_AU3_OPERATOR:
175 sc.SetState(SCE_AU3_DEFAULT);
176 break;
178 case SCE_AU3_SPECIAL:
180 if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
181 break;
183 case SCE_AU3_KEYWORD:
185 if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && (strcmp(s, "#comments") == 0 || strcmp(s, "#include") == 0))))
187 if (!IsTypeCharacter(sc.ch))
189 if (strcmp(s, "#cs")== 0 || strcmp(s, "#comments-start")== 0 )
191 sc.ChangeState(SCE_AU3_COMMENTBLOCK);
192 sc.SetState(SCE_AU3_COMMENTBLOCK);
194 else if (keywords.InList(s)) {
195 sc.ChangeState(SCE_AU3_KEYWORD);
196 sc.SetState(SCE_AU3_DEFAULT);
198 else if (keywords2.InList(s)) {
199 sc.ChangeState(SCE_AU3_FUNCTION);
200 sc.SetState(SCE_AU3_DEFAULT);
202 else if (keywords3.InList(s)) {
203 sc.ChangeState(SCE_AU3_MACRO);
204 sc.SetState(SCE_AU3_DEFAULT);
206 else if (keywords5.InList(s)) {
207 sc.ChangeState(SCE_AU3_PREPROCESSOR);
208 sc.SetState(SCE_AU3_DEFAULT);
209 if (strcmp(s, "#include")== 0)
211 si = 3; // use to determine string start for #inlude <>
214 else if (keywords6.InList(s)) {
215 sc.ChangeState(SCE_AU3_SPECIAL);
216 sc.SetState(SCE_AU3_SPECIAL);
218 else if (strcmp(s, "_") == 0) {
219 sc.ChangeState(SCE_AU3_OPERATOR);
220 sc.SetState(SCE_AU3_DEFAULT);
222 else if (!IsAWordChar(sc.ch)) {
223 sc.ChangeState(SCE_AU3_DEFAULT);
224 sc.SetState(SCE_AU3_DEFAULT);
228 if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
229 break;
231 case SCE_AU3_NUMBER:
233 if (!IsAWordChar(sc.ch)) {sc.SetState(SCE_AU3_DEFAULT);}
234 break;
236 case SCE_AU3_VARIABLE:
238 if (!IsAWordChar(sc.ch)) {sc.SetState(SCE_AU3_DEFAULT);}
239 break;
241 case SCE_AU3_STRING:
243 // check for " to end a double qouted string or
244 // check for ' to end a single qouted string
245 if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\'') || (si == 3 && sc.ch == '>'))
247 sc.ForwardSetState(SCE_AU3_DEFAULT);
249 if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
250 // find Sendkeys in a STRING
251 if (sc.ch == '{') {sc.SetState(SCE_AU3_SENT);}
252 if (sc.ch == '+' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
253 if (sc.ch == '!' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
254 if (sc.ch == '^' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
255 if (sc.ch == '#' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
256 break;
259 case SCE_AU3_SENT:
261 // Send key string ended
262 if (sc.chPrev == '}' && sc.ch != '}')
264 // set color to SENDKEY when valid sendkey .. else set back to regular string
265 char sk[100];
266 // split {111 222} and return {111} and check if 222 is valid.
267 // if return code = 1 then invalid 222 so must be string
268 if (GetSendKey(s,sk))
270 sc.ChangeState(SCE_AU3_STRING);
272 // if single char between {?} then its ok as sendkey for a single character
273 else if (strlen(sk) == 3)
275 sc.ChangeState(SCE_AU3_SENT);
277 // if sendkey {111} is in table then ok as sendkey
278 else if (keywords4.InList(sk))
280 sc.ChangeState(SCE_AU3_SENT);
282 else
284 sc.ChangeState(SCE_AU3_STRING);
286 sc.SetState(SCE_AU3_STRING);
288 // check if next portion is again a sendkey
289 if (sc.atLineEnd)
291 sc.SetState(SCE_AU3_DEFAULT);
292 si = 0; // reset string indicator
294 if (sc.ch == '{' && sc.chPrev != '{') {sc.SetState(SCE_AU3_SENT);}
295 if (sc.ch == '+' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
296 if (sc.ch == '!' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
297 if (sc.ch == '^' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
298 if (sc.ch == '#' && sc.chNext == '{') {sc.SetState(SCE_AU3_SENT);}
299 // check to see if the string ended...
300 // Sentkey string isn't complete but the string ended....
301 if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\''))
303 sc.ChangeState(SCE_AU3_STRING);
304 sc.ForwardSetState(SCE_AU3_DEFAULT);
306 break;
308 } //switch (sc.state)
310 // Determine if a new state should be entered:
312 if (sc.state == SCE_AU3_DEFAULT)
314 if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);}
315 else if (sc.ch == '#') {sc.SetState(SCE_AU3_KEYWORD);}
316 else if (sc.ch == '$') {sc.SetState(SCE_AU3_VARIABLE);}
317 else if (sc.ch == '@') {sc.SetState(SCE_AU3_KEYWORD);}
318 else if (sc.ch == '<' && si==3) {sc.SetState(SCE_AU3_STRING);} // string after #include
319 else if (sc.ch == '\"') {
320 sc.SetState(SCE_AU3_STRING);
321 si = 1; }
322 else if (sc.ch == '\'') {
323 sc.SetState(SCE_AU3_STRING);
324 si = 2; }
325 else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {sc.SetState(SCE_AU3_NUMBER);}
326 else if (IsAWordStart(sc.ch)) {sc.SetState(SCE_AU3_KEYWORD);}
327 else if (IsAOperator(static_cast<char>(sc.ch))) {sc.SetState(SCE_AU3_OPERATOR);}
328 else if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
330 } //for (; sc.More(); sc.Forward())
331 sc.Complete();
335 static bool IsStreamCommentStyle(int style) {
336 return style == SCE_AU3_COMMENT || style == SCE_AU3_COMMENTBLOCK;
340 // Routine to find first none space on the current line and return its Style
341 // needed for comment lines not starting on pos 1
342 static int GetStyleFirstWord(unsigned int szLine, Accessor &styler)
344 int nsPos = styler.LineStart(szLine);
345 int nePos = styler.LineStart(szLine+1) - 1;
346 while (isspacechar(styler.SafeGetCharAt(nsPos)) && nsPos < nePos)
348 nsPos++; // skip to next char
350 } // End While
351 return styler.StyleAt(nsPos);
353 } // GetStyleFirstWord()
356 // Routine to check the last "none comment" character on a line to see if its a continuation
358 static bool IsContinuationLine(unsigned int szLine, Accessor &styler)
360 int nsPos = styler.LineStart(szLine);
361 int nePos = styler.LineStart(szLine+1) - 2;
362 //int stylech = styler.StyleAt(nsPos);
363 while (nsPos < nePos)
365 //stylech = styler.StyleAt(nePos);
366 int stylech = styler.StyleAt(nsPos);
367 if (!(stylech == SCE_AU3_COMMENT)) {
368 char ch = styler.SafeGetCharAt(nePos);
369 if (!isspacechar(ch)) {
370 if (ch == '_')
371 return true;
372 else
373 return false;
376 nePos--; // skip to next char
377 } // End While
378 return false;
379 } // IsContinuationLine()
383 static void FoldAU3Doc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
385 int endPos = startPos + length;
386 // get settings from the config files for folding comments and preprocessor lines
387 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
388 bool foldInComment = styler.GetPropertyInt("fold.comment") == 2;
389 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
390 bool foldpreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
391 // Backtrack to previous line in case need to fix its fold status
392 int lineCurrent = styler.GetLine(startPos);
393 if (startPos > 0) {
394 if (lineCurrent > 0) {
395 lineCurrent--;
396 startPos = styler.LineStart(lineCurrent);
399 // vars for style of previous/current/next lines
400 int style = GetStyleFirstWord(lineCurrent,styler);
401 int stylePrev = 0;
402 // find the first previous line without continuation character at the end
403 while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) ||
404 (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) {
405 lineCurrent--;
406 startPos = styler.LineStart(lineCurrent);
408 if (lineCurrent > 0) {
409 stylePrev = GetStyleFirstWord(lineCurrent-1,styler);
411 // vars for getting first word to check for keywords
412 bool FirstWordStart = false;
413 bool FirstWordEnd = false;
414 char szKeyword[10]="";
415 int szKeywordlen = 0;
416 char szThen[5]="";
417 int szThenlen = 0;
418 bool ThenFoundLast = false;
419 // var for indentlevel
420 int levelCurrent = SC_FOLDLEVELBASE;
421 if (lineCurrent > 0)
422 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
423 int levelNext = levelCurrent;
425 int visibleChars = 0;
426 char chNext = styler.SafeGetCharAt(startPos);
427 char chPrev = ' ';
429 for (int i = startPos; i < endPos; i++) {
430 char ch = chNext;
431 chNext = styler.SafeGetCharAt(i + 1);
432 if (IsAWordChar(ch)) {
433 visibleChars++;
435 // get the syle for the current character neede to check in comment
436 int stylech = styler.StyleAt(i);
437 // get first word for the line for indent check max 9 characters
438 if (FirstWordStart && (!(FirstWordEnd))) {
439 if (!IsAWordChar(ch)) {
440 FirstWordEnd = true;
441 szKeyword[szKeywordlen] = '\0';
443 else {
444 if (szKeywordlen < 10) {
445 szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
449 // start the capture of the first word
450 if (!(FirstWordStart)) {
451 if (IsAWordChar(ch) || IsAWordStart(ch) || ch == ';') {
452 FirstWordStart = true;
453 szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
456 // only process this logic when not in comment section
457 if (!(stylech == SCE_AU3_COMMENT)) {
458 if (ThenFoundLast) {
459 if (IsAWordChar(ch)) {
460 ThenFoundLast = false;
463 // find out if the word "then" is the last on a "if" line
464 if (FirstWordEnd && strcmp(szKeyword,"if") == 0) {
465 if (szThenlen == 4) {
466 szThen[0] = szThen[1];
467 szThen[1] = szThen[2];
468 szThen[2] = szThen[3];
469 szThen[3] = static_cast<char>(tolower(ch));
470 if (strcmp(szThen,"then") == 0 ) {
471 ThenFoundLast = true;
474 else {
475 szThen[szThenlen++] = static_cast<char>(tolower(ch));
476 if (szThenlen == 5) {
477 szThen[4] = '\0';
482 // End of Line found so process the information
483 if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
484 // **************************
485 // Folding logic for Keywords
486 // **************************
487 // if a keyword is found on the current line and the line doesn't end with _ (continuation)
488 // and we are not inside a commentblock.
489 if (szKeywordlen > 0 && (!(chPrev == '_')) &&
490 ((!(IsStreamCommentStyle(style)) || foldInComment)) ) {
491 szKeyword[szKeywordlen] = '\0';
492 // only fold "if" last keyword is "then" (else its a one line if)
493 if (strcmp(szKeyword,"if") == 0 && ThenFoundLast) {
494 levelNext++;
496 // create new fold for these words
497 if (strcmp(szKeyword,"do") == 0 || strcmp(szKeyword,"for") == 0 ||
498 strcmp(szKeyword,"func") == 0 || strcmp(szKeyword,"while") == 0||
499 strcmp(szKeyword,"#region") == 0 ) {
500 levelNext++;
502 // create double Fold for select because Case will subtract one of the current level
503 if (strcmp(szKeyword,"select") == 0) {
504 levelNext++;
505 levelNext++;
507 // end the fold for these words before the current line
508 if (strcmp(szKeyword,"endfunc") == 0 || strcmp(szKeyword,"endif") == 0 ||
509 strcmp(szKeyword,"next") == 0 || strcmp(szKeyword,"until") == 0 ||
510 strcmp(szKeyword,"wend") == 0){
511 levelNext--;
512 levelCurrent--;
514 // end the fold for these words before the current line and Start new fold
515 if (strcmp(szKeyword,"case") == 0 || strcmp(szKeyword,"else") == 0 ||
516 strcmp(szKeyword,"elseif") == 0 ) {
517 levelCurrent--;
519 // end the double fold for this word before the current line
520 if (strcmp(szKeyword,"endselect") == 0 ) {
521 levelNext--;
522 levelNext--;
523 levelCurrent--;
524 levelCurrent--;
526 // end the fold for these words on the current line
527 if (strcmp(szKeyword,"#endregion") == 0 ) {
528 levelNext--;
531 // Preprocessor and Comment folding
532 int styleNext = GetStyleFirstWord(lineCurrent + 1,styler);
533 // *************************************
534 // Folding logic for preprocessor blocks
535 // *************************************
536 // process preprosessor line
537 if (foldpreprocessor && style == SCE_AU3_PREPROCESSOR) {
538 if (!(stylePrev == SCE_AU3_PREPROCESSOR) && (styleNext == SCE_AU3_PREPROCESSOR)) {
539 levelNext++;
541 // fold till the last line for normal comment lines
542 else if (stylePrev == SCE_AU3_PREPROCESSOR && !(styleNext == SCE_AU3_PREPROCESSOR)) {
543 levelNext--;
546 // *********************************
547 // Folding logic for Comment blocks
548 // *********************************
549 if (foldComment && IsStreamCommentStyle(style)) {
550 // Start of a comment block
551 if (!(stylePrev==style) && IsStreamCommentStyle(styleNext) && styleNext==style) {
552 levelNext++;
554 // fold till the last line for normal comment lines
555 else if (IsStreamCommentStyle(stylePrev)
556 && !(styleNext == SCE_AU3_COMMENT)
557 && stylePrev == SCE_AU3_COMMENT
558 && style == SCE_AU3_COMMENT) {
559 levelNext--;
561 // fold till the one but last line for Blockcomment lines
562 else if (IsStreamCommentStyle(stylePrev)
563 && !(styleNext == SCE_AU3_COMMENTBLOCK)
564 && style == SCE_AU3_COMMENTBLOCK) {
565 levelNext--;
566 levelCurrent--;
569 int levelUse = levelCurrent;
570 int lev = levelUse | levelNext << 16;
571 if (visibleChars == 0 && foldCompact)
572 lev |= SC_FOLDLEVELWHITEFLAG;
573 if (levelUse < levelNext) {
574 lev |= SC_FOLDLEVELHEADERFLAG;
576 if (lev != styler.LevelAt(lineCurrent)) {
577 styler.SetLevel(lineCurrent, lev);
579 // reset values for the next line
580 lineCurrent++;
581 stylePrev = style;
582 style = styleNext;
583 levelCurrent = levelNext;
584 visibleChars = 0;
585 // if the last character is an Underscore then don't reset since the line continues on the next line.
586 if (!(chPrev == '_')) {
587 szKeywordlen = 0;
588 szThenlen = 0;
589 FirstWordStart = false;
590 FirstWordEnd = false;
591 ThenFoundLast = false;
594 // save the last processed character
595 if (!isspacechar(ch)) {
596 chPrev = ch;
597 visibleChars++;
605 static const char * const AU3WordLists[] = {
606 "#autoit keywords",
607 "#autoit functions",
608 "#autoit macros",
609 "#autoit Sent keys",
610 "#autoit Pre-processors",
611 "#autoit Special",
614 LexerModule lmAU3(SCLEX_AU3, ColouriseAU3Doc, "au3", FoldAU3Doc , AU3WordLists);