1 // Scintilla source code edit control
\r
3 ** Lexer for TCL language.
\r
5 // Copyright 1998-2001 by Andre Arpin <arpin@kingston.net>
\r
6 // The License.txt file describes the conditions under which this software may be distributed.
\r
14 #include "Platform.h"
\r
16 #include "PropSet.h"
\r
17 #include "Accessor.h"
\r
18 #include "StyleContext.h"
\r
19 #include "KeyWords.h"
\r
20 #include "Scintilla.h"
\r
21 #include "SciLexer.h"
\r
23 #ifdef SCI_NAMESPACE
\r
24 using namespace Scintilla;
\r
27 // Extended to accept accented characters
\r
28 static inline bool IsAWordChar(int ch) {
\r
29 return ch >= 0x80 ||
\r
30 (isalnum(ch) || ch == '_' || ch ==':' || ch=='.'); // : name space separator
\r
33 static inline bool IsAWordStart(int ch) {
\r
34 return ch >= 0x80 || (ch ==':' || isalpha(ch) || ch == '_');
\r
37 static inline bool IsANumberChar(int ch) {
\r
38 // Not exactly following number definition (several dots are seen as OK, etc.)
\r
39 // but probably enough in most cases.
\r
40 return (ch < 0x80) &&
\r
41 (IsADigit(ch, 0x10) || toupper(ch) == 'E' ||
\r
42 ch == '.' || ch == '-' || ch == '+');
\r
45 static void ColouriseTCLDoc(unsigned int startPos, int length, int , WordList *keywordlists[], Accessor &styler) {
\r
46 #define isComment(s) (s==SCE_TCL_COMMENT || s==SCE_TCL_COMMENTLINE || s==SCE_TCL_COMMENT_BOX || s==SCE_TCL_BLOCK_COMMENT)
\r
47 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
\r
48 bool commentLevel = false;
\r
49 bool subBrace = false; // substitution begin with a brace ${.....}
\r
50 enum tLineState {LS_DEFAULT, LS_OPEN_COMMENT, LS_OPEN_DOUBLE_QUOTE, LS_COMMENT_BOX, LS_MASK_STATE = 0xf,
\r
51 LS_COMMAND_EXPECTED = 16, LS_BRACE_ONLY = 32 } lineState = LS_DEFAULT;
\r
52 bool prevSlash = false;
\r
53 int currentLevel = 0;
\r
57 int currentLine = styler.GetLine(startPos);
\r
58 if (currentLine > 0)
\r
60 length += startPos - styler.LineStart(currentLine);
\r
61 // make sure lines overlap
\r
62 startPos = styler.LineStart(currentLine);
\r
64 WordList &keywords = *keywordlists[0];
\r
65 WordList &keywords2 = *keywordlists[1];
\r
66 WordList &keywords3 = *keywordlists[2];
\r
67 WordList &keywords4 = *keywordlists[3];
\r
68 WordList &keywords5 = *keywordlists[4];
\r
69 WordList &keywords6 = *keywordlists[5];
\r
70 WordList &keywords7 = *keywordlists[6];
\r
71 WordList &keywords8 = *keywordlists[7];
\r
72 WordList &keywords9 = *keywordlists[8];
\r
74 if (currentLine > 0) {
\r
75 int ls = styler.GetLineState(currentLine - 1);
\r
76 lineState = tLineState(ls & LS_MASK_STATE);
\r
77 expected = LS_COMMAND_EXPECTED == tLineState(ls & LS_COMMAND_EXPECTED);
\r
78 subBrace = LS_BRACE_ONLY == tLineState(ls & LS_BRACE_ONLY);
\r
79 currentLevel = styler.LevelAt(currentLine - 1) >> 17;
\r
80 commentLevel = (styler.LevelAt(currentLine - 1) >> 16) & 1;
\r
82 styler.SetLevel(0, SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG);
\r
83 bool visibleChars = false;
\r
85 int previousLevel = currentLevel;
\r
86 StyleContext sc(startPos, length, SCE_TCL_DEFAULT, styler);
\r
87 for (; ; sc.Forward()) {
\r
89 if (sc.ch=='\r' && sc.chNext == '\n') // only ignore \r on PC process on the mac
\r
91 bool atEnd = !sc.More(); // make sure we coloured the last word
\r
92 if (lineState != LS_DEFAULT) {
\r
93 sc.SetState(SCE_TCL_DEFAULT);
\r
94 if (lineState == LS_OPEN_COMMENT)
\r
95 sc.SetState(SCE_TCL_COMMENTLINE);
\r
96 else if (lineState == LS_OPEN_DOUBLE_QUOTE)
\r
97 sc.SetState(SCE_TCL_IN_QUOTE);
\r
98 else if (lineState == LS_COMMENT_BOX && (sc.ch == '#' || (sc.ch == ' ' && sc.chNext=='#')))
\r
99 sc.SetState(SCE_TCL_COMMENT_BOX);
\r
100 lineState = LS_DEFAULT;
\r
102 if (subBrace) { // ${ overrides every thing even \ except }
\r
103 if (sc.ch == '}') {
\r
105 sc.SetState(SCE_TCL_OPERATOR);
\r
106 sc.ForwardSetState(SCE_TCL_DEFAULT);
\r
110 sc.SetState(SCE_TCL_SUB_BRACE);
\r
113 } else if (sc.state == SCE_TCL_DEFAULT || sc.state ==SCE_TCL_OPERATOR) {
\r
114 expected &= isspacechar(static_cast<unsigned char>(sc.ch)) || IsAWordStart(sc.ch) || sc.ch =='#';
\r
115 } else if (sc.state == SCE_TCL_SUBSTITUTION) {
\r
119 sc.SetState(SCE_TCL_OPERATOR);
\r
120 sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
\r
123 sc.SetState(SCE_TCL_OPERATOR);
\r
129 sc.SetState(SCE_TCL_OPERATOR);
\r
131 sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
\r
134 // maybe spaces should be allowed ???
\r
135 if (!IsAWordChar(sc.ch)) { // probably the code is wrong
\r
136 sc.SetState(SCE_TCL_DEFAULT);
\r
141 } else if (isComment(sc.state)) {
\r
142 } else if (!IsAWordChar(sc.ch)) {
\r
143 if ((sc.state == SCE_TCL_IDENTIFIER && expected) || sc.state == SCE_TCL_MODIFIER) {
\r
146 sc.GetCurrent(w, sizeof(w));
\r
147 if (w[strlen(w)-1]=='\r')
\r
149 while(*s == ':') // ignore leading : like in ::set a 10
\r
151 bool quote = sc.state == SCE_TCL_IN_QUOTE;
\r
152 if (commentLevel || expected) {
\r
153 if (keywords.InList(s)) {
\r
154 sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD);
\r
155 } else if (keywords2.InList(s)) {
\r
156 sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD2);
\r
157 } else if (keywords3.InList(s)) {
\r
158 sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD3);
\r
159 } else if (keywords4.InList(s)) {
\r
160 sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD4);
\r
161 } else if (sc.GetRelative(-static_cast<int>(strlen(s))-1) == '{' &&
\r
162 keywords5.InList(s) && sc.ch == '}') { // {keyword} exactly no spaces
\r
163 sc.ChangeState(SCE_TCL_EXPAND);
\r
165 if (keywords6.InList(s)) {
\r
166 sc.ChangeState(SCE_TCL_WORD5);
\r
167 } else if (keywords7.InList(s)) {
\r
168 sc.ChangeState(SCE_TCL_WORD6);
\r
169 } else if (keywords8.InList(s)) {
\r
170 sc.ChangeState(SCE_TCL_WORD7);
\r
171 } else if (keywords9.InList(s)) {
\r
172 sc.ChangeState(SCE_TCL_WORD8);
\r
176 sc.SetState(quote ? SCE_TCL_IN_QUOTE : SCE_TCL_DEFAULT);
\r
177 } else if (sc.state == SCE_TCL_MODIFIER || sc.state == SCE_TCL_IDENTIFIER) {
\r
178 sc.SetState(SCE_TCL_DEFAULT);
\r
183 if (sc.atLineEnd) {
\r
184 lineState = LS_DEFAULT;
\r
185 currentLine = styler.GetLine(sc.currentPos);
\r
186 if (foldComment && sc.state!=SCE_TCL_COMMENT && isComment(sc.state)) {
\r
187 if (currentLevel == 0) {
\r
189 commentLevel = true;
\r
192 if (visibleChars && commentLevel) {
\r
195 commentLevel = false;
\r
200 flag = SC_FOLDLEVELWHITEFLAG;
\r
201 if (currentLevel > previousLevel)
\r
202 flag = SC_FOLDLEVELHEADERFLAG;
\r
203 styler.SetLevel(currentLine, flag + previousLevel + SC_FOLDLEVELBASE + (currentLevel << 17) + (commentLevel << 16));
\r
205 // Update the line state, so it can be seen by next line
\r
206 if (sc.state == SCE_TCL_IN_QUOTE)
\r
207 lineState = LS_OPEN_DOUBLE_QUOTE;
\r
210 if (isComment(sc.state))
\r
211 lineState = LS_OPEN_COMMENT;
\r
212 } else if (sc.state == SCE_TCL_COMMENT_BOX)
\r
213 lineState = LS_COMMENT_BOX;
\r
215 styler.SetLineState(currentLine,
\r
216 (subBrace ? LS_BRACE_ONLY : 0) |
\r
217 (expected ? LS_COMMAND_EXPECTED : 0) | lineState);
\r
218 if (lineState == LS_COMMENT_BOX)
\r
219 sc.ForwardSetState(SCE_TCL_COMMENT_BOX);
\r
220 else if (lineState == LS_OPEN_DOUBLE_QUOTE)
\r
221 sc.ForwardSetState(SCE_TCL_IN_QUOTE);
\r
223 sc.ForwardSetState(SCE_TCL_DEFAULT);
\r
225 previousLevel = currentLevel;
\r
231 if (sc.ch == '#' && IsANumberChar(sc.chNext))
\r
232 sc.ForwardSetState(SCE_TCL_NUMBER);
\r
235 prevSlash = sc.ch == '\\';
\r
236 if (isComment(sc.state))
\r
238 if (sc.atLineStart) {
\r
239 visibleChars = false;
\r
240 if (sc.state!=SCE_TCL_IN_QUOTE && !isComment(sc.state))
\r
242 sc.SetState(SCE_TCL_DEFAULT);
\r
243 expected = IsAWordStart(sc.ch)|| isspacechar(static_cast<unsigned char>(sc.ch));
\r
247 switch (sc.state) {
\r
248 case SCE_TCL_NUMBER:
\r
249 if (!IsANumberChar(sc.ch))
\r
250 sc.SetState(SCE_TCL_DEFAULT);
\r
252 case SCE_TCL_IN_QUOTE:
\r
253 if (sc.ch == '"') {
\r
254 sc.ForwardSetState(SCE_TCL_DEFAULT);
\r
255 visibleChars = true; // necessary if a " is the first and only character on a line
\r
257 } else if (sc.ch == '[' || sc.ch == ']' || sc.ch == '$') {
\r
258 sc.SetState(SCE_TCL_OPERATOR);
\r
259 expected = sc.ch == '[';
\r
260 sc.ForwardSetState(SCE_TCL_IN_QUOTE);
\r
264 case SCE_TCL_OPERATOR:
\r
265 sc.SetState(SCE_TCL_DEFAULT);
\r
269 if (sc.ch == '#') {
\r
270 if (visibleChars) {
\r
271 if (sc.state != SCE_TCL_IN_QUOTE && expected)
\r
272 sc.SetState(SCE_TCL_COMMENT);
\r
274 sc.SetState(SCE_TCL_COMMENTLINE);
\r
275 if (sc.chNext == '~')
\r
276 sc.SetState(SCE_TCL_BLOCK_COMMENT);
\r
277 if (sc.atLineStart && (sc.chNext == '#' || sc.chNext == '-'))
\r
278 sc.SetState(SCE_TCL_COMMENT_BOX);
\r
282 if (!isspacechar(static_cast<unsigned char>(sc.ch))) {
\r
283 visibleChars = true;
\r
286 if (sc.ch == '\\') {
\r
291 // Determine if a new state should be entered.
\r
292 if (sc.state == SCE_TCL_DEFAULT) {
\r
293 if (IsAWordStart(sc.ch)) {
\r
294 sc.SetState(SCE_TCL_IDENTIFIER);
\r
295 } else if (IsADigit(sc.ch) && !IsAWordChar(sc.chPrev)) {
\r
296 sc.SetState(SCE_TCL_NUMBER);
\r
300 sc.SetState(SCE_TCL_IN_QUOTE);
\r
303 sc.SetState(SCE_TCL_OPERATOR);
\r
308 sc.SetState(SCE_TCL_OPERATOR);
\r
316 sc.SetState(SCE_TCL_OPERATOR);
\r
323 if (sc.chNext != '{') {
\r
324 sc.SetState(SCE_TCL_SUBSTITUTION);
\r
327 sc.SetState(SCE_TCL_OPERATOR); // $
\r
329 sc.ForwardSetState(SCE_TCL_SUB_BRACE);
\r
334 if ((isspacechar(static_cast<unsigned char>(sc.chPrev))||
\r
335 isoperator(static_cast<char>(sc.chPrev))) && IsADigit(sc.chNext,0x10))
\r
336 sc.SetState(SCE_TCL_NUMBER);
\r
339 sc.SetState(IsADigit(sc.chNext)? SCE_TCL_NUMBER: SCE_TCL_MODIFIER);
\r
342 if (isoperator(static_cast<char>(sc.ch))) {
\r
343 sc.SetState(SCE_TCL_OPERATOR);
\r
352 static const char * const tclWordListDesc[] = {
\r
365 // this code supports folding in the colourizer
\r
366 LexerModule lmTCL(SCLEX_TCL, ColouriseTCLDoc, "tcl", 0, tclWordListDesc);
\r