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