]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexAU3.cxx
cfff9279dbf39bb5e548293a7e3225c5fe9f36ec
[wxWidgets.git] / src / stc / scintilla / src / LexAU3.cxx
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 // Sep 5, 2004 - Added logic to handle colourizing words on the last line.
29 // Typed Characters now show as "default" till they match any table.
30 // Oct 10, 2004 - Added logic to show Comments in "Special" directives.
31 // Nov 1, 2004 - Added better testing for Numbers supporting x and e notation.
32 // Nov 28, 2004 - Added logic to handle continuation lines for syntax highlighting.
33 // Jan 10, 2005 - Added Abbreviations Keyword used for expansion
34 // Mar 24, 2005 - Updated Abbreviations Keywords to fix when followed by Operator.
35 // Apr 18, 2005 - Updated #CE/#Comment-End logic to take a linecomment ";" into account
36 // - Added folding support for With...EndWith
37 // - Added support for a DOT in variable names
38 // - Fixed Underscore in CommentBlock
39 // May 23, 2005 - Fixed the SentKey lexing in case of a missing }
40 // Aug 11, 2005 - Fixed possible bug with s_save length > 100.
41 // Aug 23, 2005 - Added Switch/endswitch support to the folding logic.
42 // Sep 27, 2005 - Fixed the SentKey lexing logic in case of multiple sentkeys.
43 // Mar 12, 2006 - Fixed issue with <> coloring as String in stead of Operator in rare occasions.
44 // Apr 8, 2006 - Added support for AutoIt3 Standard UDF library (SCE_AU3_UDF)
45 // Mar 9, 2007 - Fixed bug with + following a String getting the wrong Color.
46 // Jun 20, 2007 - Fixed Commentblock issue when LF's are used as EOL.
47 // Jul 26, 2007 - Fixed #endregion undetected bug.
48 //
49 // Copyright for Scintilla: 1998-2001 by Neil Hodgson <neilh@scintilla.org>
50 // The License.txt file describes the conditions under which this software may be distributed.
51 // Scintilla source code edit control
52
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include <stdio.h>
57 #include <stdarg.h>
58
59 #include "Platform.h"
60
61 #include "PropSet.h"
62 #include "Accessor.h"
63 #include "StyleContext.h"
64 #include "KeyWords.h"
65 #include "Scintilla.h"
66 #include "SciLexer.h"
67
68 #ifdef SCI_NAMESPACE
69 using namespace Scintilla;
70 #endif
71
72 static inline bool IsTypeCharacter(const int ch)
73 {
74 return ch == '$';
75 }
76 static inline bool IsAWordChar(const int ch)
77 {
78 return (ch < 0x80) && (isalnum(ch) || ch == '_');
79 }
80
81 static inline bool IsAWordStart(const int ch)
82 {
83 return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '@' || ch == '#' || ch == '$' || ch == '.');
84 }
85
86 static inline bool IsAOperator(char ch) {
87 if (isascii(ch) && isalnum(ch))
88 return false;
89 if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
90 ch == '&' || ch == '^' || ch == '=' || ch == '<' || ch == '>' ||
91 ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == ',' )
92 return true;
93 return false;
94 }
95
96 ///////////////////////////////////////////////////////////////////////////////
97 // GetSendKey() filters the portion before and after a/multiple space(s)
98 // and return the first portion to be looked-up in the table
99 // also check if the second portion is valid... (up,down.on.off,toggle or a number)
100 ///////////////////////////////////////////////////////////////////////////////
101
102 static int GetSendKey(const char *szLine, char *szKey)
103 {
104 int nFlag = 0;
105 int nStartFound = 0;
106 int nKeyPos = 0;
107 int nSpecPos= 0;
108 int nSpecNum= 1;
109 int nPos = 0;
110 char cTemp;
111 char szSpecial[100];
112
113 // split the portion of the sendkey in the part before and after the spaces
114 while ( ( (cTemp = szLine[nPos]) != '\0'))
115 {
116 // skip leading Ctrl/Shift/Alt state
117 if (cTemp == '{') {
118 nStartFound = 1;
119 }
120 //
121 if (nStartFound == 1) {
122 if ((cTemp == ' ') && (nFlag == 0) ) // get the stuff till first space
123 {
124 nFlag = 1;
125 // Add } to the end of the first bit for table lookup later.
126 szKey[nKeyPos++] = '}';
127 }
128 else if (cTemp == ' ')
129 {
130 // skip other spaces
131 }
132 else if (nFlag == 0)
133 {
134 // save first portion into var till space or } is hit
135 szKey[nKeyPos++] = cTemp;
136 }
137 else if ((nFlag == 1) && (cTemp != '}'))
138 {
139 // Save second portion into var...
140 szSpecial[nSpecPos++] = cTemp;
141 // check if Second portion is all numbers for repeat fuction
142 if (isdigit(cTemp) == false) {nSpecNum = 0;}
143 }
144 }
145 nPos++; // skip to next char
146
147 } // End While
148
149
150 // Check if the second portion is either a number or one of these keywords
151 szKey[nKeyPos] = '\0';
152 szSpecial[nSpecPos] = '\0';
153 if (strcmp(szSpecial,"down")== 0 || strcmp(szSpecial,"up")== 0 ||
154 strcmp(szSpecial,"on")== 0 || strcmp(szSpecial,"off")== 0 ||
155 strcmp(szSpecial,"toggle")== 0 || nSpecNum == 1 )
156 {
157 nFlag = 0;
158 }
159 else
160 {
161 nFlag = 1;
162 }
163 return nFlag; // 1 is bad, 0 is good
164
165 } // GetSendKey()
166
167 //
168 // Routine to check the last "none comment" character on a line to see if its a continuation
169 //
170 static bool IsContinuationLine(unsigned int szLine, Accessor &styler)
171 {
172 int nsPos = styler.LineStart(szLine);
173 int nePos = styler.LineStart(szLine+1) - 2;
174 //int stylech = styler.StyleAt(nsPos);
175 while (nsPos < nePos)
176 {
177 //stylech = styler.StyleAt(nePos);
178 int stylech = styler.StyleAt(nsPos);
179 if (!(stylech == SCE_AU3_COMMENT)) {
180 char ch = styler.SafeGetCharAt(nePos);
181 if (!isspacechar(ch)) {
182 if (ch == '_')
183 return true;
184 else
185 return false;
186 }
187 }
188 nePos--; // skip to next char
189 } // End While
190 return false;
191 } // IsContinuationLine()
192
193 //
194 // syntax highlighting logic
195 static void ColouriseAU3Doc(unsigned int startPos,
196 int length, int initStyle,
197 WordList *keywordlists[],
198 Accessor &styler) {
199
200 WordList &keywords = *keywordlists[0];
201 WordList &keywords2 = *keywordlists[1];
202 WordList &keywords3 = *keywordlists[2];
203 WordList &keywords4 = *keywordlists[3];
204 WordList &keywords5 = *keywordlists[4];
205 WordList &keywords6 = *keywordlists[5];
206 WordList &keywords7 = *keywordlists[6];
207 WordList &keywords8 = *keywordlists[7];
208 // find the first previous line without continuation character at the end
209 int lineCurrent = styler.GetLine(startPos);
210 int s_startPos = startPos;
211 // When not inside a Block comment: find First line without _
212 if (!(initStyle==SCE_AU3_COMMENTBLOCK)) {
213 while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) ||
214 (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) {
215 lineCurrent--;
216 startPos = styler.LineStart(lineCurrent); // get start position
217 initStyle = 0; // reset the start style to 0
218 }
219 }
220 // Set the new length to include it from the start and set the start position
221 length = length + s_startPos - startPos; // correct the total length to process
222 styler.StartAt(startPos);
223
224 StyleContext sc(startPos, length, initStyle, styler);
225 char si; // string indicator "=1 '=2
226 char ni; // Numeric indicator error=9 normal=0 normal+dec=1 hex=2 Enot=3
227 char ci; // comment indicator 0=not linecomment(;)
228 char s_save[100];
229 si=0;
230 ni=0;
231 ci=0;
232 //$$$
233 for (; sc.More(); sc.Forward()) {
234 char s[100];
235 sc.GetCurrentLowered(s, sizeof(s));
236 // **********************************************
237 // save the total current word for eof processing
238 if (IsAWordChar(sc.ch) || sc.ch == '}')
239 {
240 strcpy(s_save,s);
241 int tp = strlen(s_save);
242 if (tp < 99) {
243 s_save[tp] = static_cast<char>(tolower(sc.ch));
244 s_save[tp+1] = '\0';
245 }
246 }
247 // **********************************************
248 //
249 switch (sc.state)
250 {
251 case SCE_AU3_COMMENTBLOCK:
252 {
253 //Reset at line end
254 if (sc.atLineEnd) {
255 ci=0;
256 if (strcmp(s, "#ce")== 0 || strcmp(s, "#comments-end")== 0) {
257 if (sc.atLineEnd)
258 sc.SetState(SCE_AU3_DEFAULT);
259 else
260 sc.SetState(SCE_AU3_COMMENTBLOCK);
261 }
262 break;
263 }
264 //skip rest of line when a ; is encountered
265 if (sc.chPrev == ';') {
266 ci=2;
267 sc.SetState(SCE_AU3_COMMENTBLOCK);
268 }
269 // skip rest of the line
270 if (ci==2)
271 break;
272 // check when first character is detected on the line
273 if (ci==0) {
274 if (IsAWordStart(static_cast<char>(sc.ch)) || IsAOperator(static_cast<char>(sc.ch))) {
275 ci=1;
276 sc.SetState(SCE_AU3_COMMENTBLOCK);
277 }
278 break;
279 }
280 if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && strcmp(s, "#comments") == 0))) {
281 if ((strcmp(s, "#ce")== 0 || strcmp(s, "#comments-end")== 0))
282 sc.SetState(SCE_AU3_COMMENT); // set to comment line for the rest of the line
283 else
284 ci=2; // line doesn't begin with #CE so skip the rest of the line
285 }
286 break;
287 }
288 case SCE_AU3_COMMENT:
289 {
290 if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
291 break;
292 }
293 case SCE_AU3_OPERATOR:
294 {
295 // check if its a COMobject
296 if (sc.chPrev == '.' && IsAWordChar(sc.ch)) {
297 sc.SetState(SCE_AU3_COMOBJ);
298 }
299 else {
300 sc.SetState(SCE_AU3_DEFAULT);
301 }
302 break;
303 }
304 case SCE_AU3_SPECIAL:
305 {
306 if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);}
307 if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
308 break;
309 }
310 case SCE_AU3_KEYWORD:
311 {
312 if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && (strcmp(s, "#comments") == 0 || strcmp(s, "#include") == 0))))
313 {
314 if (!IsTypeCharacter(sc.ch))
315 {
316 if (strcmp(s, "#cs")== 0 || strcmp(s, "#comments-start")== 0 )
317 {
318 sc.ChangeState(SCE_AU3_COMMENTBLOCK);
319 sc.SetState(SCE_AU3_COMMENTBLOCK);
320 break;
321 }
322 else if (keywords.InList(s)) {
323 sc.ChangeState(SCE_AU3_KEYWORD);
324 sc.SetState(SCE_AU3_DEFAULT);
325 }
326 else if (keywords2.InList(s)) {
327 sc.ChangeState(SCE_AU3_FUNCTION);
328 sc.SetState(SCE_AU3_DEFAULT);
329 }
330 else if (keywords3.InList(s)) {
331 sc.ChangeState(SCE_AU3_MACRO);
332 sc.SetState(SCE_AU3_DEFAULT);
333 }
334 else if (keywords5.InList(s)) {
335 sc.ChangeState(SCE_AU3_PREPROCESSOR);
336 sc.SetState(SCE_AU3_DEFAULT);
337 if (strcmp(s, "#include")== 0)
338 {
339 si = 3; // use to determine string start for #inlude <>
340 }
341 }
342 else if (keywords6.InList(s)) {
343 sc.ChangeState(SCE_AU3_SPECIAL);
344 sc.SetState(SCE_AU3_SPECIAL);
345 }
346 else if ((keywords7.InList(s)) && (!IsAOperator(static_cast<char>(sc.ch)))) {
347 sc.ChangeState(SCE_AU3_EXPAND);
348 sc.SetState(SCE_AU3_DEFAULT);
349 }
350 else if (keywords8.InList(s)) {
351 sc.ChangeState(SCE_AU3_UDF);
352 sc.SetState(SCE_AU3_DEFAULT);
353 }
354 else if (strcmp(s, "_") == 0) {
355 sc.ChangeState(SCE_AU3_OPERATOR);
356 sc.SetState(SCE_AU3_DEFAULT);
357 }
358 else if (!IsAWordChar(sc.ch)) {
359 sc.ChangeState(SCE_AU3_DEFAULT);
360 sc.SetState(SCE_AU3_DEFAULT);
361 }
362 }
363 }
364 if (sc.atLineEnd) {
365 sc.SetState(SCE_AU3_DEFAULT);}
366 break;
367 }
368 case SCE_AU3_NUMBER:
369 {
370 // Numeric indicator error=9 normal=0 normal+dec=1 hex=2 E-not=3
371 //
372 // test for Hex notation
373 if (strcmp(s, "0") == 0 && (sc.ch == 'x' || sc.ch == 'X') && ni == 0)
374 {
375 ni = 2;
376 break;
377 }
378 // test for E notation
379 if (IsADigit(sc.chPrev) && (sc.ch == 'e' || sc.ch == 'E') && ni <= 1)
380 {
381 ni = 3;
382 break;
383 }
384 // Allow Hex characters inside hex numeric strings
385 if ((ni == 2) &&
386 (sc.ch == 'a' || sc.ch == 'b' || sc.ch == 'c' || sc.ch == 'd' || sc.ch == 'e' || sc.ch == 'f' ||
387 sc.ch == 'A' || sc.ch == 'B' || sc.ch == 'C' || sc.ch == 'D' || sc.ch == 'E' || sc.ch == 'F' ))
388 {
389 break;
390 }
391 // test for 1 dec point only
392 if (sc.ch == '.')
393 {
394 if (ni==0)
395 {
396 ni=1;
397 }
398 else
399 {
400 ni=9;
401 }
402 break;
403 }
404 // end of numeric string ?
405 if (!(IsADigit(sc.ch)))
406 {
407 if (ni==9)
408 {
409 sc.ChangeState(SCE_AU3_DEFAULT);
410 }
411 sc.SetState(SCE_AU3_DEFAULT);
412 }
413 break;
414 }
415 case SCE_AU3_VARIABLE:
416 {
417 // Check if its a COMObject
418 if (sc.ch == '.' && !IsADigit(sc.chNext)) {
419 sc.SetState(SCE_AU3_OPERATOR);
420 }
421 else if (!IsAWordChar(sc.ch)) {
422 sc.SetState(SCE_AU3_DEFAULT);
423 }
424 break;
425 }
426 case SCE_AU3_COMOBJ:
427 {
428 if (!(IsAWordChar(sc.ch))) {
429 sc.SetState(SCE_AU3_DEFAULT);
430 }
431 break;
432 }
433 case SCE_AU3_STRING:
434 {
435 // check for " to end a double qouted string or
436 // check for ' to end a single qouted string
437 if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\'') || (si == 3 && sc.ch == '>'))
438 {
439 sc.ForwardSetState(SCE_AU3_DEFAULT);
440 si=0;
441 break;
442 }
443 if (sc.atLineEnd)
444 {
445 si=0;
446 // at line end and not found a continuation char then reset to default
447 int lineCurrent = styler.GetLine(sc.currentPos);
448 if (!IsContinuationLine(lineCurrent,styler))
449 {
450 sc.SetState(SCE_AU3_DEFAULT);
451 break;
452 }
453 }
454 // find Sendkeys in a STRING
455 if (sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' ) {
456 sc.SetState(SCE_AU3_SENT);}
457 break;
458 }
459
460 case SCE_AU3_SENT:
461 {
462 // Send key string ended
463 if (sc.chPrev == '}' && sc.ch != '}')
464 {
465 // set color to SENDKEY when valid sendkey .. else set back to regular string
466 char sk[100];
467 // split {111 222} and return {111} and check if 222 is valid.
468 // if return code = 1 then invalid 222 so must be string
469 if (GetSendKey(s,sk))
470 {
471 sc.ChangeState(SCE_AU3_STRING);
472 }
473 // if single char between {?} then its ok as sendkey for a single character
474 else if (strlen(sk) == 3)
475 {
476 sc.ChangeState(SCE_AU3_SENT);
477 }
478 // if sendkey {111} is in table then ok as sendkey
479 else if (keywords4.InList(sk))
480 {
481 sc.ChangeState(SCE_AU3_SENT);
482 }
483 else
484 {
485 sc.ChangeState(SCE_AU3_STRING);
486 }
487 sc.SetState(SCE_AU3_STRING);
488 }
489 else
490 {
491 // check if the start is a valid SendKey start
492 int nPos = 0;
493 int nState = 1;
494 char cTemp;
495 while (!(nState == 2) && ((cTemp = s[nPos]) != '\0'))
496 {
497 if (cTemp == '{' && nState == 1)
498 {
499 nState = 2;
500 }
501 if (nState == 1 && !(cTemp == '+' || cTemp == '!' || cTemp == '^' || cTemp == '#' ))
502 {
503 nState = 0;
504 }
505 nPos++;
506 }
507 //Verify characters infront of { ... if not assume regular string
508 if (nState == 1 && (!(sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' ))) {
509 sc.ChangeState(SCE_AU3_STRING);
510 sc.SetState(SCE_AU3_STRING);
511 }
512 // If invalid character found then assume its a regular string
513 if (nState == 0) {
514 sc.ChangeState(SCE_AU3_STRING);
515 sc.SetState(SCE_AU3_STRING);
516 }
517 }
518 // check if next portion is again a sendkey
519 if (sc.atLineEnd)
520 {
521 sc.ChangeState(SCE_AU3_STRING);
522 sc.SetState(SCE_AU3_DEFAULT);
523 si = 0; // reset string indicator
524 }
525 //* check in next characters following a sentkey are again a sent key
526 // Need this test incase of 2 sentkeys like {F1}{ENTER} but not detect {{}
527 if (sc.state == SCE_AU3_STRING && (sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' )) {
528 sc.SetState(SCE_AU3_SENT);}
529 // check to see if the string ended...
530 // Sendkey string isn't complete but the string ended....
531 if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\''))
532 {
533 sc.ChangeState(SCE_AU3_STRING);
534 sc.ForwardSetState(SCE_AU3_DEFAULT);
535 }
536 break;
537 }
538 } //switch (sc.state)
539
540 // Determine if a new state should be entered:
541
542 if (sc.state == SCE_AU3_DEFAULT)
543 {
544 if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);}
545 else if (sc.ch == '#') {sc.SetState(SCE_AU3_KEYWORD);}
546 else if (sc.ch == '$') {sc.SetState(SCE_AU3_VARIABLE);}
547 else if (sc.ch == '.' && !IsADigit(sc.chNext)) {sc.SetState(SCE_AU3_OPERATOR);}
548 else if (sc.ch == '@') {sc.SetState(SCE_AU3_KEYWORD);}
549 //else if (sc.ch == '_') {sc.SetState(SCE_AU3_KEYWORD);}
550 else if (sc.ch == '<' && si==3) {sc.SetState(SCE_AU3_STRING);} // string after #include
551 else if (sc.ch == '\"') {
552 sc.SetState(SCE_AU3_STRING);
553 si = 1; }
554 else if (sc.ch == '\'') {
555 sc.SetState(SCE_AU3_STRING);
556 si = 2; }
557 else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext)))
558 {
559 sc.SetState(SCE_AU3_NUMBER);
560 ni = 0;
561 }
562 else if (IsAWordStart(sc.ch)) {sc.SetState(SCE_AU3_KEYWORD);}
563 else if (IsAOperator(static_cast<char>(sc.ch))) {sc.SetState(SCE_AU3_OPERATOR);}
564 else if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
565 }
566 } //for (; sc.More(); sc.Forward())
567
568 //*************************************
569 // Colourize the last word correctly
570 //*************************************
571 if (sc.state == SCE_AU3_KEYWORD)
572 {
573 if (strcmp(s_save, "#cs")== 0 || strcmp(s_save, "#comments-start")== 0 )
574 {
575 sc.ChangeState(SCE_AU3_COMMENTBLOCK);
576 sc.SetState(SCE_AU3_COMMENTBLOCK);
577 }
578 else if (keywords.InList(s_save)) {
579 sc.ChangeState(SCE_AU3_KEYWORD);
580 sc.SetState(SCE_AU3_KEYWORD);
581 }
582 else if (keywords2.InList(s_save)) {
583 sc.ChangeState(SCE_AU3_FUNCTION);
584 sc.SetState(SCE_AU3_FUNCTION);
585 }
586 else if (keywords3.InList(s_save)) {
587 sc.ChangeState(SCE_AU3_MACRO);
588 sc.SetState(SCE_AU3_MACRO);
589 }
590 else if (keywords5.InList(s_save)) {
591 sc.ChangeState(SCE_AU3_PREPROCESSOR);
592 sc.SetState(SCE_AU3_PREPROCESSOR);
593 }
594 else if (keywords6.InList(s_save)) {
595 sc.ChangeState(SCE_AU3_SPECIAL);
596 sc.SetState(SCE_AU3_SPECIAL);
597 }
598 else if (keywords7.InList(s_save) && sc.atLineEnd) {
599 sc.ChangeState(SCE_AU3_EXPAND);
600 sc.SetState(SCE_AU3_EXPAND);
601 }
602 else if (keywords8.InList(s_save)) {
603 sc.ChangeState(SCE_AU3_UDF);
604 sc.SetState(SCE_AU3_UDF);
605 }
606 else {
607 sc.ChangeState(SCE_AU3_DEFAULT);
608 sc.SetState(SCE_AU3_DEFAULT);
609 }
610 }
611 if (sc.state == SCE_AU3_SENT)
612 {
613 // Send key string ended
614 if (sc.chPrev == '}' && sc.ch != '}')
615 {
616 // set color to SENDKEY when valid sendkey .. else set back to regular string
617 char sk[100];
618 // split {111 222} and return {111} and check if 222 is valid.
619 // if return code = 1 then invalid 222 so must be string
620 if (GetSendKey(s_save,sk))
621 {
622 sc.ChangeState(SCE_AU3_STRING);
623 }
624 // if single char between {?} then its ok as sendkey for a single character
625 else if (strlen(sk) == 3)
626 {
627 sc.ChangeState(SCE_AU3_SENT);
628 }
629 // if sendkey {111} is in table then ok as sendkey
630 else if (keywords4.InList(sk))
631 {
632 sc.ChangeState(SCE_AU3_SENT);
633 }
634 else
635 {
636 sc.ChangeState(SCE_AU3_STRING);
637 }
638 sc.SetState(SCE_AU3_STRING);
639 }
640 // check if next portion is again a sendkey
641 if (sc.atLineEnd)
642 {
643 sc.ChangeState(SCE_AU3_STRING);
644 sc.SetState(SCE_AU3_DEFAULT);
645 }
646 }
647 //*************************************
648 sc.Complete();
649 }
650
651 //
652 static bool IsStreamCommentStyle(int style) {
653 return style == SCE_AU3_COMMENT || style == SCE_AU3_COMMENTBLOCK;
654 }
655
656 //
657 // Routine to find first none space on the current line and return its Style
658 // needed for comment lines not starting on pos 1
659 static int GetStyleFirstWord(unsigned int szLine, Accessor &styler)
660 {
661 int nsPos = styler.LineStart(szLine);
662 int nePos = styler.LineStart(szLine+1) - 1;
663 while (isspacechar(styler.SafeGetCharAt(nsPos)) && nsPos < nePos)
664 {
665 nsPos++; // skip to next char
666
667 } // End While
668 return styler.StyleAt(nsPos);
669
670 } // GetStyleFirstWord()
671
672
673 //
674 static void FoldAU3Doc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
675 {
676 int endPos = startPos + length;
677 // get settings from the config files for folding comments and preprocessor lines
678 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
679 bool foldInComment = styler.GetPropertyInt("fold.comment") == 2;
680 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
681 bool foldpreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
682 // Backtrack to previous line in case need to fix its fold status
683 int lineCurrent = styler.GetLine(startPos);
684 if (startPos > 0) {
685 if (lineCurrent > 0) {
686 lineCurrent--;
687 startPos = styler.LineStart(lineCurrent);
688 }
689 }
690 // vars for style of previous/current/next lines
691 int style = GetStyleFirstWord(lineCurrent,styler);
692 int stylePrev = 0;
693 // find the first previous line without continuation character at the end
694 while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) ||
695 (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) {
696 lineCurrent--;
697 startPos = styler.LineStart(lineCurrent);
698 }
699 if (lineCurrent > 0) {
700 stylePrev = GetStyleFirstWord(lineCurrent-1,styler);
701 }
702 // vars for getting first word to check for keywords
703 bool FirstWordStart = false;
704 bool FirstWordEnd = false;
705 char szKeyword[11]="";
706 int szKeywordlen = 0;
707 char szThen[5]="";
708 int szThenlen = 0;
709 bool ThenFoundLast = false;
710 // var for indentlevel
711 int levelCurrent = SC_FOLDLEVELBASE;
712 if (lineCurrent > 0)
713 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
714 int levelNext = levelCurrent;
715 //
716 int visibleChars = 0;
717 char chNext = styler.SafeGetCharAt(startPos);
718 char chPrev = ' ';
719 //
720 for (int i = startPos; i < endPos; i++) {
721 char ch = chNext;
722 chNext = styler.SafeGetCharAt(i + 1);
723 if (IsAWordChar(ch)) {
724 visibleChars++;
725 }
726 // get the syle for the current character neede to check in comment
727 int stylech = styler.StyleAt(i);
728 // get first word for the line for indent check max 9 characters
729 if (FirstWordStart && (!(FirstWordEnd))) {
730 if (!IsAWordChar(ch)) {
731 FirstWordEnd = true;
732 szKeyword[szKeywordlen] = '\0';
733 }
734 else {
735 if (szKeywordlen < 10) {
736 szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
737 }
738 }
739 }
740 // start the capture of the first word
741 if (!(FirstWordStart)) {
742 if (IsAWordChar(ch) || IsAWordStart(ch) || ch == ';') {
743 FirstWordStart = true;
744 szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
745 }
746 }
747 // only process this logic when not in comment section
748 if (!(stylech == SCE_AU3_COMMENT)) {
749 if (ThenFoundLast) {
750 if (IsAWordChar(ch)) {
751 ThenFoundLast = false;
752 }
753 }
754 // find out if the word "then" is the last on a "if" line
755 if (FirstWordEnd && strcmp(szKeyword,"if") == 0) {
756 if (szThenlen == 4) {
757 szThen[0] = szThen[1];
758 szThen[1] = szThen[2];
759 szThen[2] = szThen[3];
760 szThen[3] = static_cast<char>(tolower(ch));
761 if (strcmp(szThen,"then") == 0 ) {
762 ThenFoundLast = true;
763 }
764 }
765 else {
766 szThen[szThenlen++] = static_cast<char>(tolower(ch));
767 if (szThenlen == 5) {
768 szThen[4] = '\0';
769 }
770 }
771 }
772 }
773 // End of Line found so process the information
774 if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
775 // **************************
776 // Folding logic for Keywords
777 // **************************
778 // if a keyword is found on the current line and the line doesn't end with _ (continuation)
779 // and we are not inside a commentblock.
780 if (szKeywordlen > 0 && (!(chPrev == '_')) &&
781 ((!(IsStreamCommentStyle(style)) || foldInComment)) ) {
782 szKeyword[szKeywordlen] = '\0';
783 // only fold "if" last keyword is "then" (else its a one line if)
784 if (strcmp(szKeyword,"if") == 0 && ThenFoundLast) {
785 levelNext++;
786 }
787 // create new fold for these words
788 if (strcmp(szKeyword,"do") == 0 || strcmp(szKeyword,"for") == 0 ||
789 strcmp(szKeyword,"func") == 0 || strcmp(szKeyword,"while") == 0||
790 strcmp(szKeyword,"with") == 0 || strcmp(szKeyword,"#region") == 0 ) {
791 levelNext++;
792 }
793 // create double Fold for select&switch because Case will subtract one of the current level
794 if (strcmp(szKeyword,"select") == 0 || strcmp(szKeyword,"switch") == 0) {
795 levelNext++;
796 levelNext++;
797 }
798 // end the fold for these words before the current line
799 if (strcmp(szKeyword,"endfunc") == 0 || strcmp(szKeyword,"endif") == 0 ||
800 strcmp(szKeyword,"next") == 0 || strcmp(szKeyword,"until") == 0 ||
801 strcmp(szKeyword,"endwith") == 0 ||strcmp(szKeyword,"wend") == 0){
802 levelNext--;
803 levelCurrent--;
804 }
805 // end the fold for these words before the current line and Start new fold
806 if (strcmp(szKeyword,"case") == 0 || strcmp(szKeyword,"else") == 0 ||
807 strcmp(szKeyword,"elseif") == 0 ) {
808 levelCurrent--;
809 }
810 // end the double fold for this word before the current line
811 if (strcmp(szKeyword,"endselect") == 0 || strcmp(szKeyword,"endswitch") == 0 ) {
812 levelNext--;
813 levelNext--;
814 levelCurrent--;
815 levelCurrent--;
816 }
817 // end the fold for these words on the current line
818 if (strcmp(szKeyword,"#endregion") == 0 ) {
819 levelNext--;
820 }
821 }
822 // Preprocessor and Comment folding
823 int styleNext = GetStyleFirstWord(lineCurrent + 1,styler);
824 // *************************************
825 // Folding logic for preprocessor blocks
826 // *************************************
827 // process preprosessor line
828 if (foldpreprocessor && style == SCE_AU3_PREPROCESSOR) {
829 if (!(stylePrev == SCE_AU3_PREPROCESSOR) && (styleNext == SCE_AU3_PREPROCESSOR)) {
830 levelNext++;
831 }
832 // fold till the last line for normal comment lines
833 else if (stylePrev == SCE_AU3_PREPROCESSOR && !(styleNext == SCE_AU3_PREPROCESSOR)) {
834 levelNext--;
835 }
836 }
837 // *********************************
838 // Folding logic for Comment blocks
839 // *********************************
840 if (foldComment && IsStreamCommentStyle(style)) {
841 // Start of a comment block
842 if (!(stylePrev==style) && IsStreamCommentStyle(styleNext) && styleNext==style) {
843 levelNext++;
844 }
845 // fold till the last line for normal comment lines
846 else if (IsStreamCommentStyle(stylePrev)
847 && !(styleNext == SCE_AU3_COMMENT)
848 && stylePrev == SCE_AU3_COMMENT
849 && style == SCE_AU3_COMMENT) {
850 levelNext--;
851 }
852 // fold till the one but last line for Blockcomment lines
853 else if (IsStreamCommentStyle(stylePrev)
854 && !(styleNext == SCE_AU3_COMMENTBLOCK)
855 && style == SCE_AU3_COMMENTBLOCK) {
856 levelNext--;
857 levelCurrent--;
858 }
859 }
860 int levelUse = levelCurrent;
861 int lev = levelUse | levelNext << 16;
862 if (visibleChars == 0 && foldCompact)
863 lev |= SC_FOLDLEVELWHITEFLAG;
864 if (levelUse < levelNext) {
865 lev |= SC_FOLDLEVELHEADERFLAG;
866 }
867 if (lev != styler.LevelAt(lineCurrent)) {
868 styler.SetLevel(lineCurrent, lev);
869 }
870 // reset values for the next line
871 lineCurrent++;
872 stylePrev = style;
873 style = styleNext;
874 levelCurrent = levelNext;
875 visibleChars = 0;
876 // if the last character is an Underscore then don't reset since the line continues on the next line.
877 if (!(chPrev == '_')) {
878 szKeywordlen = 0;
879 szThenlen = 0;
880 FirstWordStart = false;
881 FirstWordEnd = false;
882 ThenFoundLast = false;
883 }
884 }
885 // save the last processed character
886 if (!isspacechar(ch)) {
887 chPrev = ch;
888 visibleChars++;
889 }
890 }
891 }
892
893
894 //
895
896 static const char * const AU3WordLists[] = {
897 "#autoit keywords",
898 "#autoit functions",
899 "#autoit macros",
900 "#autoit Sent keys",
901 "#autoit Pre-processors",
902 "#autoit Special",
903 "#autoit Expand",
904 "#autoit UDF",
905 0
906 };
907 LexerModule lmAU3(SCLEX_AU3, ColouriseAU3Doc, "au3", FoldAU3Doc , AU3WordLists);