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