LexAU3.cpp 29 KB

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