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