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"
25 static inline bool IsAWordChar(const int ch
) {
26 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_' || ch
== '.');
29 static inline bool IsAWordStart(const int ch
) {
30 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
33 static inline bool IsANumberChar(const int ch
) {
34 // Not exactly following number definition (several dots are seen as OK, etc.)
35 // but probably enough in most cases.
37 (isdigit(ch
) || toupper(ch
) == 'E' ||
38 ch
== '.' || ch
== '-' || ch
== '+');
41 static inline bool IsLuaOperator(int ch
) {
42 if (ch
>= 0x80 || isalnum(ch
)) {
45 // '.' left out as it is used to make up numbers
46 if (ch
== '*' || ch
== '/' || ch
== '-' || ch
== '+' ||
47 ch
== '(' || ch
== ')' || ch
== '=' ||
48 ch
== '{' || ch
== '}' || ch
== '~' ||
49 ch
== '[' || ch
== ']' || ch
== ';' ||
50 ch
== '<' || ch
== '>' || ch
== ',' ||
51 ch
== '.' || ch
== '^' || ch
== '%' || ch
== ':') {
57 static void ColouriseLuaDoc(
58 unsigned int startPos
,
61 WordList
*keywordlists
[],
64 WordList
&keywords
= *keywordlists
[0];
65 WordList
&keywords2
= *keywordlists
[1];
66 WordList
&keywords3
= *keywordlists
[2];
67 WordList
&keywords4
= *keywordlists
[3];
68 WordList
&keywords5
= *keywordlists
[4];
69 WordList
&keywords6
= *keywordlists
[5];
70 WordList
&keywords7
= *keywordlists
[6];
71 WordList
&keywords8
= *keywordlists
[7];
73 int currentLine
= styler
.GetLine(startPos
);
74 // Initialize the literal string [[ ... ]] nesting level, if we are inside such a string.
75 int literalStringLevel
= 0;
76 if (initStyle
== SCE_LUA_LITERALSTRING
) {
77 literalStringLevel
= styler
.GetLineState(currentLine
- 1);
79 // Initialize the block comment --[[ ... ]] nesting level, if we are inside such a comment
80 int blockCommentLevel
= 0;
81 if (initStyle
== SCE_LUA_COMMENT
) {
82 blockCommentLevel
= styler
.GetLineState(currentLine
- 1);
85 // Do not leak onto next line
86 if (initStyle
== SCE_LUA_STRINGEOL
) {
87 initStyle
= SCE_LUA_DEFAULT
;
90 StyleContext
sc(startPos
, length
, initStyle
, styler
);
91 if (startPos
== 0 && sc
.ch
== '#') {
92 // shbang line: # is a comment only if first char of the script
93 sc
.SetState(SCE_LUA_COMMENTLINE
);
95 for (; sc
.More(); sc
.Forward()) {
97 // Update the line state, so it can be seen by next line
98 currentLine
= styler
.GetLine(sc
.currentPos
);
100 case SCE_LUA_LITERALSTRING
:
101 // Inside a literal string, we set the line state
102 styler
.SetLineState(currentLine
, literalStringLevel
);
104 case SCE_LUA_COMMENT
: // Block comment
105 // Inside a block comment, we set the line state
106 styler
.SetLineState(currentLine
, blockCommentLevel
);
109 // Reset the line state
110 styler
.SetLineState(currentLine
, 0);
114 if (sc
.atLineStart
&& (sc
.state
== SCE_LUA_STRING
)) {
115 // Prevent SCE_LUA_STRINGEOL from leaking back to previous line
116 sc
.SetState(SCE_LUA_STRING
);
119 // Handle string line continuation
120 if ((sc
.state
== SCE_LUA_STRING
|| sc
.state
== SCE_LUA_CHARACTER
) &&
122 if (sc
.chNext
== '\n' || sc
.chNext
== '\r') {
124 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
131 // Determine if the current state should terminate.
132 if (sc
.state
== SCE_LUA_OPERATOR
) {
133 sc
.SetState(SCE_LUA_DEFAULT
);
134 } else if (sc
.state
== SCE_LUA_NUMBER
) {
135 // We stop the number definition on non-numerical non-dot non-eE non-sign char
136 if (!IsANumberChar(sc
.ch
)) {
137 sc
.SetState(SCE_LUA_DEFAULT
);
139 } else if (sc
.state
== SCE_LUA_IDENTIFIER
) {
140 if (!IsAWordChar(sc
.ch
) || sc
.Match('.', '.')) {
142 sc
.GetCurrent(s
, sizeof(s
));
143 if (keywords
.InList(s
)) {
144 sc
.ChangeState(SCE_LUA_WORD
);
145 } else if (keywords2
.InList(s
)) {
146 sc
.ChangeState(SCE_LUA_WORD2
);
147 } else if (keywords3
.InList(s
)) {
148 sc
.ChangeState(SCE_LUA_WORD3
);
149 } else if (keywords4
.InList(s
)) {
150 sc
.ChangeState(SCE_LUA_WORD4
);
151 } else if (keywords5
.InList(s
)) {
152 sc
.ChangeState(SCE_LUA_WORD5
);
153 } else if (keywords6
.InList(s
)) {
154 sc
.ChangeState(SCE_LUA_WORD6
);
155 } else if (keywords6
.InList(s
)) {
156 sc
.ChangeState(SCE_LUA_WORD6
);
157 } else if (keywords7
.InList(s
)) {
158 sc
.ChangeState(SCE_LUA_WORD7
);
159 } else if (keywords8
.InList(s
)) {
160 sc
.ChangeState(SCE_LUA_WORD8
);
162 sc
.SetState(SCE_LUA_DEFAULT
);
164 } else if (sc
.state
== SCE_LUA_COMMENTLINE
) {
166 sc
.SetState(SCE_LUA_DEFAULT
);
168 } else if (sc
.state
== SCE_LUA_PREPROCESSOR
) {
170 sc
.SetState(SCE_LUA_DEFAULT
);
172 } else if (sc
.state
== SCE_LUA_STRING
) {
174 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
177 } else if (sc
.ch
== '\"') {
178 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
179 } else if (sc
.atLineEnd
) {
180 sc
.ChangeState(SCE_LUA_STRINGEOL
);
181 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
183 } else if (sc
.state
== SCE_LUA_CHARACTER
) {
185 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
188 } else if (sc
.ch
== '\'') {
189 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
190 } else if (sc
.atLineEnd
) {
191 sc
.ChangeState(SCE_LUA_STRINGEOL
);
192 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
194 } else if (sc
.state
== SCE_LUA_LITERALSTRING
) {
195 if (sc
.Match('[', '[')) {
196 literalStringLevel
++;
198 sc
.SetState(SCE_LUA_LITERALSTRING
);
199 } else if (sc
.Match(']', ']') && literalStringLevel
> 0) {
200 literalStringLevel
--;
202 if (literalStringLevel
== 0) {
203 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
206 } else if (sc
.state
== SCE_LUA_COMMENT
) { // Lua 5.0's block comment
207 if (sc
.Match('[', '[')) {
210 } else if (sc
.Match(']', ']') && blockCommentLevel
> 0) {
213 if (blockCommentLevel
== 0) {
214 sc
.ForwardSetState(SCE_LUA_DEFAULT
);
219 // Determine if a new state should be entered.
220 if (sc
.state
== SCE_LUA_DEFAULT
) {
221 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
222 sc
.SetState(SCE_LUA_NUMBER
);
223 } else if (IsAWordStart(sc
.ch
)) {
224 sc
.SetState(SCE_LUA_IDENTIFIER
);
225 } else if (sc
.Match('\"')) {
226 sc
.SetState(SCE_LUA_STRING
);
227 } else if (sc
.Match('\'')) {
228 sc
.SetState(SCE_LUA_CHARACTER
);
229 } else if (sc
.Match('[', '[')) {
230 literalStringLevel
= 1;
231 sc
.SetState(SCE_LUA_LITERALSTRING
);
233 } else if (sc
.Match("--[[")) { // Lua 5.0's block comment
234 blockCommentLevel
= 1;
235 sc
.SetState(SCE_LUA_COMMENT
);
237 } else if (sc
.Match('-', '-')) {
238 sc
.SetState(SCE_LUA_COMMENTLINE
);
240 } else if (sc
.atLineStart
&& sc
.Match('$')) {
241 sc
.SetState(SCE_LUA_PREPROCESSOR
); // Obsolete since Lua 4.0, but still in old code
242 } else if (IsLuaOperator(static_cast<char>(sc
.ch
))) {
243 sc
.SetState(SCE_LUA_OPERATOR
);
250 static void FoldLuaDoc(unsigned int startPos
, int length
, int /* initStyle */, WordList
*[],
252 unsigned int lengthDoc
= startPos
+ length
;
253 int visibleChars
= 0;
254 int lineCurrent
= styler
.GetLine(startPos
);
255 int levelPrev
= styler
.LevelAt(lineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
256 int levelCurrent
= levelPrev
;
257 char chNext
= styler
[startPos
];
258 bool foldCompact
= styler
.GetPropertyInt("fold.compact", 1) != 0;
259 int styleNext
= styler
.StyleAt(startPos
);
262 for (unsigned int i
= startPos
; i
< lengthDoc
; i
++) {
264 chNext
= styler
.SafeGetCharAt(i
+ 1);
265 int style
= styleNext
;
266 styleNext
= styler
.StyleAt(i
+ 1);
267 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
268 if (style
== SCE_LUA_WORD
) {
269 if (ch
== 'i' || ch
== 'd' || ch
== 'f' || ch
== 'e') {
270 for (unsigned int j
= 0; j
< 8; j
++) {
271 if (!iswordchar(styler
[i
+ j
])) {
274 s
[j
] = styler
[i
+ j
];
278 if ((strcmp(s
, "if") == 0) || (strcmp(s
, "do") == 0) || (strcmp(s
, "function") == 0)) {
281 if ((strcmp(s
, "end") == 0) || (strcmp(s
, "elseif") == 0)) {
285 } else if (style
== SCE_LUA_OPERATOR
) {
286 if (ch
== '{' || ch
== '(') {
288 } else if (ch
== '}' || ch
== ')') {
295 if (visibleChars
== 0 && foldCompact
) {
296 lev
|= SC_FOLDLEVELWHITEFLAG
;
298 if ((levelCurrent
> levelPrev
) && (visibleChars
> 0)) {
299 lev
|= SC_FOLDLEVELHEADERFLAG
;
301 if (lev
!= styler
.LevelAt(lineCurrent
)) {
302 styler
.SetLevel(lineCurrent
, lev
);
305 levelPrev
= levelCurrent
;
308 if (!isspacechar(ch
)) {
312 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
314 int flagsNext
= styler
.LevelAt(lineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
315 styler
.SetLevel(lineCurrent
, levelPrev
| flagsNext
);
318 static const char * const luaWordListDesc
[] = {
321 "String, (table) & math functions",
322 "(coroutines), I/O & system facilities",
328 LexerModule
lmLua(SCLEX_LUA
, ColouriseLuaDoc
, "lua", FoldLuaDoc
, luaWordListDesc
);