1 // Scintilla source code edit control
3 ** Lexer for Lua language.
5 ** Written by Paul Winwood.
6 ** Folder by Alexey Yutkin.
7 ** Modified by Marcos E. Wurzius & Philippe Lhoste
20 #include "StyleContext.h"
22 #include "Scintilla.h"
24 #include "CharacterSet.h"
27 using namespace Scintilla
;
30 // Test for [=[ ... ]=] delimiters, returns 0 if it's only a [ or ],
31 // return 1 for [[ or ]], returns >=2 for [=[ or ]=] and so on.
32 // The maximum number of '=' characters allowed is 254.
33 static int LongDelimCheck(StyleContext
&sc
) {
35 while (sc
.GetRelative(sep
) == '=' && sep
< 0xFF)
37 if (sc
.GetRelative(sep
) == sc
.ch
)
42 static void ColouriseLuaDoc(
43 unsigned int startPos
,
46 WordList
*keywordlists
[],
49 WordList
&keywords
= *keywordlists
[0];
50 WordList
&keywords2
= *keywordlists
[1];
51 WordList
&keywords3
= *keywordlists
[2];
52 WordList
&keywords4
= *keywordlists
[3];
53 WordList
&keywords5
= *keywordlists
[4];
54 WordList
&keywords6
= *keywordlists
[5];
55 WordList
&keywords7
= *keywordlists
[6];
56 WordList
&keywords8
= *keywordlists
[7];
58 // Accepts accented characters
59 CharacterSet
setWordStart(CharacterSet::setAlpha
, "_", 0x80, true);
60 CharacterSet
setWord(CharacterSet::setAlphaNum
, "._", 0x80, true);
61 // Not exactly following number definition (several dots are seen as OK, etc.)
62 // but probably enough in most cases.
63 CharacterSet
setNumber(CharacterSet::setDigits
, ".-+abcdefABCDEF");
64 CharacterSet
setLuaOperator(CharacterSet::setNone
, "*/-+()={}~[];<>,.^%:#");
65 CharacterSet
setEscapeSkip(CharacterSet::setNone
, "\"'\\");
67 int currentLine
= styler
.GetLine(startPos
);
68 // Initialize long string [[ ... ]] or block comment --[[ ... ]] nesting level,
69 // if we are inside such a string. Block comment was introduced in Lua 5.0,
70 // blocks with separators [=[ ... ]=] in Lua 5.1.
73 if (initStyle
== SCE_LUA_LITERALSTRING
|| initStyle
== SCE_LUA_COMMENT
) {
74 int lineState
= styler
.GetLineState(currentLine
- 1);
75 nestLevel
= lineState
>> 8;
76 sepCount
= lineState
& 0xFF;
79 // Do not leak onto next line
80 if (initStyle
== SCE_LUA_STRINGEOL
|| initStyle
== SCE_LUA_COMMENTLINE
|| initStyle
== SCE_LUA_PREPROCESSOR
) {
81 initStyle
= SCE_LUA_DEFAULT
;
84 StyleContext
sc(startPos
, length
, initStyle
, styler
);
85 if (startPos
== 0 && sc
.ch
== '#') {
86 // shbang line: # is a comment only if first char of the script
87 sc
.SetState(SCE_LUA_COMMENTLINE
);
89 for (; sc
.More(); sc
.Forward()) {
91 // Update the line state, so it can be seen by next line
92 currentLine
= styler
.GetLine(sc
.currentPos
);
94 case SCE_LUA_LITERALSTRING
:
96 // Inside a literal string or block comment, we set the line state
97 styler
.SetLineState(currentLine
, (nestLevel
<< 8) | sepCount
);
100 // Reset the line state
101 styler
.SetLineState(currentLine
, 0);
105 if (sc
.atLineStart
&& (sc
.state
== SCE_LUA_STRING
)) {
106 // Prevent SCE_LUA_STRINGEOL from leaking back to previous line
107 sc
.SetState(SCE_LUA_STRING
);
110 // Handle string line continuation
111 if ((sc
.state
== SCE_LUA_STRING
|| sc
.state
== SCE_LUA_CHARACTER
) &&
113 if (sc
.chNext
== '\n' || sc
.chNext
== '\r') {
115 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
122 // Determine if the current state should terminate.
123 if (sc
.state
== SCE_LUA_OPERATOR
) {
124 sc
.SetState(SCE_LUA_DEFAULT
);
125 } else if (sc
.state
== SCE_LUA_NUMBER
) {
126 // We stop the number definition on non-numerical non-dot non-eE non-sign non-hexdigit char
127 if (!setNumber
.Contains(sc
.ch
)) {
128 sc
.SetState(SCE_LUA_DEFAULT
);
129 } else if (sc
.ch
== '-' || sc
.ch
== '+') {
130 if (sc
.chPrev
!= 'E' && sc
.chPrev
!= 'e')
131 sc
.SetState(SCE_LUA_DEFAULT
);
133 } else if (sc
.state
== SCE_LUA_IDENTIFIER
) {
134 if (!setWord
.Contains(sc
.ch
) || sc
.Match('.', '.')) {
136 sc
.GetCurrent(s
, sizeof(s
));
137 if (keywords
.InList(s
)) {
138 sc
.ChangeState(SCE_LUA_WORD
);
139 } else if (keywords2
.InList(s
)) {
140 sc
.ChangeState(SCE_LUA_WORD2
);
141 } else if (keywords3
.InList(s
)) {
142 sc
.ChangeState(SCE_LUA_WORD3
);
143 } else if (keywords4
.InList(s
)) {
144 sc
.ChangeState(SCE_LUA_WORD4
);
145 } else if (keywords5
.InList(s
)) {
146 sc
.ChangeState(SCE_LUA_WORD5
);
147 } else if (keywords6
.InList(s
)) {
148 sc
.ChangeState(SCE_LUA_WORD6
);
149 } else if (keywords7
.InList(s
)) {
150 sc
.ChangeState(SCE_LUA_WORD7
);
151 } else if (keywords8
.InList(s
)) {
152 sc
.ChangeState(SCE_LUA_WORD8
);
154 sc
.SetState(SCE_LUA_DEFAULT
);
156 } else if (sc
.state
== SCE_LUA_COMMENTLINE
|| sc
.state
== SCE_LUA_PREPROCESSOR
) {
158 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
160 } else if (sc
.state
== SCE_LUA_STRING
) {
162 if (setEscapeSkip
.Contains(sc
.chNext
)) {
165 } else if (sc
.ch
== '\"') {
166 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
167 } else if (sc
.atLineEnd
) {
168 sc
.ChangeState(SCE_LUA_STRINGEOL
);
169 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
171 } else if (sc
.state
== SCE_LUA_CHARACTER
) {
173 if (setEscapeSkip
.Contains(sc
.chNext
)) {
176 } else if (sc
.ch
== '\'') {
177 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
178 } else if (sc
.atLineEnd
) {
179 sc
.ChangeState(SCE_LUA_STRINGEOL
);
180 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
182 } else if (sc
.state
== SCE_LUA_LITERALSTRING
|| sc
.state
== SCE_LUA_COMMENT
) {
184 int sep
= LongDelimCheck(sc
);
185 if (sep
== 1 && sepCount
== 1) { // [[-only allowed to nest
189 } else if (sc
.ch
== ']') {
190 int sep
= LongDelimCheck(sc
);
191 if (sep
== 1 && sepCount
== 1) { // un-nest with ]]-only
194 if (nestLevel
== 0) {
195 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
197 } else if (sep
> 1 && sep
== sepCount
) { // ]=]-style delim
199 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
204 // Determine if a new state should be entered.
205 if (sc
.state
== SCE_LUA_DEFAULT
) {
206 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
207 sc
.SetState(SCE_LUA_NUMBER
);
208 if (sc
.ch
== '0' && toupper(sc
.chNext
) == 'X') {
211 } else if (setWordStart
.Contains(sc
.ch
)) {
212 sc
.SetState(SCE_LUA_IDENTIFIER
);
213 } else if (sc
.ch
== '\"') {
214 sc
.SetState(SCE_LUA_STRING
);
215 } else if (sc
.ch
== '\'') {
216 sc
.SetState(SCE_LUA_CHARACTER
);
217 } else if (sc
.ch
== '[') {
218 sepCount
= LongDelimCheck(sc
);
220 sc
.SetState(SCE_LUA_OPERATOR
);
223 sc
.SetState(SCE_LUA_LITERALSTRING
);
224 sc
.Forward(sepCount
);
226 } else if (sc
.Match('-', '-')) {
227 sc
.SetState(SCE_LUA_COMMENTLINE
);
228 if (sc
.Match("--[")) {
230 sepCount
= LongDelimCheck(sc
);
233 sc
.ChangeState(SCE_LUA_COMMENT
);
234 sc
.Forward(sepCount
);
239 } else if (sc
.atLineStart
&& sc
.Match('$')) {
240 sc
.SetState(SCE_LUA_PREPROCESSOR
); // Obsolete since Lua 4.0, but still in old code
241 } else if (setLuaOperator
.Contains(sc
.ch
)) {
242 sc
.SetState(SCE_LUA_OPERATOR
);
247 if (setWord
.Contains(sc
.chPrev
)) {
249 sc
.GetCurrent(s
, sizeof(s
));
250 if (keywords
.InList(s
)) {
251 sc
.ChangeState(SCE_LUA_WORD
);
252 } else if (keywords2
.InList(s
)) {
253 sc
.ChangeState(SCE_LUA_WORD2
);
254 } else if (keywords3
.InList(s
)) {
255 sc
.ChangeState(SCE_LUA_WORD3
);
256 } else if (keywords4
.InList(s
)) {
257 sc
.ChangeState(SCE_LUA_WORD4
);
258 } else if (keywords5
.InList(s
)) {
259 sc
.ChangeState(SCE_LUA_WORD5
);
260 } else if (keywords6
.InList(s
)) {
261 sc
.ChangeState(SCE_LUA_WORD6
);
262 } else if (keywords7
.InList(s
)) {
263 sc
.ChangeState(SCE_LUA_WORD7
);
264 } else if (keywords8
.InList(s
)) {
265 sc
.ChangeState(SCE_LUA_WORD8
);
272 static void FoldLuaDoc(unsigned int startPos
, int length
, int /* initStyle */, WordList
*[],
274 unsigned int lengthDoc
= startPos
+ length
;
275 int visibleChars
= 0;
276 int lineCurrent
= styler
.GetLine(startPos
);
277 int levelPrev
= styler
.LevelAt(lineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
278 int levelCurrent
= levelPrev
;
279 char chNext
= styler
[startPos
];
280 bool foldCompact
= styler
.GetPropertyInt("fold.compact", 1) != 0;
281 int styleNext
= styler
.StyleAt(startPos
);
284 for (unsigned int i
= startPos
; i
< lengthDoc
; i
++) {
286 chNext
= styler
.SafeGetCharAt(i
+ 1);
287 int style
= styleNext
;
288 styleNext
= styler
.StyleAt(i
+ 1);
289 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
290 if (style
== SCE_LUA_WORD
) {
291 if (ch
== 'i' || ch
== 'd' || ch
== 'f' || ch
== 'e' || ch
== 'r' || ch
== 'u') {
292 for (unsigned int j
= 0; j
< 8; j
++) {
293 if (!iswordchar(styler
[i
+ j
])) {
296 s
[j
] = styler
[i
+ j
];
300 if ((strcmp(s
, "if") == 0) || (strcmp(s
, "do") == 0) || (strcmp(s
, "function") == 0) || (strcmp(s
, "repeat") == 0)) {
303 if ((strcmp(s
, "end") == 0) || (strcmp(s
, "elseif") == 0) || (strcmp(s
, "until") == 0)) {
307 } else if (style
== SCE_LUA_OPERATOR
) {
308 if (ch
== '{' || ch
== '(') {
310 } else if (ch
== '}' || ch
== ')') {
313 } else if (style
== SCE_LUA_LITERALSTRING
|| style
== SCE_LUA_COMMENT
) {
316 } else if (ch
== ']') {
323 if (visibleChars
== 0 && foldCompact
) {
324 lev
|= SC_FOLDLEVELWHITEFLAG
;
326 if ((levelCurrent
> levelPrev
) && (visibleChars
> 0)) {
327 lev
|= SC_FOLDLEVELHEADERFLAG
;
329 if (lev
!= styler
.LevelAt(lineCurrent
)) {
330 styler
.SetLevel(lineCurrent
, lev
);
333 levelPrev
= levelCurrent
;
336 if (!isspacechar(ch
)) {
340 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
342 int flagsNext
= styler
.LevelAt(lineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
343 styler
.SetLevel(lineCurrent
, levelPrev
| flagsNext
);
346 static const char * const luaWordListDesc
[] = {
349 "String, (table) & math functions",
350 "(coroutines), I/O & system facilities",
358 LexerModule
lmLua(SCLEX_LUA
, ColouriseLuaDoc
, "lua", FoldLuaDoc
, luaWordListDesc
);