LexTCL.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Scintilla source code edit control
  2. /** @file LexTCL.cxx
  3. ** Lexer for TCL language.
  4. **/
  5. // Copyright 1998-2001 by Andre Arpin <arpin@kingston.net>
  6. // The License.txt file describes the conditions under which this software may be distributed.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <stdarg.h>
  11. #include <assert.h>
  12. #include <ctype.h>
  13. #include "ILexer.h"
  14. #include "Scintilla.h"
  15. #include "SciLexer.h"
  16. #include "WordList.h"
  17. #include "LexAccessor.h"
  18. #include "Accessor.h"
  19. #include "StyleContext.h"
  20. #include "CharacterSet.h"
  21. #include "LexerModule.h"
  22. #ifdef SCI_NAMESPACE
  23. using namespace Scintilla;
  24. #endif
  25. // Extended to accept accented characters
  26. static inline bool IsAWordChar(int ch) {
  27. return ch >= 0x80 ||
  28. (isalnum(ch) || ch == '_' || ch ==':' || ch=='.'); // : name space separator
  29. }
  30. static inline bool IsAWordStart(int ch) {
  31. return ch >= 0x80 || (ch ==':' || isalpha(ch) || ch == '_');
  32. }
  33. static inline bool IsANumberChar(int ch) {
  34. // Not exactly following number definition (several dots are seen as OK, etc.)
  35. // but probably enough in most cases.
  36. return (ch < 0x80) &&
  37. (IsADigit(ch, 0x10) || toupper(ch) == 'E' ||
  38. ch == '.' || ch == '-' || ch == '+');
  39. }
  40. static void ColouriseTCLDoc(Sci_PositionU startPos, Sci_Position length, int , WordList *keywordlists[], Accessor &styler) {
  41. #define isComment(s) (s==SCE_TCL_COMMENT || s==SCE_TCL_COMMENTLINE || s==SCE_TCL_COMMENT_BOX || s==SCE_TCL_BLOCK_COMMENT)
  42. bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
  43. bool commentLevel = false;
  44. bool subBrace = false; // substitution begin with a brace ${.....}
  45. enum tLineState {LS_DEFAULT, LS_OPEN_COMMENT, LS_OPEN_DOUBLE_QUOTE, LS_COMMENT_BOX, LS_MASK_STATE = 0xf,
  46. LS_COMMAND_EXPECTED = 16, LS_BRACE_ONLY = 32
  47. } lineState = LS_DEFAULT;
  48. bool prevSlash = false;
  49. int currentLevel = 0;
  50. bool expected = 0;
  51. bool subParen = 0;
  52. Sci_Position currentLine = styler.GetLine(startPos);
  53. if (currentLine > 0)
  54. currentLine--;
  55. length += startPos - styler.LineStart(currentLine);
  56. // make sure lines overlap
  57. startPos = styler.LineStart(currentLine);
  58. WordList &keywords = *keywordlists[0];
  59. WordList &keywords2 = *keywordlists[1];
  60. WordList &keywords3 = *keywordlists[2];
  61. WordList &keywords4 = *keywordlists[3];
  62. WordList &keywords5 = *keywordlists[4];
  63. WordList &keywords6 = *keywordlists[5];
  64. WordList &keywords7 = *keywordlists[6];
  65. WordList &keywords8 = *keywordlists[7];
  66. WordList &keywords9 = *keywordlists[8];
  67. if (currentLine > 0) {
  68. int ls = styler.GetLineState(currentLine - 1);
  69. lineState = tLineState(ls & LS_MASK_STATE);
  70. expected = LS_COMMAND_EXPECTED == tLineState(ls & LS_COMMAND_EXPECTED);
  71. subBrace = LS_BRACE_ONLY == tLineState(ls & LS_BRACE_ONLY);
  72. currentLevel = styler.LevelAt(currentLine - 1) >> 17;
  73. commentLevel = (styler.LevelAt(currentLine - 1) >> 16) & 1;
  74. } else
  75. styler.SetLevel(0, SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG);
  76. bool visibleChars = false;
  77. int previousLevel = currentLevel;
  78. StyleContext sc(startPos, length, SCE_TCL_DEFAULT, styler);
  79. for (; ; sc.Forward()) {
  80. next:
  81. if (sc.ch=='\r' && sc.chNext == '\n') // only ignore \r on PC process on the mac
  82. continue;
  83. bool atEnd = !sc.More(); // make sure we coloured the last word
  84. if (lineState != LS_DEFAULT) {
  85. sc.SetState(SCE_TCL_DEFAULT);
  86. if (lineState == LS_OPEN_COMMENT)
  87. sc.SetState(SCE_TCL_COMMENTLINE);
  88. else if (lineState == LS_OPEN_DOUBLE_QUOTE)
  89. sc.SetState(SCE_TCL_IN_QUOTE);
  90. else if (lineState == LS_COMMENT_BOX && (sc.ch == '#' || (sc.ch == ' ' && sc.chNext=='#')))
  91. sc.SetState(SCE_TCL_COMMENT_BOX);
  92. lineState = LS_DEFAULT;
  93. }
  94. if (subBrace) { // ${ overrides every thing even \ except }
  95. if (sc.ch == '}') {
  96. subBrace = false;
  97. sc.SetState(SCE_TCL_OPERATOR);
  98. sc.ForwardSetState(SCE_TCL_DEFAULT);
  99. goto next;
  100. } else
  101. sc.SetState(SCE_TCL_SUB_BRACE);
  102. if (!sc.atLineEnd)
  103. continue;
  104. } else if (sc.state == SCE_TCL_DEFAULT || sc.state ==SCE_TCL_OPERATOR) {
  105. expected &= isspacechar(static_cast<unsigned char>(sc.ch)) || IsAWordStart(sc.ch) || sc.ch =='#';
  106. } else if (sc.state == SCE_TCL_SUBSTITUTION) {
  107. switch (sc.ch) {
  108. case '(':
  109. subParen=true;
  110. sc.SetState(SCE_TCL_OPERATOR);
  111. sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
  112. continue;
  113. case ')':
  114. sc.SetState(SCE_TCL_OPERATOR);
  115. subParen=false;
  116. continue;
  117. case '$':
  118. continue;
  119. case ',':
  120. sc.SetState(SCE_TCL_OPERATOR);
  121. if (subParen)
  122. sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
  123. continue;
  124. default :
  125. // maybe spaces should be allowed ???
  126. if (!IsAWordChar(sc.ch)) { // probably the code is wrong
  127. sc.SetState(SCE_TCL_DEFAULT);
  128. subParen = 0;
  129. }
  130. break;
  131. }
  132. } else if (isComment(sc.state)) {
  133. } else if (!IsAWordChar(sc.ch)) {
  134. if ((sc.state == SCE_TCL_IDENTIFIER && expected) || sc.state == SCE_TCL_MODIFIER) {
  135. char w[100];
  136. char *s=w;
  137. sc.GetCurrent(w, sizeof(w));
  138. if (w[strlen(w)-1]=='\r')
  139. w[strlen(w)-1]=0;
  140. while (*s == ':') // ignore leading : like in ::set a 10
  141. ++s;
  142. bool quote = sc.state == SCE_TCL_IN_QUOTE;
  143. if (commentLevel || expected) {
  144. if (keywords.InList(s)) {
  145. sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD);
  146. } else if (keywords2.InList(s)) {
  147. sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD2);
  148. } else if (keywords3.InList(s)) {
  149. sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD3);
  150. } else if (keywords4.InList(s)) {
  151. sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD4);
  152. } else if (sc.GetRelative(-static_cast<int>(strlen(s))-1) == '{' &&
  153. keywords5.InList(s) && sc.ch == '}') { // {keyword} exactly no spaces
  154. sc.ChangeState(SCE_TCL_EXPAND);
  155. }
  156. if (keywords6.InList(s)) {
  157. sc.ChangeState(SCE_TCL_WORD5);
  158. } else if (keywords7.InList(s)) {
  159. sc.ChangeState(SCE_TCL_WORD6);
  160. } else if (keywords8.InList(s)) {
  161. sc.ChangeState(SCE_TCL_WORD7);
  162. } else if (keywords9.InList(s)) {
  163. sc.ChangeState(SCE_TCL_WORD8);
  164. }
  165. }
  166. expected = false;
  167. sc.SetState(quote ? SCE_TCL_IN_QUOTE : SCE_TCL_DEFAULT);
  168. } else if (sc.state == SCE_TCL_MODIFIER || sc.state == SCE_TCL_IDENTIFIER) {
  169. sc.SetState(SCE_TCL_DEFAULT);
  170. }
  171. }
  172. if (atEnd)
  173. break;
  174. if (sc.atLineEnd) {
  175. lineState = LS_DEFAULT;
  176. currentLine = styler.GetLine(sc.currentPos);
  177. if (foldComment && sc.state!=SCE_TCL_COMMENT && isComment(sc.state)) {
  178. if (currentLevel == 0) {
  179. ++currentLevel;
  180. commentLevel = true;
  181. }
  182. } else {
  183. if (visibleChars && commentLevel) {
  184. --currentLevel;
  185. --previousLevel;
  186. commentLevel = false;
  187. }
  188. }
  189. int flag = 0;
  190. if (!visibleChars)
  191. flag = SC_FOLDLEVELWHITEFLAG;
  192. if (currentLevel > previousLevel)
  193. flag = SC_FOLDLEVELHEADERFLAG;
  194. styler.SetLevel(currentLine, flag + previousLevel + SC_FOLDLEVELBASE + (currentLevel << 17) + (commentLevel << 16));
  195. // Update the line state, so it can be seen by next line
  196. if (sc.state == SCE_TCL_IN_QUOTE) {
  197. lineState = LS_OPEN_DOUBLE_QUOTE;
  198. } else {
  199. if (prevSlash) {
  200. if (isComment(sc.state))
  201. lineState = LS_OPEN_COMMENT;
  202. } else if (sc.state == SCE_TCL_COMMENT_BOX)
  203. lineState = LS_COMMENT_BOX;
  204. }
  205. styler.SetLineState(currentLine,
  206. (subBrace ? LS_BRACE_ONLY : 0) |
  207. (expected ? LS_COMMAND_EXPECTED : 0) | lineState);
  208. if (lineState == LS_COMMENT_BOX)
  209. sc.ForwardSetState(SCE_TCL_COMMENT_BOX);
  210. else if (lineState == LS_OPEN_DOUBLE_QUOTE)
  211. sc.ForwardSetState(SCE_TCL_IN_QUOTE);
  212. else
  213. sc.ForwardSetState(SCE_TCL_DEFAULT);
  214. prevSlash = false;
  215. previousLevel = currentLevel;
  216. goto next;
  217. }
  218. if (prevSlash) {
  219. prevSlash = false;
  220. if (sc.ch == '#' && IsANumberChar(sc.chNext))
  221. sc.ForwardSetState(SCE_TCL_NUMBER);
  222. continue;
  223. }
  224. prevSlash = sc.ch == '\\';
  225. if (isComment(sc.state))
  226. continue;
  227. if (sc.atLineStart) {
  228. visibleChars = false;
  229. if (sc.state!=SCE_TCL_IN_QUOTE && !isComment(sc.state))
  230. {
  231. sc.SetState(SCE_TCL_DEFAULT);
  232. expected = IsAWordStart(sc.ch)|| isspacechar(static_cast<unsigned char>(sc.ch));
  233. }
  234. }
  235. switch (sc.state) {
  236. case SCE_TCL_NUMBER:
  237. if (!IsANumberChar(sc.ch))
  238. sc.SetState(SCE_TCL_DEFAULT);
  239. break;
  240. case SCE_TCL_IN_QUOTE:
  241. if (sc.ch == '"') {
  242. sc.ForwardSetState(SCE_TCL_DEFAULT);
  243. visibleChars = true; // necessary if a " is the first and only character on a line
  244. goto next;
  245. } else if (sc.ch == '[' || sc.ch == ']' || sc.ch == '$') {
  246. sc.SetState(SCE_TCL_OPERATOR);
  247. expected = sc.ch == '[';
  248. sc.ForwardSetState(SCE_TCL_IN_QUOTE);
  249. goto next;
  250. }
  251. continue;
  252. case SCE_TCL_OPERATOR:
  253. sc.SetState(SCE_TCL_DEFAULT);
  254. break;
  255. }
  256. if (sc.ch == '#') {
  257. if (visibleChars) {
  258. if (sc.state != SCE_TCL_IN_QUOTE && expected)
  259. sc.SetState(SCE_TCL_COMMENT);
  260. } else {
  261. sc.SetState(SCE_TCL_COMMENTLINE);
  262. if (sc.chNext == '~')
  263. sc.SetState(SCE_TCL_BLOCK_COMMENT);
  264. if (sc.atLineStart && (sc.chNext == '#' || sc.chNext == '-'))
  265. sc.SetState(SCE_TCL_COMMENT_BOX);
  266. }
  267. }
  268. if (!isspacechar(static_cast<unsigned char>(sc.ch))) {
  269. visibleChars = true;
  270. }
  271. if (sc.ch == '\\') {
  272. prevSlash = true;
  273. continue;
  274. }
  275. // Determine if a new state should be entered.
  276. if (sc.state == SCE_TCL_DEFAULT) {
  277. if (IsAWordStart(sc.ch)) {
  278. sc.SetState(SCE_TCL_IDENTIFIER);
  279. } else if (IsADigit(sc.ch) && !IsAWordChar(sc.chPrev)) {
  280. sc.SetState(SCE_TCL_NUMBER);
  281. } else {
  282. switch (sc.ch) {
  283. case '\"':
  284. sc.SetState(SCE_TCL_IN_QUOTE);
  285. break;
  286. case '{':
  287. sc.SetState(SCE_TCL_OPERATOR);
  288. expected = true;
  289. ++currentLevel;
  290. break;
  291. case '}':
  292. sc.SetState(SCE_TCL_OPERATOR);
  293. expected = true;
  294. --currentLevel;
  295. break;
  296. case '[':
  297. expected = true;
  298. case ']':
  299. case '(':
  300. case ')':
  301. sc.SetState(SCE_TCL_OPERATOR);
  302. break;
  303. case ';':
  304. expected = true;
  305. break;
  306. case '$':
  307. subParen = 0;
  308. if (sc.chNext != '{') {
  309. sc.SetState(SCE_TCL_SUBSTITUTION);
  310. } else {
  311. sc.SetState(SCE_TCL_OPERATOR); // $
  312. sc.Forward(); // {
  313. sc.ForwardSetState(SCE_TCL_SUB_BRACE);
  314. subBrace = true;
  315. }
  316. break;
  317. case '#':
  318. if ((isspacechar(static_cast<unsigned char>(sc.chPrev))||
  319. isoperator(static_cast<char>(sc.chPrev))) && IsADigit(sc.chNext,0x10))
  320. sc.SetState(SCE_TCL_NUMBER);
  321. break;
  322. case '-':
  323. sc.SetState(IsADigit(sc.chNext)? SCE_TCL_NUMBER: SCE_TCL_MODIFIER);
  324. break;
  325. default:
  326. if (isoperator(static_cast<char>(sc.ch))) {
  327. sc.SetState(SCE_TCL_OPERATOR);
  328. }
  329. }
  330. }
  331. }
  332. }
  333. sc.Complete();
  334. }
  335. static const char *const tclWordListDesc[] = {
  336. "TCL Keywords",
  337. "TK Keywords",
  338. "iTCL Keywords",
  339. "tkCommands",
  340. "expand",
  341. "user1",
  342. "user2",
  343. "user3",
  344. "user4",
  345. 0
  346. };
  347. // this code supports folding in the colourizer
  348. LexerModule lmTCL(SCLEX_TCL, ColouriseTCLDoc, "tcl", 0, tclWordListDesc);