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