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