Add xml_indent_tags filetype setting for documents using the
[geany-mirror.git] / scintilla / LexLua.cxx
bloba1e579f269281546bbdf540a3756e444e987a463
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 **/
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdio.h>
16 #include "Platform.h"
18 #include "PropSet.h"
19 #include "Accessor.h"
20 #include "StyleContext.h"
21 #include "KeyWords.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24 #include "CharacterSet.h"
26 #ifdef SCI_NAMESPACE
27 using namespace Scintilla;
28 #endif
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) {
34 int sep = 1;
35 while (sc.GetRelative(sep) == '=' && sep < 0xFF)
36 sep++;
37 if (sc.GetRelative(sep) == sc.ch)
38 return sep;
39 return 0;
42 static void ColouriseLuaDoc(
43 unsigned int startPos,
44 int length,
45 int initStyle,
46 WordList *keywordlists[],
47 Accessor &styler) {
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.
71 int nestLevel = 0;
72 int sepCount = 0;
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()) {
90 if (sc.atLineEnd) {
91 // Update the line state, so it can be seen by next line
92 currentLine = styler.GetLine(sc.currentPos);
93 switch (sc.state) {
94 case SCE_LUA_LITERALSTRING:
95 case SCE_LUA_COMMENT:
96 // Inside a literal string or block comment, we set the line state
97 styler.SetLineState(currentLine, (nestLevel << 8) | sepCount);
98 break;
99 default:
100 // Reset the line state
101 styler.SetLineState(currentLine, 0);
102 break;
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) &&
112 sc.ch == '\\') {
113 if (sc.chNext == '\n' || sc.chNext == '\r') {
114 sc.Forward();
115 if (sc.ch == '\r' && sc.chNext == '\n') {
116 sc.Forward();
118 continue;
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('.', '.')) {
135 char s[100];
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) {
157 if (sc.atLineEnd) {
158 sc.ForwardSetState(SCE_LUA_DEFAULT);
160 } else if (sc.state == SCE_LUA_STRING) {
161 if (sc.ch == '\\') {
162 if (setEscapeSkip.Contains(sc.chNext)) {
163 sc.Forward();
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) {
172 if (sc.ch == '\\') {
173 if (setEscapeSkip.Contains(sc.chNext)) {
174 sc.Forward();
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) {
183 if (sc.ch == '[') {
184 int sep = LongDelimCheck(sc);
185 if (sep == 1 && sepCount == 1) { // [[-only allowed to nest
186 nestLevel++;
187 sc.Forward();
189 } else if (sc.ch == ']') {
190 int sep = LongDelimCheck(sc);
191 if (sep == 1 && sepCount == 1) { // un-nest with ]]-only
192 nestLevel--;
193 sc.Forward();
194 if (nestLevel == 0) {
195 sc.ForwardSetState(SCE_LUA_DEFAULT);
197 } else if (sep > 1 && sep == sepCount) { // ]=]-style delim
198 sc.Forward(sep);
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') {
209 sc.Forward();
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);
219 if (sepCount == 0) {
220 sc.SetState(SCE_LUA_OPERATOR);
221 } else {
222 nestLevel = 1;
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("--[")) {
229 sc.Forward(2);
230 sepCount = LongDelimCheck(sc);
231 if (sepCount > 0) {
232 nestLevel = 1;
233 sc.ChangeState(SCE_LUA_COMMENT);
234 sc.Forward(sepCount);
236 } else {
237 sc.Forward();
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)) {
248 char s[100];
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);
269 sc.Complete();
272 static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
273 Accessor &styler) {
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);
282 char s[10];
284 for (unsigned int i = startPos; i < lengthDoc; i++) {
285 char ch = chNext;
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])) {
294 break;
296 s[j] = styler[i + j];
297 s[j + 1] = '\0';
300 if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
301 levelCurrent++;
303 if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
304 levelCurrent--;
307 } else if (style == SCE_LUA_OPERATOR) {
308 if (ch == '{' || ch == '(') {
309 levelCurrent++;
310 } else if (ch == '}' || ch == ')') {
311 levelCurrent--;
313 } else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
314 if (ch == '[') {
315 levelCurrent++;
316 } else if (ch == ']') {
317 levelCurrent--;
321 if (atEOL) {
322 int lev = levelPrev;
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);
332 lineCurrent++;
333 levelPrev = levelCurrent;
334 visibleChars = 0;
336 if (!isspacechar(ch)) {
337 visibleChars++;
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[] = {
347 "Keywords",
348 "Basic functions",
349 "String, (table) & math functions",
350 "(coroutines), I/O & system facilities",
351 "user1",
352 "user2",
353 "user3",
354 "user4",
358 LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);