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"
27 using namespace Scintilla
;
30 // Extended to accept accented characters
31 static inline bool IsAWordChar(int ch
) {
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.
44 (IsADigit(ch
, 0x10) || toupper(ch
) == 'E' ||
45 ch
== '.' || ch
== '-' || ch
== '+');
48 static void ColouriseTCLDoc(Sci_PositionU startPos
, Sci_Position 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
55 } lineState
= LS_DEFAULT
;
56 bool prevSlash
= false;
61 Sci_Position currentLine
= styler
.GetLine(startPos
);
64 length
+= startPos
- styler
.LineStart(currentLine
);
65 // make sure lines overlap
66 startPos
= styler
.LineStart(currentLine
);
68 WordList
&keywords
= *keywordlists
[0];
69 WordList
&keywords2
= *keywordlists
[1];
70 WordList
&keywords3
= *keywordlists
[2];
71 WordList
&keywords4
= *keywordlists
[3];
72 WordList
&keywords5
= *keywordlists
[4];
73 WordList
&keywords6
= *keywordlists
[5];
74 WordList
&keywords7
= *keywordlists
[6];
75 WordList
&keywords8
= *keywordlists
[7];
76 WordList
&keywords9
= *keywordlists
[8];
78 if (currentLine
> 0) {
79 int ls
= styler
.GetLineState(currentLine
- 1);
80 lineState
= tLineState(ls
& LS_MASK_STATE
);
81 expected
= LS_COMMAND_EXPECTED
== tLineState(ls
& LS_COMMAND_EXPECTED
);
82 subBrace
= LS_BRACE_ONLY
== tLineState(ls
& LS_BRACE_ONLY
);
83 currentLevel
= styler
.LevelAt(currentLine
- 1) >> 17;
84 commentLevel
= (styler
.LevelAt(currentLine
- 1) >> 16) & 1;
86 styler
.SetLevel(0, SC_FOLDLEVELBASE
| SC_FOLDLEVELHEADERFLAG
);
87 bool visibleChars
= false;
89 int previousLevel
= currentLevel
;
90 StyleContext
sc(startPos
, length
, SCE_TCL_DEFAULT
, styler
);
91 for (; ; sc
.Forward()) {
93 if (sc
.ch
=='\r' && sc
.chNext
== '\n') // only ignore \r on PC process on the mac
95 bool atEnd
= !sc
.More(); // make sure we coloured the last word
96 if (lineState
!= LS_DEFAULT
) {
97 sc
.SetState(SCE_TCL_DEFAULT
);
98 if (lineState
== LS_OPEN_COMMENT
)
99 sc
.SetState(SCE_TCL_COMMENTLINE
);
100 else if (lineState
== LS_OPEN_DOUBLE_QUOTE
)
101 sc
.SetState(SCE_TCL_IN_QUOTE
);
102 else if (lineState
== LS_COMMENT_BOX
&& (sc
.ch
== '#' || (sc
.ch
== ' ' && sc
.chNext
=='#')))
103 sc
.SetState(SCE_TCL_COMMENT_BOX
);
104 lineState
= LS_DEFAULT
;
106 if (subBrace
) { // ${ overrides every thing even \ except }
109 sc
.SetState(SCE_TCL_OPERATOR
);
110 sc
.ForwardSetState(SCE_TCL_DEFAULT
);
113 sc
.SetState(SCE_TCL_SUB_BRACE
);
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
) {
122 sc
.SetState(SCE_TCL_OPERATOR
);
123 sc
.ForwardSetState(SCE_TCL_SUBSTITUTION
);
126 sc
.SetState(SCE_TCL_OPERATOR
);
132 sc
.SetState(SCE_TCL_OPERATOR
);
134 sc
.ForwardSetState(SCE_TCL_SUBSTITUTION
);
137 // maybe spaces should be allowed ???
138 if (!IsAWordChar(sc
.ch
)) { // probably the code is wrong
139 sc
.SetState(SCE_TCL_DEFAULT
);
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
) {
149 sc
.GetCurrent(w
, sizeof(w
));
150 if (w
[strlen(w
)-1]=='\r')
152 while (*s
== ':') // ignore leading : like in ::set a 10
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
);
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
);
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) {
195 if (visibleChars
&& commentLevel
) {
198 commentLevel
= false;
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
;
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
);
226 sc
.ForwardSetState(SCE_TCL_DEFAULT
);
228 previousLevel
= currentLevel
;
234 if (sc
.ch
== '#' && IsANumberChar(sc
.chNext
))
235 sc
.ForwardSetState(SCE_TCL_NUMBER
);
238 prevSlash
= sc
.ch
== '\\';
239 if (isComment(sc
.state
))
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
));
252 if (!IsANumberChar(sc
.ch
))
253 sc
.SetState(SCE_TCL_DEFAULT
);
255 case SCE_TCL_IN_QUOTE
:
257 sc
.ForwardSetState(SCE_TCL_DEFAULT
);
258 visibleChars
= true; // necessary if a " is the first and only character on a line
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
);
267 case SCE_TCL_OPERATOR
:
268 sc
.SetState(SCE_TCL_DEFAULT
);
274 if (sc
.state
!= SCE_TCL_IN_QUOTE
&& expected
)
275 sc
.SetState(SCE_TCL_COMMENT
);
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
))) {
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
);
303 sc
.SetState(SCE_TCL_IN_QUOTE
);
306 sc
.SetState(SCE_TCL_OPERATOR
);
311 sc
.SetState(SCE_TCL_OPERATOR
);
320 sc
.SetState(SCE_TCL_OPERATOR
);
327 if (sc
.chNext
!= '{') {
328 sc
.SetState(SCE_TCL_SUBSTITUTION
);
330 sc
.SetState(SCE_TCL_OPERATOR
); // $
332 sc
.ForwardSetState(SCE_TCL_SUB_BRACE
);
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
);
342 sc
.SetState(IsADigit(sc
.chNext
)? SCE_TCL_NUMBER
: SCE_TCL_MODIFIER
);
345 if (isoperator(static_cast<char>(sc
.ch
))) {
346 sc
.SetState(SCE_TCL_OPERATOR
);
355 static const char *const tclWordListDesc
[] = {
368 // this code supports folding in the colourizer
369 LexerModule
lmTCL(SCLEX_TCL
, ColouriseTCLDoc
, "tcl", 0, tclWordListDesc
);