1 // Scintilla source code edit control
3 ** Lexer for TCL language.
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.
16 #include "Scintilla.h"
20 #include "LexAccessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
26 using namespace Scintilla
;
28 // Extended to accept accented characters
29 static inline bool IsAWordChar(int ch
) {
31 (isalnum(ch
) || ch
== '_' || ch
==':' || ch
=='.'); // : name space separator
34 static inline bool IsAWordStart(int ch
) {
35 return ch
>= 0x80 || (ch
==':' || isalpha(ch
) || ch
== '_');
38 static inline bool IsANumberChar(int ch
) {
39 // Not exactly following number definition (several dots are seen as OK, etc.)
40 // but probably enough in most cases.
42 (IsADigit(ch
, 0x10) || toupper(ch
) == 'E' ||
43 ch
== '.' || ch
== '-' || ch
== '+');
46 static void ColouriseTCLDoc(Sci_PositionU startPos
, Sci_Position length
, int , WordList
*keywordlists
[], Accessor
&styler
) {
47 #define isComment(s) (s==SCE_TCL_COMMENT || s==SCE_TCL_COMMENTLINE || s==SCE_TCL_COMMENT_BOX || s==SCE_TCL_BLOCK_COMMENT)
48 bool foldComment
= styler
.GetPropertyInt("fold.comment") != 0;
49 bool commentLevel
= false;
50 bool subBrace
= false; // substitution begin with a brace ${.....}
51 enum tLineState
{LS_DEFAULT
, LS_OPEN_COMMENT
, LS_OPEN_DOUBLE_QUOTE
, LS_COMMENT_BOX
, LS_MASK_STATE
= 0xf,
52 LS_COMMAND_EXPECTED
= 16, LS_BRACE_ONLY
= 32
53 } lineState
= LS_DEFAULT
;
54 bool prevSlash
= false;
59 Sci_Position currentLine
= styler
.GetLine(startPos
);
62 length
+= startPos
- styler
.LineStart(currentLine
);
63 // make sure lines overlap
64 startPos
= styler
.LineStart(currentLine
);
66 WordList
&keywords
= *keywordlists
[0];
67 WordList
&keywords2
= *keywordlists
[1];
68 WordList
&keywords3
= *keywordlists
[2];
69 WordList
&keywords4
= *keywordlists
[3];
70 WordList
&keywords5
= *keywordlists
[4];
71 WordList
&keywords6
= *keywordlists
[5];
72 WordList
&keywords7
= *keywordlists
[6];
73 WordList
&keywords8
= *keywordlists
[7];
74 WordList
&keywords9
= *keywordlists
[8];
76 if (currentLine
> 0) {
77 int ls
= styler
.GetLineState(currentLine
- 1);
78 lineState
= tLineState(ls
& LS_MASK_STATE
);
79 expected
= LS_COMMAND_EXPECTED
== tLineState(ls
& LS_COMMAND_EXPECTED
);
80 subBrace
= LS_BRACE_ONLY
== tLineState(ls
& LS_BRACE_ONLY
);
81 currentLevel
= styler
.LevelAt(currentLine
- 1) >> 17;
82 commentLevel
= (styler
.LevelAt(currentLine
- 1) >> 16) & 1;
84 styler
.SetLevel(0, SC_FOLDLEVELBASE
| SC_FOLDLEVELHEADERFLAG
);
85 bool visibleChars
= false;
87 int previousLevel
= currentLevel
;
88 StyleContext
sc(startPos
, length
, SCE_TCL_DEFAULT
, styler
);
89 for (; ; sc
.Forward()) {
91 if (sc
.ch
=='\r' && sc
.chNext
== '\n') // only ignore \r on PC process on the mac
93 bool atEnd
= !sc
.More(); // make sure we coloured the last word
94 if (lineState
!= LS_DEFAULT
) {
95 sc
.SetState(SCE_TCL_DEFAULT
);
96 if (lineState
== LS_OPEN_COMMENT
)
97 sc
.SetState(SCE_TCL_COMMENTLINE
);
98 else if (lineState
== LS_OPEN_DOUBLE_QUOTE
)
99 sc
.SetState(SCE_TCL_IN_QUOTE
);
100 else if (lineState
== LS_COMMENT_BOX
&& (sc
.ch
== '#' || (sc
.ch
== ' ' && sc
.chNext
=='#')))
101 sc
.SetState(SCE_TCL_COMMENT_BOX
);
102 lineState
= LS_DEFAULT
;
104 if (subBrace
) { // ${ overrides every thing even \ except }
107 sc
.SetState(SCE_TCL_OPERATOR
);
108 sc
.ForwardSetState(SCE_TCL_DEFAULT
);
111 sc
.SetState(SCE_TCL_SUB_BRACE
);
114 } else if (sc
.state
== SCE_TCL_DEFAULT
|| sc
.state
==SCE_TCL_OPERATOR
) {
115 expected
&= isspacechar(static_cast<unsigned char>(sc
.ch
)) || IsAWordStart(sc
.ch
) || sc
.ch
=='#';
116 } else if (sc
.state
== SCE_TCL_SUBSTITUTION
) {
120 sc
.SetState(SCE_TCL_OPERATOR
);
121 sc
.ForwardSetState(SCE_TCL_SUBSTITUTION
);
124 sc
.SetState(SCE_TCL_OPERATOR
);
130 sc
.SetState(SCE_TCL_OPERATOR
);
132 sc
.ForwardSetState(SCE_TCL_SUBSTITUTION
);
135 // maybe spaces should be allowed ???
136 if (!IsAWordChar(sc
.ch
)) { // probably the code is wrong
137 sc
.SetState(SCE_TCL_DEFAULT
);
142 } else if (isComment(sc
.state
)) {
143 } else if (!IsAWordChar(sc
.ch
)) {
144 if ((sc
.state
== SCE_TCL_IDENTIFIER
&& expected
) || sc
.state
== SCE_TCL_MODIFIER
) {
147 sc
.GetCurrent(w
, sizeof(w
));
148 if (w
[strlen(w
)-1]=='\r')
150 while (*s
== ':') // ignore leading : like in ::set a 10
152 bool quote
= sc
.state
== SCE_TCL_IN_QUOTE
;
153 if (commentLevel
|| expected
) {
154 if (keywords
.InList(s
)) {
155 sc
.ChangeState(quote
? SCE_TCL_WORD_IN_QUOTE
: SCE_TCL_WORD
);
156 } else if (keywords2
.InList(s
)) {
157 sc
.ChangeState(quote
? SCE_TCL_WORD_IN_QUOTE
: SCE_TCL_WORD2
);
158 } else if (keywords3
.InList(s
)) {
159 sc
.ChangeState(quote
? SCE_TCL_WORD_IN_QUOTE
: SCE_TCL_WORD3
);
160 } else if (keywords4
.InList(s
)) {
161 sc
.ChangeState(quote
? SCE_TCL_WORD_IN_QUOTE
: SCE_TCL_WORD4
);
162 } else if (sc
.GetRelative(-static_cast<int>(strlen(s
))-1) == '{' &&
163 keywords5
.InList(s
) && sc
.ch
== '}') { // {keyword} exactly no spaces
164 sc
.ChangeState(SCE_TCL_EXPAND
);
166 if (keywords6
.InList(s
)) {
167 sc
.ChangeState(SCE_TCL_WORD5
);
168 } else if (keywords7
.InList(s
)) {
169 sc
.ChangeState(SCE_TCL_WORD6
);
170 } else if (keywords8
.InList(s
)) {
171 sc
.ChangeState(SCE_TCL_WORD7
);
172 } else if (keywords9
.InList(s
)) {
173 sc
.ChangeState(SCE_TCL_WORD8
);
177 sc
.SetState(quote
? SCE_TCL_IN_QUOTE
: SCE_TCL_DEFAULT
);
178 } else if (sc
.state
== SCE_TCL_MODIFIER
|| sc
.state
== SCE_TCL_IDENTIFIER
) {
179 sc
.SetState(SCE_TCL_DEFAULT
);
185 lineState
= LS_DEFAULT
;
186 currentLine
= styler
.GetLine(sc
.currentPos
);
187 if (foldComment
&& sc
.state
!=SCE_TCL_COMMENT
&& isComment(sc
.state
)) {
188 if (currentLevel
== 0) {
193 if (visibleChars
&& commentLevel
) {
196 commentLevel
= false;
201 flag
= SC_FOLDLEVELWHITEFLAG
;
202 if (currentLevel
> previousLevel
)
203 flag
= SC_FOLDLEVELHEADERFLAG
;
204 styler
.SetLevel(currentLine
, flag
+ previousLevel
+ SC_FOLDLEVELBASE
+ (currentLevel
<< 17) + (commentLevel
<< 16));
206 // Update the line state, so it can be seen by next line
207 if (sc
.state
== SCE_TCL_IN_QUOTE
) {
208 lineState
= LS_OPEN_DOUBLE_QUOTE
;
211 if (isComment(sc
.state
))
212 lineState
= LS_OPEN_COMMENT
;
213 } else if (sc
.state
== SCE_TCL_COMMENT_BOX
)
214 lineState
= LS_COMMENT_BOX
;
216 styler
.SetLineState(currentLine
,
217 (subBrace
? LS_BRACE_ONLY
: 0) |
218 (expected
? LS_COMMAND_EXPECTED
: 0) | lineState
);
219 if (lineState
== LS_COMMENT_BOX
)
220 sc
.ForwardSetState(SCE_TCL_COMMENT_BOX
);
221 else if (lineState
== LS_OPEN_DOUBLE_QUOTE
)
222 sc
.ForwardSetState(SCE_TCL_IN_QUOTE
);
224 sc
.ForwardSetState(SCE_TCL_DEFAULT
);
226 previousLevel
= currentLevel
;
232 if (sc
.ch
== '#' && IsANumberChar(sc
.chNext
))
233 sc
.ForwardSetState(SCE_TCL_NUMBER
);
236 prevSlash
= sc
.ch
== '\\';
237 if (isComment(sc
.state
))
239 if (sc
.atLineStart
) {
240 visibleChars
= false;
241 if (sc
.state
!=SCE_TCL_IN_QUOTE
&& !isComment(sc
.state
))
243 sc
.SetState(SCE_TCL_DEFAULT
);
244 expected
= IsAWordStart(sc
.ch
)|| isspacechar(static_cast<unsigned char>(sc
.ch
));
250 if (!IsANumberChar(sc
.ch
))
251 sc
.SetState(SCE_TCL_DEFAULT
);
253 case SCE_TCL_IN_QUOTE
:
255 sc
.ForwardSetState(SCE_TCL_DEFAULT
);
256 visibleChars
= true; // necessary if a " is the first and only character on a line
258 } else if (sc
.ch
== '[' || sc
.ch
== ']' || sc
.ch
== '$') {
259 sc
.SetState(SCE_TCL_OPERATOR
);
260 expected
= sc
.ch
== '[';
261 sc
.ForwardSetState(SCE_TCL_IN_QUOTE
);
265 case SCE_TCL_OPERATOR
:
266 sc
.SetState(SCE_TCL_DEFAULT
);
272 if (sc
.state
!= SCE_TCL_IN_QUOTE
&& expected
)
273 sc
.SetState(SCE_TCL_COMMENT
);
275 sc
.SetState(SCE_TCL_COMMENTLINE
);
276 if (sc
.chNext
== '~')
277 sc
.SetState(SCE_TCL_BLOCK_COMMENT
);
278 if (sc
.atLineStart
&& (sc
.chNext
== '#' || sc
.chNext
== '-'))
279 sc
.SetState(SCE_TCL_COMMENT_BOX
);
283 if (!isspacechar(static_cast<unsigned char>(sc
.ch
))) {
292 // Determine if a new state should be entered.
293 if (sc
.state
== SCE_TCL_DEFAULT
) {
294 if (IsAWordStart(sc
.ch
)) {
295 sc
.SetState(SCE_TCL_IDENTIFIER
);
296 } else if (IsADigit(sc
.ch
) && !IsAWordChar(sc
.chPrev
)) {
297 sc
.SetState(SCE_TCL_NUMBER
);
301 sc
.SetState(SCE_TCL_IN_QUOTE
);
304 sc
.SetState(SCE_TCL_OPERATOR
);
309 sc
.SetState(SCE_TCL_OPERATOR
);
319 sc
.SetState(SCE_TCL_OPERATOR
);
326 if (sc
.chNext
!= '{') {
327 sc
.SetState(SCE_TCL_SUBSTITUTION
);
329 sc
.SetState(SCE_TCL_OPERATOR
); // $
331 sc
.ForwardSetState(SCE_TCL_SUB_BRACE
);
336 if ((isspacechar(static_cast<unsigned char>(sc
.chPrev
))||
337 isoperator(static_cast<char>(sc
.chPrev
))) && IsADigit(sc
.chNext
,0x10))
338 sc
.SetState(SCE_TCL_NUMBER
);
341 sc
.SetState(IsADigit(sc
.chNext
)? SCE_TCL_NUMBER
: SCE_TCL_MODIFIER
);
344 if (isoperator(static_cast<char>(sc
.ch
))) {
345 sc
.SetState(SCE_TCL_OPERATOR
);
354 static const char *const tclWordListDesc
[] = {
367 // this code supports folding in the colourizer
368 LexerModule
lmTCL(SCLEX_TCL
, ColouriseTCLDoc
, "tcl", 0, tclWordListDesc
);