fix own regression in 2.66 [#34610] Text editor: Syntax highlighting freezes
[blender.git] / source / blender / editors / space_text / text_format_osl.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  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
18  * All rights reserved.
19  *
20  * The Original Code is: all of this file.
21  *
22  * Contributor(s): none yet.
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_text/text_format_osl.c
28  *  \ingroup sptext
29  */
30
31 #include <string.h>
32
33 #include "BLI_blenlib.h"
34
35 #include "DNA_text_types.h"
36 #include "DNA_space_types.h"
37
38 #include "BKE_text.h"
39
40 #include "text_format.h"
41
42 /* *** Local Functions (for format_line) *** */
43
44 static int txtfmt_osl_find_builtinfunc(const char *string)
45 {
46         int i, len;
47         /* list is from
48          * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf
49          */
50         if      (STR_LITERAL_STARTSWITH(string, "break",        len)) i = len;
51         else if (STR_LITERAL_STARTSWITH(string, "closure",      len)) i = len;
52         else if (STR_LITERAL_STARTSWITH(string, "color",        len)) i = len;
53         else if (STR_LITERAL_STARTSWITH(string, "continue",     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, "emit",         len)) i = len;
57         else if (STR_LITERAL_STARTSWITH(string, "float",        len)) i = len;
58         else if (STR_LITERAL_STARTSWITH(string, "for",          len)) i = len;
59         else if (STR_LITERAL_STARTSWITH(string, "if",           len)) i = len;
60         else if (STR_LITERAL_STARTSWITH(string, "illuminance",  len)) i = len;
61         else if (STR_LITERAL_STARTSWITH(string, "illuminate",   len)) i = len;
62         else if (STR_LITERAL_STARTSWITH(string, "int",          len)) i = len;
63         else if (STR_LITERAL_STARTSWITH(string, "matrix",       len)) i = len;
64         else if (STR_LITERAL_STARTSWITH(string, "normal",       len)) i = len;
65         else if (STR_LITERAL_STARTSWITH(string, "output",       len)) i = len;
66         else if (STR_LITERAL_STARTSWITH(string, "point",        len)) i = len;
67         else if (STR_LITERAL_STARTSWITH(string, "public",       len)) i = len;
68         else if (STR_LITERAL_STARTSWITH(string, "return",       len)) i = len;
69         else if (STR_LITERAL_STARTSWITH(string, "string",       len)) i = len;
70         else if (STR_LITERAL_STARTSWITH(string, "struct",       len)) i = len;
71         else if (STR_LITERAL_STARTSWITH(string, "vector",       len)) i = len;
72         else if (STR_LITERAL_STARTSWITH(string, "void",         len)) i = len;
73         else if (STR_LITERAL_STARTSWITH(string, "while",        len)) i = len;
74         else                                                          i = 0;
75
76         /* If next source char is an identifier (eg. 'i' in "definate") no match */
77         if (i == 0 || text_check_identifier(string[i]))
78                 return -1;
79         return i;
80 }
81
82 static int txtfmt_osl_find_reserved(const char *string)
83 {
84         int i, len;
85         /* list is from...
86          * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf
87          */
88         if      (STR_LITERAL_STARTSWITH(string, "bool",         len)) i = len;
89         else if (STR_LITERAL_STARTSWITH(string, "case",         len)) i = len;
90         else if (STR_LITERAL_STARTSWITH(string, "catch",        len)) i = len;
91         else if (STR_LITERAL_STARTSWITH(string, "char",         len)) i = len;
92         else if (STR_LITERAL_STARTSWITH(string, "const",        len)) i = len;
93         else if (STR_LITERAL_STARTSWITH(string, "delete",       len)) i = len;
94         else if (STR_LITERAL_STARTSWITH(string, "default",      len)) i = len;
95         else if (STR_LITERAL_STARTSWITH(string, "double",       len)) i = len;
96         else if (STR_LITERAL_STARTSWITH(string, "enum",         len)) i = len;
97         else if (STR_LITERAL_STARTSWITH(string, "extern",       len)) i = len;
98         else if (STR_LITERAL_STARTSWITH(string, "false",        len)) i = len;
99         else if (STR_LITERAL_STARTSWITH(string, "friend",       len)) i = len;
100         else if (STR_LITERAL_STARTSWITH(string, "goto",         len)) i = len;
101         else if (STR_LITERAL_STARTSWITH(string, "inline",       len)) i = len;
102         else if (STR_LITERAL_STARTSWITH(string, "long",         len)) i = len;
103         else if (STR_LITERAL_STARTSWITH(string, "new",          len)) i = len;
104         else if (STR_LITERAL_STARTSWITH(string, "operator",     len)) i = len;
105         else if (STR_LITERAL_STARTSWITH(string, "private",      len)) i = len;
106         else if (STR_LITERAL_STARTSWITH(string, "protected",    len)) i = len;
107         else if (STR_LITERAL_STARTSWITH(string, "short",        len)) i = len;
108         else if (STR_LITERAL_STARTSWITH(string, "signed",       len)) i = len;
109         else if (STR_LITERAL_STARTSWITH(string, "sizeof",       len)) i = len;
110         else if (STR_LITERAL_STARTSWITH(string, "static",       len)) i = len;
111         else if (STR_LITERAL_STARTSWITH(string, "switch",       len)) i = len;
112         else if (STR_LITERAL_STARTSWITH(string, "template",     len)) i = len;
113         else if (STR_LITERAL_STARTSWITH(string, "this",         len)) i = len;
114         else if (STR_LITERAL_STARTSWITH(string, "throw",        len)) i = len;
115         else if (STR_LITERAL_STARTSWITH(string, "true",         len)) i = len;
116         else if (STR_LITERAL_STARTSWITH(string, "try",          len)) i = len;
117         else if (STR_LITERAL_STARTSWITH(string, "typedef",      len)) i = len;
118         else if (STR_LITERAL_STARTSWITH(string, "uniform",      len)) i = len;
119         else if (STR_LITERAL_STARTSWITH(string, "union",        len)) i = len;
120         else if (STR_LITERAL_STARTSWITH(string, "unsigned",     len)) i = len;
121         else if (STR_LITERAL_STARTSWITH(string, "varying",      len)) i = len;
122         else if (STR_LITERAL_STARTSWITH(string, "virtual",      len)) i = len;
123         else if (STR_LITERAL_STARTSWITH(string, "volatile",     len)) i = len;
124         else                                                          i = 0;
125
126         /* If next source char is an identifier (eg. 'i' in "definate") no match */
127         if (i == 0 || text_check_identifier(string[i]))
128                 return -1;
129         return i;
130 }
131
132 /* Checks the specified source string for a OSL special name. This name must
133  * start at the beginning of the source string and must be followed by a non-
134  * identifier (see text_check_identifier(char)) or null character.
135  *
136  * If a special name is found, the length of the matching name is returned.
137  * Otherwise, -1 is returned. */
138
139 static int txtfmt_osl_find_specialvar(const char *string)
140 {
141         int i, len;
142         
143         /* OSL shader types */
144         if      (STR_LITERAL_STARTSWITH(string, "shader",               len)) i = len;
145         else if (STR_LITERAL_STARTSWITH(string, "surface",              len)) i = len;
146         else if (STR_LITERAL_STARTSWITH(string, "volume",               len)) i = len;
147         else if (STR_LITERAL_STARTSWITH(string, "displacement", len)) i = len;
148         else                                                    i = 0;
149
150         /* If next source char is an identifier (eg. 'i' in "definate") no match */
151         if (i == 0 || text_check_identifier(string[i]))
152                 return -1;
153         return i;
154 }
155
156 /* matches py 'txtfmt_osl_find_decorator' */
157 static int txtfmt_osl_find_preprocessor(const char *string)
158 {
159         if (string[0] == '#') {
160                 int i = 1;
161                 /* Whitespace is ok '#  foo' */
162                 while (text_check_whitespace(string[i])) {
163                         i++;
164                 }
165                 while (text_check_identifier(string[i])) {
166                         i++;
167                 }
168                 return i;
169         }
170         return -1;
171 }
172
173 static char txtfmt_osl_format_identifier(const char *str)
174 {
175         char fmt;
176         if      ((txtfmt_osl_find_specialvar(str))   != -1) fmt = FMT_TYPE_SPECIAL;
177         else if ((txtfmt_osl_find_builtinfunc(str))  != -1) fmt = FMT_TYPE_KEYWORD;
178         else if ((txtfmt_osl_find_reserved(str))     != -1) fmt = FMT_TYPE_RESERVED;
179         else if ((txtfmt_osl_find_preprocessor(str)) != -1) fmt = FMT_TYPE_DIRECTIVE;
180         else                                                fmt = FMT_TYPE_DEFAULT;
181         return fmt;
182 }
183
184 static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const int do_next)
185 {
186         FlattenString fs;
187         const char *str;
188         char *fmt;
189         char cont_orig, cont, find, prev = ' ';
190         int len, i;
191
192         /* Get continuation from previous line */
193         if (line->prev && line->prev->format != NULL) {
194                 fmt = line->prev->format;
195                 cont = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
196                 BLI_assert((FMT_CONT_ALL & cont) == cont);
197         }
198         else {
199                 cont = FMT_CONT_NOP;
200         }
201
202         /* Get original continuation from this line */
203         if (line->format != NULL) {
204                 fmt = line->format;
205                 cont_orig = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
206                 BLI_assert((FMT_CONT_ALL & cont_orig) == cont_orig);
207         }
208         else {
209                 cont_orig = 0xFF;
210         }
211
212         len = flatten_string(st, &fs, line->line);
213         str = fs.buf;
214         if (!text_check_format_len(line, len)) {
215                 flatten_string_free(&fs);
216                 return;
217         }
218         fmt = line->format;
219
220         while (*str) {
221                 /* Handle escape sequences by skipping both \ and next char */
222                 if (*str == '\\') {
223                         *fmt = prev; fmt++; str++;
224                         if (*str == '\0') break;
225                         *fmt = prev; fmt++; str += BLI_str_utf8_size_safe(str);
226                         continue;
227                 }
228                 /* Handle continuations */
229                 else if (cont) {
230                         /* C-Style comments */
231                         if (cont & FMT_CONT_COMMENT_C) {
232                                 if (*str == '*' && *(str + 1) == '/') {
233                                         *fmt = FMT_TYPE_COMMENT; fmt++; str++;
234                                         *fmt = FMT_TYPE_COMMENT;
235                                         cont = FMT_CONT_NOP;
236                                 }
237                                 else {
238                                         *fmt = FMT_TYPE_COMMENT;
239                                 }
240                                 /* Handle other comments */
241                         }
242                         else {
243                                 find = (cont & FMT_CONT_QUOTEDOUBLE) ? '"' : '\'';
244                                 if (*str == find) cont = 0;
245                                 *fmt = FMT_TYPE_STRING;
246                         }
247
248                         str += BLI_str_utf8_size_safe(str) - 1;
249                 }
250                 /* Not in a string... */
251                 else {
252                         /* Deal with comments first */
253                         if (*str == '/' && *(str + 1) == '/') {
254                                 /* fill the remaining line */
255                                 text_format_fill(&str, &fmt, FMT_TYPE_COMMENT, len - (int)(fmt - line->format));
256                         }
257                         /* C-Style (multi-line) comments */
258                         else if (*str == '/' && *(str + 1) == '*') {
259                                 cont = FMT_CONT_COMMENT_C;
260                                 *fmt = FMT_TYPE_COMMENT; fmt++; str++;
261                                 *fmt = FMT_TYPE_COMMENT;
262                         }
263                         else if (*str == '"' || *str == '\'') {
264                                 /* Strings */
265                                 find = *str;
266                                 cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
267                                 *fmt = FMT_TYPE_STRING;
268                         }
269                         /* Whitespace (all ws. has been converted to spaces) */
270                         else if (*str == ' ') {
271                                 *fmt = FMT_TYPE_WHITESPACE;
272                         }
273                         /* Numbers (digits not part of an identifier and periods followed by digits) */
274                         else if ((prev != FMT_TYPE_DEFAULT && text_check_digit(*str)) ||
275                                  (*str == '.' && text_check_digit(*(str + 1))))
276                         {
277                                 *fmt = FMT_TYPE_NUMERAL;
278                         }
279                         /* Punctuation */
280                         else if ((*str != '#') && text_check_delim(*str)) {
281                                 *fmt = FMT_TYPE_SYMBOL;
282                         }
283                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
284                         else if (prev == FMT_TYPE_DEFAULT) {
285                                 str += BLI_str_utf8_size_safe(str) - 1;
286                                 *fmt = FMT_TYPE_DEFAULT;
287                         }
288                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
289                         else {
290                                 /* Special vars(v) or built-in keywords(b) */
291                                 /* keep in sync with 'txtfmt_osl_format_identifier()' */
292                                 if      ((i = txtfmt_osl_find_specialvar(str))   != -1) prev = FMT_TYPE_SPECIAL;
293                                 else if ((i = txtfmt_osl_find_builtinfunc(str))  != -1) prev = FMT_TYPE_KEYWORD;
294                                 else if ((i = txtfmt_osl_find_reserved(str))     != -1) prev = FMT_TYPE_RESERVED;
295                                 else if ((i = txtfmt_osl_find_preprocessor(str)) != -1) prev = FMT_TYPE_DIRECTIVE;
296
297                                 if (i > 0) {
298                                         if (prev == FMT_TYPE_DIRECTIVE) {  /* can contain utf8 */
299                                                 text_format_fill(&str, &fmt, prev, i);
300                                         }
301                                         else {
302                                                 text_format_fill_ascii(&str, &fmt, prev, i);
303                                         }
304                                 }
305                                 else {
306                                         str += BLI_str_utf8_size_safe(str) - 1;
307                                         *fmt = FMT_TYPE_DEFAULT;
308                                 }
309                         }
310                 }
311                 prev = *fmt; fmt++; str++;
312         }
313
314         /* Terminate and add continuation char */
315         *fmt = '\0'; fmt++;
316         *fmt = cont;
317
318         /* If continuation has changed and we're allowed, process the next line */
319         if (cont != cont_orig && do_next && line->next) {
320                 txtfmt_osl_format_line(st, line->next, do_next);
321         }
322
323         flatten_string_free(&fs);
324 }
325
326 void ED_text_format_register_osl(void)
327 {
328         static TextFormatType tft = {0};
329         static const char *ext[] = {"osl", NULL};
330
331         tft.format_identifier = txtfmt_osl_format_identifier;
332         tft.format_line       = txtfmt_osl_format_line;
333         tft.ext = ext;
334
335         ED_text_format_register(&tft);
336 }