]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexLua.cxx
159bc1585d14f4347fd258c3114944a873613bb8
[wxWidgets.git] / src / stc / scintilla / src / LexLua.cxx
1 // Scintilla source code edit control
2 /** @file LexLua.cxx
3 ** Lexer for Lua language.
4 **
5 ** Written by Paul Winwood.
6 ** Folder by Alexey Yutkin.
7 ** Modified by Marcos E. Wurzius & Philippe Lhoste
8 **/
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <fcntl.h>
16
17 #include "Platform.h"
18
19 #include "PropSet.h"
20 #include "Accessor.h"
21 #include "StyleContext.h"
22 #include "KeyWords.h"
23 #include "Scintilla.h"
24 #include "SciLexer.h"
25
26 static inline bool IsAWordChar(const int ch) {
27 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
28 }
29
30 inline bool IsAWordStart(const int ch) {
31 return (ch < 0x80) && (isalnum(ch) || ch == '_');
32 }
33
34 inline bool isLuaOperator(char ch) {
35 if (isalnum(ch))
36 return false;
37 // '.' left out as it is used to make up numbers
38 if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
39 ch == '(' || ch == ')' || ch == '=' ||
40 ch == '{' || ch == '}' || ch == '~' ||
41 ch == '[' || ch == ']' || ch == ';' ||
42 ch == '<' || ch == '>' || ch == ',' ||
43 ch == '.' || ch == '^' || ch == '%' || ch == ':')
44 return true;
45 return false;
46 }
47
48 static void ColouriseLuaDoc(
49 unsigned int startPos,
50 int length,
51 int initStyle,
52 WordList *keywordlists[],
53 Accessor &styler) {
54
55 WordList &keywords = *keywordlists[0];
56 WordList &keywords2 = *keywordlists[1];
57 WordList &keywords3 = *keywordlists[2];
58 WordList &keywords4 = *keywordlists[3];
59 WordList &keywords5 = *keywordlists[4];
60 WordList &keywords6 = *keywordlists[5];
61
62 int currentLine = styler.GetLine(startPos);
63 // Initialize the literal string [[ ... ]] nesting level, if we are inside such a string.
64 int literalStringLevel = 0;
65 if (initStyle == SCE_LUA_LITERALSTRING) {
66 literalStringLevel = styler.GetLineState(currentLine - 1);
67 }
68 // Initialize the block comment --[[ ... ]] nesting level, if we are inside such a comment
69 int blockCommentLevel = 0;
70 if (initStyle == SCE_LUA_COMMENT) {
71 blockCommentLevel = styler.GetLineState(currentLine - 1);
72 }
73
74 // Do not leak onto next line
75 if (initStyle == SCE_LUA_STRINGEOL) {
76 initStyle = SCE_LUA_DEFAULT;
77 }
78
79 StyleContext sc(startPos, length, initStyle, styler);
80 if (startPos == 0 && sc.ch == '#') {
81 // shbang line: # is a comment only if first char of the script
82 sc.SetState(SCE_LUA_COMMENTLINE);
83 }
84 for (; sc.More(); sc.Forward()) {
85 if (sc.atLineEnd) {
86 // Update the line state, so it can be seen by next line
87 currentLine = styler.GetLine(sc.currentPos);
88 switch (sc.state) {
89 case SCE_LUA_LITERALSTRING:
90 // Inside a literal string, we set the line state
91 styler.SetLineState(currentLine, literalStringLevel);
92 break;
93 case SCE_LUA_COMMENT: // Block comment
94 // Inside a block comment, we set the line state
95 styler.SetLineState(currentLine, blockCommentLevel);
96 break;
97 default:
98 // Reset the line state
99 styler.SetLineState(currentLine, 0);
100 break;
101 }
102 }
103 if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) {
104 // Prevent SCE_LUA_STRINGEOL from leaking back to previous line
105 sc.SetState(SCE_LUA_STRING);
106 }
107
108 // Handle string line continuation
109 if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) &&
110 sc.ch == '\\') {
111 if (sc.chNext == '\n' || sc.chNext == '\r') {
112 sc.Forward();
113 if (sc.ch == '\r' && sc.chNext == '\n') {
114 sc.Forward();
115 }
116 continue;
117 }
118 }
119
120 // Determine if the current state should terminate.
121 if (sc.state == SCE_LUA_OPERATOR) {
122 sc.SetState(SCE_LUA_DEFAULT);
123 } else if (sc.state == SCE_LUA_NUMBER) {
124 if (!IsAWordChar(sc.ch)) {
125 sc.SetState(SCE_LUA_DEFAULT);
126 }
127 } else if (sc.state == SCE_LUA_IDENTIFIER) {
128 if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
129 char s[100];
130 sc.GetCurrent(s, sizeof(s));
131 if (keywords.InList(s)) {
132 sc.ChangeState(SCE_LUA_WORD);
133 } else if (keywords2.InList(s)) {
134 sc.ChangeState(SCE_LUA_WORD2);
135 } else if (keywords3.InList(s)) {
136 sc.ChangeState(SCE_LUA_WORD3);
137 } else if (keywords4.InList(s)) {
138 sc.ChangeState(SCE_LUA_WORD4);
139 } else if (keywords5.InList(s)) {
140 sc.ChangeState(SCE_LUA_WORD5);
141 } else if (keywords6.InList(s)) {
142 sc.ChangeState(SCE_LUA_WORD6);
143 }
144 sc.SetState(SCE_LUA_DEFAULT);
145 }
146 } else if (sc.state == SCE_LUA_COMMENTLINE ) {
147 if (sc.atLineEnd) {
148 sc.SetState(SCE_LUA_DEFAULT);
149 }
150 } else if (sc.state == SCE_LUA_PREPROCESSOR ) {
151 if (sc.atLineEnd) {
152 sc.SetState(SCE_LUA_DEFAULT);
153 }
154 } else if (sc.state == SCE_LUA_STRING) {
155 if (sc.ch == '\\') {
156 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
157 sc.Forward();
158 }
159 } else if (sc.ch == '\"') {
160 sc.ForwardSetState(SCE_LUA_DEFAULT);
161 } else if (sc.atLineEnd) {
162 sc.ChangeState(SCE_LUA_STRINGEOL);
163 sc.ForwardSetState(SCE_LUA_DEFAULT);
164 }
165 } else if (sc.state == SCE_LUA_CHARACTER) {
166 if (sc.ch == '\\') {
167 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
168 sc.Forward();
169 }
170 } else if (sc.ch == '\'') {
171 sc.ForwardSetState(SCE_LUA_DEFAULT);
172 } else if (sc.atLineEnd) {
173 sc.ChangeState(SCE_LUA_STRINGEOL);
174 sc.ForwardSetState(SCE_LUA_DEFAULT);
175 }
176 } else if (sc.state == SCE_LUA_LITERALSTRING) {
177 if (sc.Match('[', '[')) {
178 literalStringLevel++;
179 sc.Forward();
180 sc.SetState(SCE_LUA_LITERALSTRING);
181 } else if (sc.Match(']', ']') && literalStringLevel > 0) {
182 literalStringLevel--;
183 sc.Forward();
184 if (literalStringLevel == 0) {
185 sc.ForwardSetState(SCE_LUA_DEFAULT);
186 }
187 }
188 } else if (sc.state == SCE_LUA_COMMENT) { // Lua 5.0's block comment
189 if (sc.Match('[', '[')) {
190 blockCommentLevel++;
191 sc.Forward();
192 } else if (sc.Match(']', ']') && blockCommentLevel > 0) {
193 blockCommentLevel--;
194 sc.Forward();
195 if (blockCommentLevel == 0) {
196 sc.ForwardSetState(SCE_LUA_DEFAULT);
197 }
198 }
199 }
200
201 // Determine if a new state should be entered.
202 if (sc.state == SCE_LUA_DEFAULT) {
203 if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
204 sc.SetState(SCE_LUA_NUMBER);
205 } else if (IsAWordStart(sc.ch)) {
206 sc.SetState(SCE_LUA_IDENTIFIER);
207 } else if (sc.Match('\"')) {
208 sc.SetState(SCE_LUA_STRING);
209 } else if (sc.Match('\'')) {
210 sc.SetState(SCE_LUA_CHARACTER);
211 } else if (sc.Match('[', '[')) {
212 literalStringLevel = 1;
213 sc.SetState(SCE_LUA_LITERALSTRING);
214 sc.Forward();
215 } else if (sc.Match("--[[")) { // Lua 5.0's block comment
216 blockCommentLevel = 1;
217 sc.SetState(SCE_LUA_COMMENT);
218 sc.Forward(3);
219 } else if (sc.Match('-', '-')) {
220 sc.SetState(SCE_LUA_COMMENTLINE);
221 sc.Forward();
222 } else if (sc.atLineStart && sc.Match('$')) {
223 sc.SetState(SCE_LUA_PREPROCESSOR); // Obsolete since Lua 4.0, but still in old code
224 } else if (isLuaOperator(static_cast<char>(sc.ch))) {
225 sc.SetState(SCE_LUA_OPERATOR);
226 }
227 }
228 }
229 sc.Complete();
230 }
231
232 static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
233 Accessor &styler) {
234 unsigned int lengthDoc = startPos + length;
235 int visibleChars = 0;
236 int lineCurrent = styler.GetLine(startPos);
237 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
238 int levelCurrent = levelPrev;
239 char chNext = styler[startPos];
240 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
241 int styleNext = styler.StyleAt(startPos);
242 char s[10];
243
244 for (unsigned int i = startPos; i < lengthDoc; i++) {
245 char ch = chNext;
246 chNext = styler.SafeGetCharAt(i + 1);
247 int style = styleNext;
248 styleNext = styler.StyleAt(i + 1);
249 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
250 if (style == SCE_LUA_WORD) {
251 if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e') {
252 for (unsigned int j = 0; j < 8; j++) {
253 if (!iswordchar(styler[i + j])) {
254 break;
255 }
256 s[j] = styler[i + j];
257 s[j + 1] = '\0';
258 }
259
260 if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0)) {
261 levelCurrent++;
262 }
263 if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0)) {
264 levelCurrent--;
265 }
266 }
267 } else if (style == SCE_LUA_OPERATOR) {
268 if (ch == '{' || ch == '(') {
269 levelCurrent++;
270 } else if (ch == '}' || ch == ')') {
271 levelCurrent--;
272 }
273 }
274
275 if (atEOL) {
276 int lev = levelPrev;
277 if (visibleChars == 0 && foldCompact) {
278 lev |= SC_FOLDLEVELWHITEFLAG;
279 }
280 if ((levelCurrent > levelPrev) && (visibleChars > 0)) {
281 lev |= SC_FOLDLEVELHEADERFLAG;
282 }
283 if (lev != styler.LevelAt(lineCurrent)) {
284 styler.SetLevel(lineCurrent, lev);
285 }
286 lineCurrent++;
287 levelPrev = levelCurrent;
288 visibleChars = 0;
289 }
290 if (!isspacechar(ch)) {
291 visibleChars++;
292 }
293 }
294 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
295
296 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
297 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
298 }
299
300 static const char * const luaWordListDesc[] = {
301 "Keywords",
302 "Basic functions",
303 "String & math functions",
304 "I/O & system facilities",
305 "XXX",
306 "XXX",
307 0
308 };
309
310 LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);