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(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;
60
int currentLine = styler.GetLine(startPos);
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;
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()) {
92
if (sc.ch=='\r' && sc.chNext == '\n') // only ignore \r on PC process on the mac
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 }
108
sc.SetState(SCE_TCL_OPERATOR);
109
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);
331
sc.SetState(SCE_TCL_OPERATOR); // $
333
sc.ForwardSetState(SCE_TCL_SUB_BRACE);
338
if ((isspacechar(static_cast<unsigned char>(sc.chPrev))||
339
isoperator(static_cast<char>(sc.chPrev))) && IsADigit(sc.chNext,0x10))
340
sc.SetState(SCE_TCL_NUMBER);
343
sc.SetState(IsADigit(sc.chNext)? SCE_TCL_NUMBER: SCE_TCL_MODIFIER);
346
if (isoperator(static_cast<char>(sc.ch))) {
347
sc.SetState(SCE_TCL_OPERATOR);
356
static const char * const tclWordListDesc[] = {
369
// this code supports folding in the colourizer
370
LexerModule lmTCL(SCLEX_TCL, ColouriseTCLDoc, "tcl", 0, tclWordListDesc);