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