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