]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexAU3.cxx
31e4ee52d2d78be5a5ce611d5e1cd1702eca52ed
1 // Scintilla source code edit control
3 // Lexer for AutoIt3 http://www.hiddensoft.com/autoit3
4 // by Jos van der Zande, jvdzande@yahoo.com
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.
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
43 #include "StyleContext.h"
45 #include "Scintilla.h"
48 static inline bool IsTypeCharacter(const int 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
))
65 if (ch
== '+' || ch
== '-' || ch
== '*' || ch
== '/' ||
66 ch
== '&' || ch
== '^' || ch
== '=' || ch
== '<' || ch
== '>' ||
67 ch
== '(' || ch
== ')' || ch
== '[' || ch
== ']' || ch
== ',' )
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
)
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
94 // Add } to the end of the first bit for table lookup later.
95 szKey
[nKeyPos
++] = '}';
97 else if (cTemp
== ' ')
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
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 )
131 return nFlag
; // 1 is bad, 0 is good
135 static void ColouriseAU3Doc(unsigned int startPos
,
136 int length
, int initStyle
,
137 WordList
*keywordlists
[],
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
152 for (; sc
.More(); sc
.Forward()) {
154 sc
.GetCurrentLowered(s
, sizeof(s
));
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
164 {sc
.SetState(SCE_AU3_COMMENTBLOCK
);}
168 case SCE_AU3_COMMENT
:
170 if (sc
.atLineEnd
) {sc
.SetState(SCE_AU3_DEFAULT
);}
173 case SCE_AU3_OPERATOR
:
175 sc
.SetState(SCE_AU3_DEFAULT
);
178 case SCE_AU3_SPECIAL
:
180 if (sc
.atLineEnd
) {sc
.SetState(SCE_AU3_DEFAULT
);}
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
);}
233 if (!IsAWordChar(sc
.ch
)) {sc
.SetState(SCE_AU3_DEFAULT
);}
236 case SCE_AU3_VARIABLE
:
238 if (!IsAWordChar(sc
.ch
)) {sc
.SetState(SCE_AU3_DEFAULT
);}
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
);}
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
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
);
284 sc
.ChangeState(SCE_AU3_STRING
);
286 sc
.SetState(SCE_AU3_STRING
);
288 // check if next portion is again a sendkey
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
);
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
);
322 else if (sc
.ch
== '\'') {
323 sc
.SetState(SCE_AU3_STRING
);
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())
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
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
)) {
376 nePos
--; // skip to next char
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
);
394 if (lineCurrent
> 0) {
396 startPos
= styler
.LineStart(lineCurrent
);
399 // vars for style of previous/current/next lines
400 int style
= GetStyleFirstWord(lineCurrent
,styler
);
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
))) {
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;
418 bool ThenFoundLast
= false;
419 // var for indentlevel
420 int levelCurrent
= SC_FOLDLEVELBASE
;
422 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
423 int levelNext
= levelCurrent
;
425 int visibleChars
= 0;
426 char chNext
= styler
.SafeGetCharAt(startPos
);
429 for (int i
= startPos
; i
< endPos
; i
++) {
431 chNext
= styler
.SafeGetCharAt(i
+ 1);
432 if (IsAWordChar(ch
)) {
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
)) {
441 szKeyword
[szKeywordlen
] = '\0';
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
)) {
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;
475 szThen
[szThenlen
++] = static_cast<char>(tolower(ch
));
476 if (szThenlen
== 5) {
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
) {
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 ) {
502 // create double Fold for select because Case will subtract one of the current level
503 if (strcmp(szKeyword
,"select") == 0) {
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){
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 ) {
519 // end the double fold for this word before the current line
520 if (strcmp(szKeyword
,"endselect") == 0 ) {
526 // end the fold for these words on the current line
527 if (strcmp(szKeyword
,"#endregion") == 0 ) {
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
)) {
541 // fold till the last line for normal comment lines
542 else if (stylePrev
== SCE_AU3_PREPROCESSOR
&& !(styleNext
== SCE_AU3_PREPROCESSOR
)) {
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
) {
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
) {
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
) {
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
583 levelCurrent
= levelNext
;
585 // if the last character is an Underscore then don't reset since the line continues on the next line.
586 if (!(chPrev
== '_')) {
589 FirstWordStart
= false;
590 FirstWordEnd
= false;
591 ThenFoundLast
= false;
594 // save the last processed character
595 if (!isspacechar(ch
)) {
605 static const char * const AU3WordLists
[] = {
610 "#autoit Pre-processors",
614 LexerModule
lmAU3(SCLEX_AU3
, ColouriseAU3Doc
, "au3", FoldAU3Doc
, AU3WordLists
);