Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_text / text_format_lua.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  *
17  * ***** END GPL LICENSE BLOCK *****
18  */
19
20 /** \file blender/editors/space_text/text_format_lua.c
21  *  \ingroup sptext
22  */
23
24 #include <string.h>
25
26 #include "BLI_blenlib.h"
27
28 #include "DNA_text_types.h"
29 #include "DNA_space_types.h"
30
31 #include "BKE_text.h"
32
33 #include "text_format.h"
34
35 /* *** Lua Keywords (for format_line) *** */
36
37 /* Checks the specified source string for a Lua keyword (minus boolean & 'nil'). 
38  * This name must start at the beginning of the source string and must be 
39  * followed by a non-identifier (see text_check_identifier(char)) or null char.
40  *
41  * If a keyword is found, the length of the matching word is returned.
42  * Otherwise, -1 is returned.
43  *
44  * See:
45  * http://www.lua.org/manual/5.1/manual.html#2.1
46  */
47
48 static int txtfmt_lua_find_keyword(const char *string)
49 {
50         int i, len;
51
52         if      (STR_LITERAL_STARTSWITH(string, "and",      len)) i = len;
53         else if (STR_LITERAL_STARTSWITH(string, "break",    len)) i = len;
54         else if (STR_LITERAL_STARTSWITH(string, "do",       len)) i = len;
55         else if (STR_LITERAL_STARTSWITH(string, "else",     len)) i = len;
56         else if (STR_LITERAL_STARTSWITH(string, "elseif",   len)) i = len;
57         else if (STR_LITERAL_STARTSWITH(string, "end",      len)) i = len;
58         else if (STR_LITERAL_STARTSWITH(string, "for",      len)) i = len;
59         else if (STR_LITERAL_STARTSWITH(string, "function", len)) i = len;
60         else if (STR_LITERAL_STARTSWITH(string, "if",       len)) i = len;
61         else if (STR_LITERAL_STARTSWITH(string, "in",       len)) i = len;
62         else if (STR_LITERAL_STARTSWITH(string, "local",    len)) i = len;
63         else if (STR_LITERAL_STARTSWITH(string, "not",      len)) i = len;
64         else if (STR_LITERAL_STARTSWITH(string, "or",       len)) i = len;
65         else if (STR_LITERAL_STARTSWITH(string, "repeat",   len)) i = len;
66         else if (STR_LITERAL_STARTSWITH(string, "return",   len)) i = len;
67         else if (STR_LITERAL_STARTSWITH(string, "then",     len)) i = len;
68         else if (STR_LITERAL_STARTSWITH(string, "until",    len)) i = len;
69         else if (STR_LITERAL_STARTSWITH(string, "while",    len)) i = len;
70         else                                                      i = 0;
71
72         /* If next source char is an identifier (eg. 'i' in "definate") no match */
73         if (i == 0 || text_check_identifier(string[i]))
74                 return -1;
75         return i;
76 }
77
78 /* Checks the specified source string for a Lua special name/function. This 
79  * name must start at the beginning of the source string and must be followed 
80  * by a non-identifier (see text_check_identifier(char)) or null character.
81  *
82  * If a special name is found, the length of the matching name is returned.
83  * Otherwise, -1 is returned. 
84  * 
85  * See:
86  * http://www.lua.org/manual/5.1/manual.html#5.1
87  */
88
89 static int txtfmt_lua_find_specialvar(const char *string)
90 {
91         int i, len;
92
93         if      (STR_LITERAL_STARTSWITH(string, "assert",           len))   i = len;
94         else if (STR_LITERAL_STARTSWITH(string, "collectgarbage",   len))   i = len;
95         else if (STR_LITERAL_STARTSWITH(string, "dofile",           len))   i = len;
96         else if (STR_LITERAL_STARTSWITH(string, "error",            len))   i = len;
97         else if (STR_LITERAL_STARTSWITH(string, "_G",               len))   i = len;
98         else if (STR_LITERAL_STARTSWITH(string, "getfenv",          len))   i = len;
99         else if (STR_LITERAL_STARTSWITH(string, "getmetatable",     len))   i = len;
100         else if (STR_LITERAL_STARTSWITH(string, "__index",          len))   i = len;
101         else if (STR_LITERAL_STARTSWITH(string, "ipairs",           len))   i = len;
102         else if (STR_LITERAL_STARTSWITH(string, "load",             len))   i = len;
103         else if (STR_LITERAL_STARTSWITH(string, "loadfile",         len))   i = len;
104         else if (STR_LITERAL_STARTSWITH(string, "loadstring",       len))   i = len;
105         else if (STR_LITERAL_STARTSWITH(string, "next",             len))   i = len;
106         else if (STR_LITERAL_STARTSWITH(string, "pairs",            len))   i = len;
107         else if (STR_LITERAL_STARTSWITH(string, "pcall",            len))   i = len;
108         else if (STR_LITERAL_STARTSWITH(string, "print",            len))   i = len;
109         else if (STR_LITERAL_STARTSWITH(string, "rawequal",         len))   i = len;
110         else if (STR_LITERAL_STARTSWITH(string, "rawget",           len))   i = len;
111         else if (STR_LITERAL_STARTSWITH(string, "rawset",           len))   i = len;
112         else if (STR_LITERAL_STARTSWITH(string, "select",           len))   i = len;
113         else if (STR_LITERAL_STARTSWITH(string, "setfenv",          len))   i = len;
114         else if (STR_LITERAL_STARTSWITH(string, "setmetatable",     len))   i = len;
115         else if (STR_LITERAL_STARTSWITH(string, "tonumber",         len))   i = len;
116         else if (STR_LITERAL_STARTSWITH(string, "tostring",         len))   i = len;
117         else if (STR_LITERAL_STARTSWITH(string, "type",             len))   i = len;
118         else if (STR_LITERAL_STARTSWITH(string, "unpack",           len))   i = len;
119         else if (STR_LITERAL_STARTSWITH(string, "_VERSION",         len))   i = len;
120         else if (STR_LITERAL_STARTSWITH(string, "xpcall",           len))   i = len;
121         else                                                i = 0;
122
123         /* If next source char is an identifier (eg. 'i' in "definate") no match */
124         if (i == 0 || text_check_identifier(string[i]))
125                 return -1;
126         return i;
127 }
128
129 static int txtfmt_lua_find_bool(const char *string)
130 {
131         int i, len;
132
133         if      (STR_LITERAL_STARTSWITH(string, "nil",   len))  i = len;
134         else if (STR_LITERAL_STARTSWITH(string, "true",  len))  i = len;
135         else if (STR_LITERAL_STARTSWITH(string, "false", len))  i = len;
136         else                                                    i = 0;
137
138         /* If next source char is an identifier (eg. 'i' in "Nonetheless") no match */
139         if (i == 0 || text_check_identifier(string[i]))
140                 return -1;
141         return i;
142 }
143
144 static char txtfmt_lua_format_identifier(const char *str)
145 {
146         char fmt;
147         if      ((txtfmt_lua_find_specialvar(str))  != -1) fmt = FMT_TYPE_SPECIAL;
148         else if ((txtfmt_lua_find_keyword(str))     != -1) fmt = FMT_TYPE_KEYWORD;
149         else                                               fmt = FMT_TYPE_DEFAULT;
150         return fmt;
151 }
152
153 static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_next)
154 {
155         FlattenString fs;
156         const char *str;
157         char *fmt;
158         char cont_orig, cont, find, prev = ' ';
159         int len, i;
160
161         /* Get continuation from previous line */
162         if (line->prev && line->prev->format != NULL) {
163                 fmt = line->prev->format;
164                 cont = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
165                 BLI_assert((FMT_CONT_ALL & cont) == cont);
166         }
167         else {
168                 cont = FMT_CONT_NOP;
169         }
170
171         /* Get original continuation from this line */
172         if (line->format != NULL) {
173                 fmt = line->format;
174                 cont_orig = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
175                 BLI_assert((FMT_CONT_ALL & cont_orig) == cont_orig);
176         }
177         else {
178                 cont_orig = 0xFF;
179         }
180
181         len = flatten_string(st, &fs, line->line);
182         str = fs.buf;
183         if (!text_check_format_len(line, len)) {
184                 flatten_string_free(&fs);
185                 return;
186         }
187         fmt = line->format;
188
189         while (*str) {
190                 /* Handle escape sequences by skipping both \ and next char */
191                 if (*str == '\\') {
192                         *fmt = prev; fmt++; str++;
193                         if (*str == '\0') break;
194                         *fmt = prev; fmt++; str += BLI_str_utf8_size_safe(str);
195                         continue;
196                 }
197                 /* Handle continuations */
198                 else if (cont) {
199                         /* Multi-line comments */
200                         if (cont & FMT_CONT_COMMENT_C) {
201                                 if (*str == ']' && *(str + 1) == ']') {
202                                         *fmt = FMT_TYPE_COMMENT; fmt++; str++;
203                                         *fmt = FMT_TYPE_COMMENT;
204                                         cont = FMT_CONT_NOP;
205                                 }
206                                 else {
207                                         *fmt = FMT_TYPE_COMMENT;
208                                 }
209                                 /* Handle other comments */
210                         }
211                         else {
212                                 find = (cont & FMT_CONT_QUOTEDOUBLE) ? '"' : '\'';
213                                 if (*str == find) cont = 0;
214                                 *fmt = FMT_TYPE_STRING;
215                         }
216
217                         str += BLI_str_utf8_size_safe(str) - 1;
218                 }
219                 /* Not in a string... */
220                 else {
221                         /* Multi-line comments */
222                         if (*str == '-'       && *(str + 1) == '-' &&
223                             *(str + 2) == '[' && *(str + 3) == '[')
224                         {
225                                 cont = FMT_CONT_COMMENT_C;
226                                 *fmt = FMT_TYPE_COMMENT; fmt++; str++;
227                                 *fmt = FMT_TYPE_COMMENT; fmt++; str++;
228                                 *fmt = FMT_TYPE_COMMENT; fmt++; str++;
229                                 *fmt = FMT_TYPE_COMMENT;
230                         }
231                         /* Single line comment */
232                         else if (*str == '-' && *(str + 1) == '-') {
233                                 text_format_fill(&str, &fmt, FMT_TYPE_COMMENT, len - (int)(fmt - line->format));
234                         }
235                         else if (*str == '"' || *str == '\'') {
236                                 /* Strings */
237                                 find = *str;
238                                 cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
239                                 *fmt = FMT_TYPE_STRING;
240                         }
241                         /* Whitespace (all ws. has been converted to spaces) */
242                         else if (*str == ' ') {
243                                 *fmt = FMT_TYPE_WHITESPACE;
244                         }
245                         /* Numbers (digits not part of an identifier and periods followed by digits) */
246                         else if ((prev != FMT_TYPE_DEFAULT && text_check_digit(*str)) ||
247                                  (*str == '.' && text_check_digit(*(str + 1))))
248                         {
249                                 *fmt = FMT_TYPE_NUMERAL;
250                         }
251                         /* Booleans */
252                         else if (prev != FMT_TYPE_DEFAULT && (i = txtfmt_lua_find_bool(str)) != -1) {
253                                 if (i > 0) {
254                                         text_format_fill_ascii(&str, &fmt, FMT_TYPE_NUMERAL, i);
255                                 }
256                                 else {
257                                         str += BLI_str_utf8_size_safe(str) - 1;
258                                         *fmt = FMT_TYPE_DEFAULT;
259                                 }
260                         }
261                         /* Punctuation */
262                         else if ((*str != '#') && text_check_delim(*str)) {
263                                 *fmt = FMT_TYPE_SYMBOL;
264                         }
265                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
266                         else if (prev == FMT_TYPE_DEFAULT) {
267                                 str += BLI_str_utf8_size_safe(str) - 1;
268                                 *fmt = FMT_TYPE_DEFAULT;
269                         }
270                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
271                         else {
272                                 /* Special vars(v) or built-in keywords(b) */
273                                 /* keep in sync with 'txtfmt_osl_format_identifier()' */
274                                 if      ((i = txtfmt_lua_find_specialvar(str))   != -1) prev = FMT_TYPE_SPECIAL;
275                                 else if ((i = txtfmt_lua_find_keyword(str))      != -1) prev = FMT_TYPE_KEYWORD;
276
277                                 if (i > 0) {
278                                         text_format_fill_ascii(&str, &fmt, prev, i);
279                                 }
280                                 else {
281                                         str += BLI_str_utf8_size_safe(str) - 1;
282                                         *fmt = FMT_TYPE_DEFAULT;
283                                 }
284                         }
285                 }
286                 prev = *fmt; fmt++; str++;
287         }
288
289         /* Terminate and add continuation char */
290         *fmt = '\0'; fmt++;
291         *fmt = cont;
292
293         /* If continuation has changed and we're allowed, process the next line */
294         if (cont != cont_orig && do_next && line->next) {
295                 txtfmt_lua_format_line(st, line->next, do_next);
296         }
297
298         flatten_string_free(&fs);
299 }
300
301 void ED_text_format_register_lua(void)
302 {
303         static TextFormatType tft = {NULL};
304         static const char *ext[] = {"lua", NULL};
305
306         tft.format_identifier = txtfmt_lua_format_identifier;
307         tft.format_line       = txtfmt_lua_format_line;
308         tft.ext = ext;
309
310         ED_text_format_register(&tft);
311 }