1 /*
2  *      highlighting.c - this file is part of Geany, a fast and lightweight IDE
3  *
4  *      Copyright 2005 The Geany contributors
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; either version 2 of the License, or
9  *      (at your option) any later version.
10  *
11  *      This program is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *      GNU General Public License for more details.
15  *
16  *      You should have received a copy of the GNU General Public License along
17  *      with this program; if not, write to the Free Software Foundation, Inc.,
18  *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 /**
22  * @file highlighting.h
23  * Syntax highlighting for the different filetypes, using the Scintilla lexers.
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 
30 #include "highlighting.h"
31 #include "highlightingmappings.h"
32 
33 #include "app.h"
34 #include "dialogs.h"
35 #include "document.h"
36 #include "editor.h"
37 #include "filetypesprivate.h"
38 #include "sciwrappers.h"
39 #include "support.h"
40 #include "symbols.h"
41 #include "ui_utils.h"
42 #include "utils.h"
43 
44 #include "SciLexer.h"
45 
46 #include <stdlib.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <glib.h>
50 #include <glib/gprintf.h>
51 
52 
53 #define GEANY_COLORSCHEMES_SUBDIR "colorschemes"
54 
55 /* Whitespace has to be set after setting wordchars. */
56 #define GEANY_WHITESPACE_CHARS " \t" "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"
57 
58 
59 static gchar *whitespace_chars = NULL;
60 
61 
62 typedef struct
63 {
64 	gsize			count;		/* number of styles */
65 	GeanyLexerStyle	*styling;		/* array of styles, NULL if not used or uninitialised */
66 	gchar			**keywords;
67 	gchar			*wordchars;	/* NULL used for style sets with no styles */
68 	gchar			**property_keys;
69 	gchar			**property_values;
70 } StyleSet;
71 
72 /* each filetype has a styleset but GEANY_FILETYPES_NONE uses common_style_set for styling */
73 static StyleSet *style_sets = NULL;
74 
75 
76 enum	/* Geany common styling */
77 {
78 	GCS_DEFAULT,
79 	GCS_SELECTION,
80 	GCS_BRACE_GOOD,
81 	GCS_BRACE_BAD,
82 	GCS_MARGIN_LINENUMBER,
83 	GCS_MARGIN_FOLDING,
84 	GCS_FOLD_SYMBOL_HIGHLIGHT,
85 	GCS_CURRENT_LINE,
86 	GCS_CARET,
87 	GCS_INDENT_GUIDE,
88 	GCS_WHITE_SPACE,
89 	GCS_LINE_WRAP_VISUALS,
90 	GCS_LINE_WRAP_INDENT,
91 	GCS_TRANSLUCENCY,
92 	GCS_MARKER_LINE,
93 	GCS_MARKER_SEARCH,
94 	GCS_MARKER_MARK,
95 	GCS_MARKER_TRANSLUCENCY,
96 	GCS_LINE_HEIGHT,
97 	GCS_CALLTIPS,
98 	GCS_INDICATOR_ERROR,
99 	GCS_MAX
100 };
101 
102 static struct
103 {
104 	GeanyLexerStyle	 styling[GCS_MAX];
105 
106 	/* icon style, 1-4 */
107 	gint fold_marker;
108 	/* vertical line style, 0-2 */
109 	gint fold_lines;
110 	/* horizontal line when folded, 0-2 */
111 	gint fold_draw_line;
112 
113 	gchar			*wordchars;
114 } common_style_set = { { { 0 } }, 0, 0, 0, NULL };
115 
116 
117 /* For filetypes.common [named_styles] section.
118  * 0xBBGGRR format.
119  * e.g. "comment" => &GeanyLexerStyle{0x0000d0, 0xffffff, FALSE, FALSE} */
120 static GHashTable *named_style_hash = NULL;
121 
122 /* 0xBBGGRR format, set by "default" named style. */
123 static GeanyLexerStyle gsd_default = {0x000000, 0xffffff, FALSE, FALSE};
124 
125 
126 /* filetypes should use the filetypes.foo [lexer_properties] group instead of hardcoding */
sci_set_property(ScintillaObject * sci,const gchar * name,const gchar * value)127 static void sci_set_property(ScintillaObject *sci, const gchar *name, const gchar *value)
128 {
129 	SSM(sci, SCI_SETPROPERTY, (uptr_t) name, (sptr_t) value);
130 }
131 
132 
new_styleset(guint file_type_id,gsize styling_count)133 static void new_styleset(guint file_type_id, gsize styling_count)
134 {
135 	StyleSet *set = &style_sets[file_type_id];
136 
137 	set->count = styling_count;
138 	set->styling = g_new0(GeanyLexerStyle, styling_count);
139 }
140 
141 
free_styleset(guint file_type_id)142 static void free_styleset(guint file_type_id)
143 {
144 	StyleSet *style_ptr;
145 	style_ptr = &style_sets[file_type_id];
146 
147 	style_ptr->count = 0;
148 	g_free(style_ptr->styling);
149 	style_ptr->styling = NULL;
150 	g_strfreev(style_ptr->keywords);
151 	style_ptr->keywords = NULL;
152 	g_free(style_ptr->wordchars);
153 	style_ptr->wordchars = NULL;
154 	g_strfreev(style_ptr->property_keys);
155 	style_ptr->property_keys = NULL;
156 	g_strfreev(style_ptr->property_values);
157 	style_ptr->property_values = NULL;
158 }
159 
160 
get_keyfile_keywords(GKeyFile * config,GKeyFile * configh,const gchar * key,guint ft_id,guint pos)161 static void get_keyfile_keywords(GKeyFile *config, GKeyFile *configh,
162 				const gchar *key, guint ft_id, guint pos)
163 {
164 	style_sets[ft_id].keywords[pos] =
165 		utils_get_setting(string, configh, config, "keywords", key, "");
166 }
167 
168 
get_keyfile_wordchars(GKeyFile * config,GKeyFile * configh,gchar ** wordchars,const gchar * default_wordchars)169 static void get_keyfile_wordchars(GKeyFile *config, GKeyFile *configh, gchar **wordchars,
170 		const gchar *default_wordchars)
171 {
172 	*wordchars = utils_get_setting(string, configh, config,
173 		"settings", "wordchars", default_wordchars);
174 }
175 
176 
read_named_style(const gchar * named_style,GeanyLexerStyle * style)177 static gboolean read_named_style(const gchar *named_style, GeanyLexerStyle *style)
178 {
179 	GeanyLexerStyle *cs;
180 	gchar *comma, *name = NULL;
181 	const gchar *bold = NULL;
182 	const gchar *italic = NULL;
183 
184 	g_return_val_if_fail(named_style, FALSE);
185 	name = utils_strdupa(named_style);	/* named_style must not be written to, may be a static string */
186 
187 	comma = strstr(name, ",");
188 	if (comma)
189 	{
190 		bold = strstr(comma, ",bold");
191 		italic = strstr(comma, ",italic");
192 		*comma = '\0';	/* terminate name to make lookup work */
193 	}
194 	cs = g_hash_table_lookup(named_style_hash, name);
195 
196 	if (cs)
197 	{
198  		*style = *cs;
199  		if (bold)
200  			style->bold = !style->bold;
201  		if (italic)
202  			style->italic = !style->italic;
203 	}
204 	else
205 	{
206 		*style = gsd_default;
207 	}
208 	return (cs != NULL);
209 }
210 
211 
212 /* Parses a color in `str` which can be an HTML color (ex. #0099cc),
213  * an abbreviated HTML color (ex. #09c) or a hex string color
214  * (ex. 0x0099cc). The result of the conversion is stored into the
215  * location pointed to by `clr`. */
parse_color(GKeyFile * kf,const gchar * str,gint * clr)216 static void parse_color(GKeyFile *kf, const gchar *str, gint *clr)
217 {
218 	gint c;
219 	gchar *named_color = NULL;
220 
221 	g_return_if_fail(clr != NULL);
222 
223 	if (G_UNLIKELY(EMPTY(str)))
224 		return;
225 
226 	named_color = g_key_file_get_string(kf, "named_colors", str, NULL);
227 	if (named_color)
228 		str = named_color;
229 
230 	c = utils_parse_color_to_bgr(str);
231 	if (c == -1)
232 		geany_debug("Bad color '%s'", str);
233 	else
234 		*clr = c;
235 
236 	g_free(named_color);
237 }
238 
239 
parse_keyfile_style(GKeyFile * kf,gchar ** list,const GeanyLexerStyle * default_style,GeanyLexerStyle * style)240 static void parse_keyfile_style(GKeyFile *kf, gchar **list,
241 		const GeanyLexerStyle *default_style, GeanyLexerStyle *style)
242 {
243 	gsize len;
244 
245 	g_return_if_fail(default_style);
246 	g_return_if_fail(style);
247 
248 	*style = *default_style;
249 
250 	if (!list)
251 		return;
252 
253 	len = g_strv_length(list);
254 	if (len == 0)
255 		return;
256 	else if (len == 1)
257 	{
258 		gchar **items = g_strsplit(list[0], ",", 0);
259 		if (items != NULL)
260 		{
261 			if (g_strv_length(items) > 0)
262 			{
263 				if (g_hash_table_lookup(named_style_hash, items[0]) != NULL)
264 				{
265 					if (!read_named_style(list[0], style))
266 						geany_debug("Unable to read named style '%s'", items[0]);
267 					g_strfreev(items);
268 					return;
269 				}
270 				else if (strchr(list[0], ',') != NULL)
271 				{
272 					geany_debug("Unknown named style '%s'", items[0]);
273 					g_strfreev(items);
274 					return;
275 				}
276 			}
277 			g_strfreev(items);
278 		}
279 	}
280 
281 	switch (len)
282 	{
283 		case 4:
284 			style->italic = utils_atob(list[3]);
285 			/* fall through */
286 		case 3:
287 			style->bold = utils_atob(list[2]);
288 			/* fall through */
289 		case 2:
290 			parse_color(kf, list[1], &style->background);
291 			/* fall through */
292 		case 1:
293 			parse_color(kf, list[0], &style->foreground);
294 	}
295 }
296 
297 
get_keyfile_style(GKeyFile * config,GKeyFile * configh,const gchar * key_name,GeanyLexerStyle * style)298 static void get_keyfile_style(GKeyFile *config, GKeyFile *configh,
299 		const gchar *key_name, GeanyLexerStyle *style)
300 {
301 	gchar **list;
302 	gsize len;
303 
304 	g_return_if_fail(config);
305 	g_return_if_fail(configh);
306 	g_return_if_fail(key_name);
307 	g_return_if_fail(style);
308 
309 	list = g_key_file_get_string_list(configh, "styling", key_name, &len, NULL);
310 	if (list == NULL)
311 	{
312 		list = g_key_file_get_string_list(config, "styling", key_name, &len, NULL);
313 		parse_keyfile_style(config, list, &gsd_default, style);
314 	}
315 	else
316 		parse_keyfile_style(configh, list, &gsd_default, style);
317 
318 	g_strfreev(list);
319 }
320 
321 
convert_int(const gchar * int_str,gint * val)322 static void convert_int(const gchar *int_str, gint *val)
323 {
324 	gchar *end;
325 	gint v = strtol(int_str, &end, 10);
326 
327 	if (int_str != end)
328 		*val = v;
329 }
330 
331 
332 /* Get first and second integer numbers, store in foreground and background fields of @a style. */
get_keyfile_int(GKeyFile * config,GKeyFile * configh,const gchar * section,const gchar * key,gint fdefault_val,gint sdefault_val,GeanyLexerStyle * style)333 static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *section,
334 							const gchar *key, gint fdefault_val, gint sdefault_val,
335 							GeanyLexerStyle *style)
336 {
337 	gchar **list;
338 	gsize len;
339 	GeanyLexerStyle def = {fdefault_val, sdefault_val, FALSE, FALSE};
340 
341 	g_return_if_fail(config);
342 	g_return_if_fail(configh);
343 	g_return_if_fail(section);
344 	g_return_if_fail(key);
345 
346 	list = g_key_file_get_string_list(configh, section, key, &len, NULL);
347 	if (list == NULL)
348 		list = g_key_file_get_string_list(config, section, key, &len, NULL);
349 
350 	*style = def;
351 	if (!list)
352 		return;
353 
354 	if (list[0])
355 	{
356 		convert_int(list[0], &style->foreground);
357 		if (list[1])
358 		{
359 			convert_int(list[1], &style->background);
360 		}
361 	}
362 	g_strfreev(list);
363 }
364 
365 
366 /* first or second can be NULL. */
get_keyfile_ints(GKeyFile * config,GKeyFile * configh,const gchar * section,const gchar * key,gint fdefault_val,gint sdefault_val,gint * first,gint * second)367 static void get_keyfile_ints(GKeyFile *config, GKeyFile *configh, const gchar *section,
368 							const gchar *key,
369 							gint fdefault_val, gint sdefault_val,
370 							gint *first, gint *second)
371 {
372 	GeanyLexerStyle tmp_style;
373 
374 	get_keyfile_int(config, configh, section, key, fdefault_val, sdefault_val, &tmp_style);
375 	if (first)
376 		*first = tmp_style.foreground;
377 	if (second)
378 		*second = tmp_style.background;
379 }
380 
381 
invert(guint icolour)382 static guint invert(guint icolour)
383 {
384 	if (interface_prefs.highlighting_invert_all)
385 		return 0xffffff - icolour;
386 
387 	return icolour;
388 }
389 
390 
get_style(guint ft_id,guint styling_index)391 static GeanyLexerStyle *get_style(guint ft_id, guint styling_index)
392 {
393 	g_assert(ft_id < filetypes_array->len);
394 
395 	if (G_UNLIKELY(ft_id == GEANY_FILETYPES_NONE))
396 	{
397 		g_assert(styling_index < GCS_MAX);
398 		return &common_style_set.styling[styling_index];
399 	}
400 	else
401 	{
402 		StyleSet *set = &style_sets[ft_id];
403 
404 		g_assert(styling_index < set->count);
405 		return &set->styling[styling_index];
406 	}
407 }
408 
409 
set_sci_style(ScintillaObject * sci,guint style,guint ft_id,guint styling_index)410 static void set_sci_style(ScintillaObject *sci, guint style, guint ft_id, guint styling_index)
411 {
412 	GeanyLexerStyle *style_ptr = get_style(ft_id, styling_index);
413 
414 	SSM(sci, SCI_STYLESETFORE, style,	invert(style_ptr->foreground));
415 	SSM(sci, SCI_STYLESETBACK, style,	invert(style_ptr->background));
416 	SSM(sci, SCI_STYLESETBOLD, style,	style_ptr->bold);
417 	SSM(sci, SCI_STYLESETITALIC, style,	style_ptr->italic);
418 }
419 
420 
highlighting_free_styles(void)421 void highlighting_free_styles(void)
422 {
423 	guint i;
424 
425 	for (i = 0; i < filetypes_array->len; i++)
426 		free_styleset(i);
427 
428 	if (named_style_hash)
429 		g_hash_table_destroy(named_style_hash);
430 
431 	g_free(style_sets);
432 }
433 
434 
435 static gchar*
get_keyfile_whitespace_chars(GKeyFile * config,GKeyFile * configh)436 get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh)
437 {
438 	return utils_get_setting(string, configh, config,
439 		"settings", "whitespace_chars", GEANY_WHITESPACE_CHARS);
440 }
441 
442 
add_named_style(GKeyFile * config,const gchar * key)443 static void add_named_style(GKeyFile *config, const gchar *key)
444 {
445 	const gchar group[] = "named_styles";
446 	gchar **list;
447 	gsize len;
448 
449 	list = g_key_file_get_string_list(config, group, key, &len, NULL);
450 	/* we allow a named style to reference another style above it */
451 	if (list && len >= 1)
452 	{
453 		GeanyLexerStyle *style = g_new0(GeanyLexerStyle, 1);
454 
455 		parse_keyfile_style(config, list, &gsd_default, style);
456 		g_hash_table_insert(named_style_hash, g_strdup(key), style);
457 	}
458 	g_strfreev(list);
459 }
460 
461 
get_named_styles(GKeyFile * config)462 static void get_named_styles(GKeyFile *config)
463 {
464 	const gchar group[] = "named_styles";
465 	gchar **keys = g_key_file_get_keys(config, group, NULL, NULL);
466 	gchar **ptr = keys;
467 
468 	if (!ptr)
469 		return;
470 
471 	while (1)
472 	{
473 		const gchar *key = *ptr;
474 
475 		if (!key)
476 			break;
477 
478 		/* don't replace already read default style with system one */
479 		if (!g_str_equal(key, "default"))
480 			add_named_style(config, key);
481 
482 		ptr++;
483 	}
484 	g_strfreev(keys);
485 }
486 
487 
utils_key_file_new(const gchar * filename)488 static GKeyFile *utils_key_file_new(const gchar *filename)
489 {
490 	GKeyFile *config = g_key_file_new();
491 
492 	g_key_file_load_from_file(config, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
493 	return config;
494 }
495 
496 
load_named_styles(GKeyFile * config,GKeyFile * config_home)497 static void load_named_styles(GKeyFile *config, GKeyFile *config_home)
498 {
499 	const gchar *scheme = editor_prefs.color_scheme;
500 	gboolean free_kf = FALSE;
501 
502 	if (named_style_hash)
503 		g_hash_table_destroy(named_style_hash);	/* reloading */
504 
505 	named_style_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
506 
507 	if (!EMPTY(scheme))
508 	{
509 		gchar *path, *path_home;
510 
511 		path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
512 		path_home = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
513 
514 		if (g_file_test(path, G_FILE_TEST_EXISTS) || g_file_test(path_home, G_FILE_TEST_EXISTS))
515 		{
516 			config = utils_key_file_new(path);
517 			config_home = utils_key_file_new(path_home);
518 			free_kf = TRUE;
519 		}
520 		/* if color scheme is missing, use default */
521 		g_free(path);
522 		g_free(path_home);
523 	}
524 	/* first set default to the "default" named style */
525 	add_named_style(config, "default");
526 	read_named_style("default", &gsd_default);	/* in case user overrides but not with both colors */
527 	add_named_style(config_home, "default");
528 	read_named_style("default", &gsd_default);
529 
530 	get_named_styles(config);
531 	/* home overrides any system named style */
532 	get_named_styles(config_home);
533 
534 	if (free_kf)
535 	{
536 		g_key_file_free(config);
537 		g_key_file_free(config_home);
538 	}
539 }
540 
541 
styleset_common_init(GKeyFile * config,GKeyFile * config_home)542 static void styleset_common_init(GKeyFile *config, GKeyFile *config_home)
543 {
544 	load_named_styles(config, config_home);
545 
546 	get_keyfile_style(config, config_home, "default", &common_style_set.styling[GCS_DEFAULT]);
547 	get_keyfile_style(config, config_home, "selection", &common_style_set.styling[GCS_SELECTION]);
548 	get_keyfile_style(config, config_home, "brace_good", &common_style_set.styling[GCS_BRACE_GOOD]);
549 	get_keyfile_style(config, config_home, "brace_bad", &common_style_set.styling[GCS_BRACE_BAD]);
550 	get_keyfile_style(config, config_home, "margin_linenumber", &common_style_set.styling[GCS_MARGIN_LINENUMBER]);
551 	get_keyfile_style(config, config_home, "margin_folding", &common_style_set.styling[GCS_MARGIN_FOLDING]);
552 	get_keyfile_style(config, config_home, "fold_symbol_highlight", &common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT]);
553 	get_keyfile_style(config, config_home, "current_line", &common_style_set.styling[GCS_CURRENT_LINE]);
554 	get_keyfile_style(config, config_home, "caret", &common_style_set.styling[GCS_CARET]);
555 	get_keyfile_style(config, config_home, "indent_guide", &common_style_set.styling[GCS_INDENT_GUIDE]);
556 	get_keyfile_style(config, config_home, "white_space", &common_style_set.styling[GCS_WHITE_SPACE]);
557 	get_keyfile_style(config, config_home, "marker_line", &common_style_set.styling[GCS_MARKER_LINE]);
558 	get_keyfile_style(config, config_home, "marker_search", &common_style_set.styling[GCS_MARKER_SEARCH]);
559 	get_keyfile_style(config, config_home, "marker_mark", &common_style_set.styling[GCS_MARKER_MARK]);
560 	get_keyfile_style(config, config_home, "calltips", &common_style_set.styling[GCS_CALLTIPS]);
561 	get_keyfile_style(config, config_home, "indicator_error", &common_style_set.styling[GCS_INDICATOR_ERROR]);
562 
563 	get_keyfile_ints(config, config_home, "styling", "folding_style",
564 		1, 1, &common_style_set.fold_marker, &common_style_set.fold_lines);
565 	get_keyfile_ints(config, config_home, "styling", "folding_horiz_line",
566 		2, 0, &common_style_set.fold_draw_line, NULL);
567 	get_keyfile_ints(config, config_home, "styling", "caret_width",
568 		1, 0, &common_style_set.styling[GCS_CARET].background, NULL); /* caret.foreground used earlier */
569 	get_keyfile_int(config, config_home, "styling", "line_wrap_visuals",
570 		3, 0, &common_style_set.styling[GCS_LINE_WRAP_VISUALS]);
571 	get_keyfile_int(config, config_home, "styling", "line_wrap_indent",
572 		0, 0, &common_style_set.styling[GCS_LINE_WRAP_INDENT]);
573 	get_keyfile_int(config, config_home, "styling", "translucency",
574 		256, 256, &common_style_set.styling[GCS_TRANSLUCENCY]);
575 	get_keyfile_int(config, config_home, "styling", "marker_translucency",
576 		256, 256, &common_style_set.styling[GCS_MARKER_TRANSLUCENCY]);
577 	get_keyfile_int(config, config_home, "styling", "line_height",
578 		0, 0, &common_style_set.styling[GCS_LINE_HEIGHT]);
579 
580 	g_free(common_style_set.wordchars);
581 	get_keyfile_wordchars(config, config_home, &common_style_set.wordchars, GEANY_WORDCHARS);
582 	g_free(whitespace_chars);
583 	whitespace_chars = get_keyfile_whitespace_chars(config, config_home);
584 }
585 
586 
set_character_classes(ScintillaObject * sci,guint ft_id)587 static void set_character_classes(ScintillaObject *sci, guint ft_id)
588 {
589 	const gchar *word = (ft_id == GEANY_FILETYPES_NONE ?
590 		common_style_set.wordchars : style_sets[ft_id].wordchars);
591 	gchar *whitespace;
592 	guint i, j;
593 
594 	SSM(sci, SCI_SETWORDCHARS, 0, (sptr_t) word);
595 
596 	/* setting wordchars resets character classes, so we have to set whitespaces after
597 	 * wordchars, but we want wordchars to have precenence over whitepace chars */
598 	whitespace = g_malloc0(strlen(whitespace_chars) + 1);
599 	for (i = 0, j = 0; whitespace_chars[i] != 0; i++)
600 	{
601 		if (! strchr(word, whitespace_chars[i]))
602 			whitespace[j++] = whitespace_chars[i];
603 	}
604 	whitespace[j] = 0;
605 
606 	SSM(sci, SCI_SETWHITESPACECHARS, 0, (sptr_t) whitespace);
607 
608 	g_free(whitespace);
609 }
610 
611 
styleset_common(ScintillaObject * sci,guint ft_id)612 static void styleset_common(ScintillaObject *sci, guint ft_id)
613 {
614 	GeanyLexerStyle *style;
615 
616 	SSM(sci, SCI_STYLECLEARALL, 0, 0);
617 
618 	set_character_classes(sci, ft_id);
619 
620 	/* caret colour, style and width */
621 	SSM(sci, SCI_SETCARETFORE, invert(common_style_set.styling[GCS_CARET].foreground), 0);
622 	SSM(sci, SCI_SETCARETWIDTH, common_style_set.styling[GCS_CARET].background, 0);
623 	if (common_style_set.styling[GCS_CARET].bold)
624 		SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_BLOCK, 0);
625 	else
626 		SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_LINE, 0);
627 
628 	/* line height */
629 	SSM(sci, SCI_SETEXTRAASCENT, common_style_set.styling[GCS_LINE_HEIGHT].foreground, 0);
630 	SSM(sci, SCI_SETEXTRADESCENT, common_style_set.styling[GCS_LINE_HEIGHT].background, 0);
631 
632 	/* colourise the current line */
633 	SSM(sci, SCI_SETCARETLINEBACK, invert(common_style_set.styling[GCS_CURRENT_LINE].background), 0);
634 	/* bold=enable current line */
635 	SSM(sci, SCI_SETCARETLINEVISIBLE, common_style_set.styling[GCS_CURRENT_LINE].bold, 0);
636 
637 	/* Translucency for current line and selection */
638 	SSM(sci, SCI_SETCARETLINEBACKALPHA, common_style_set.styling[GCS_TRANSLUCENCY].foreground, 0);
639 	SSM(sci, SCI_SETSELALPHA, common_style_set.styling[GCS_TRANSLUCENCY].background, 0);
640 
641 	/* line wrapping visuals */
642 	SSM(sci, SCI_SETWRAPVISUALFLAGS,
643 		common_style_set.styling[GCS_LINE_WRAP_VISUALS].foreground, 0);
644 	SSM(sci, SCI_SETWRAPVISUALFLAGSLOCATION,
645 		common_style_set.styling[GCS_LINE_WRAP_VISUALS].background, 0);
646 	SSM(sci, SCI_SETWRAPSTARTINDENT, common_style_set.styling[GCS_LINE_WRAP_INDENT].foreground, 0);
647 	SSM(sci, SCI_SETWRAPINDENTMODE, common_style_set.styling[GCS_LINE_WRAP_INDENT].background, 0);
648 
649 	/* Error indicator */
650 	SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_ERROR, INDIC_SQUIGGLEPIXMAP);
651 	SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_ERROR,
652 		invert(common_style_set.styling[GCS_INDICATOR_ERROR].foreground));
653 
654 	/* Search indicator, used for 'Mark' matches */
655 	SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_SEARCH, INDIC_ROUNDBOX);
656 	SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_SEARCH,
657 		invert(common_style_set.styling[GCS_MARKER_SEARCH].background));
658 	SSM(sci, SCI_INDICSETALPHA, GEANY_INDICATOR_SEARCH, 60);
659 
660 	/* Snippet cursor indicator, when inserting snippets with multiple
661 	 * cursor positions. */
662 	SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_SNIPPET, INDIC_DOTBOX);
663 	SSM(sci, SCI_INDICSETALPHA, GEANY_INDICATOR_SNIPPET, 60);
664 
665 	/* define marker symbols
666 	 * 0 -> line marker */
667 	SSM(sci, SCI_MARKERDEFINE, 0, SC_MARK_SHORTARROW);
668 	SSM(sci, SCI_MARKERSETFORE, 0, invert(common_style_set.styling[GCS_MARKER_LINE].foreground));
669 	SSM(sci, SCI_MARKERSETBACK, 0, invert(common_style_set.styling[GCS_MARKER_LINE].background));
670 	SSM(sci, SCI_MARKERSETALPHA, 0, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].foreground);
671 
672 	/* 1 -> user marker */
673 	SSM(sci, SCI_MARKERDEFINE, 1, SC_MARK_PLUS);
674 	SSM(sci, SCI_MARKERSETFORE, 1, invert(common_style_set.styling[GCS_MARKER_MARK].foreground));
675 	SSM(sci, SCI_MARKERSETBACK, 1, invert(common_style_set.styling[GCS_MARKER_MARK].background));
676 	SSM(sci, SCI_MARKERSETALPHA, 1, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].background);
677 
678 	/* 2 -> folding marker, other folding settings */
679 	SSM(sci, SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);
680 	SSM(sci, SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
681 
682 	/* drawing a horizontal line when text if folded */
683 	switch (common_style_set.fold_draw_line)
684 	{
685 		case 1:
686 		{
687 			SSM(sci, SCI_SETFOLDFLAGS, 4, 0);
688 			break;
689 		}
690 		case 2:
691 		{
692 			SSM(sci, SCI_SETFOLDFLAGS, 16, 0);
693 			break;
694 		}
695 		default:
696 		{
697 			SSM(sci, SCI_SETFOLDFLAGS, 0, 0);
698 			break;
699 		}
700 	}
701 
702 	/* choose the folding style - boxes or circles, I prefer boxes, so it is default ;-) */
703 	SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY);
704 	SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY);
705 	switch (common_style_set.fold_marker)
706 	{
707 		case 2:
708 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS);
709 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS);
710 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED);
711 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
712 			break;
713 		default:
714 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS);
715 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS);
716 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED);
717 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);
718 			break;
719 		case 3:
720 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN);
721 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW);
722 			break;
723 		case 4:
724 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_MINUS);
725 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PLUS);
726 			break;
727 	}
728 
729 	/* choose the folding style - straight or curved, I prefer straight, so it is default ;-) */
730 	switch (common_style_set.fold_lines)
731 	{
732 		case 2:
733 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
734 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);
735 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
736 			break;
737 		default:
738 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);
739 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER);
740 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
741 			break;
742 		case 0:
743 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY);
744 			SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY);
745 			SSM(sci, SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY);
746 			break;
747 	}
748 	{
749 		gint markers[] = {
750 			SC_MARKNUM_FOLDEROPEN,
751 			SC_MARKNUM_FOLDER,
752 			SC_MARKNUM_FOLDERSUB,
753 			SC_MARKNUM_FOLDERTAIL,
754 			SC_MARKNUM_FOLDEREND,
755 			SC_MARKNUM_FOLDEROPENMID,
756 			SC_MARKNUM_FOLDERMIDTAIL
757 		};
758 		guint i;
759 
760 		foreach_range(i, G_N_ELEMENTS(markers))
761 		{
762 			SSM(sci, SCI_MARKERSETFORE, markers[i],
763 				invert(common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT].foreground));
764 			SSM(sci, SCI_MARKERSETBACK, markers[i],
765 				invert(common_style_set.styling[GCS_MARGIN_FOLDING].foreground));
766 		}
767 	}
768 
769 	/* set some common defaults */
770 	sci_set_property(sci, "fold", "1");
771 	sci_set_property(sci, "fold.compact", "0");
772 	sci_set_property(sci, "fold.comment", "1");
773 	sci_set_property(sci, "fold.preprocessor", "1");
774 	sci_set_property(sci, "fold.at.else", "1");
775 
776 	style = &common_style_set.styling[GCS_SELECTION];
777 	if (!style->bold && !style->italic)
778 	{
779 		geany_debug("selection style is set to invisible - ignoring!");
780 		style->italic = TRUE;
781 		style->background = 0xc0c0c0;
782 	}
783 	/* bold (3rd argument) is whether to override default foreground selection */
784 	SSM(sci, SCI_SETSELFORE, style->bold, invert(style->foreground));
785 	/* italic (4th argument) is whether to override default background selection */
786 	SSM(sci, SCI_SETSELBACK, style->italic, invert(style->background));
787 
788 	SSM(sci, SCI_SETFOLDMARGINCOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
789 	SSM(sci, SCI_SETFOLDMARGINHICOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
790 	set_sci_style(sci, STYLE_LINENUMBER, GEANY_FILETYPES_NONE, GCS_MARGIN_LINENUMBER);
791 	set_sci_style(sci, STYLE_BRACELIGHT, GEANY_FILETYPES_NONE, GCS_BRACE_GOOD);
792 	set_sci_style(sci, STYLE_BRACEBAD, GEANY_FILETYPES_NONE, GCS_BRACE_BAD);
793 	set_sci_style(sci, STYLE_INDENTGUIDE, GEANY_FILETYPES_NONE, GCS_INDENT_GUIDE);
794 
795 	/* bold = common whitespace settings enabled */
796 	SSM(sci, SCI_SETWHITESPACEFORE, common_style_set.styling[GCS_WHITE_SPACE].bold,
797 		invert(common_style_set.styling[GCS_WHITE_SPACE].foreground));
798 	SSM(sci, SCI_SETWHITESPACEBACK, common_style_set.styling[GCS_WHITE_SPACE].italic,
799 		invert(common_style_set.styling[GCS_WHITE_SPACE].background));
800 
801 	if (common_style_set.styling[GCS_CALLTIPS].bold)
802 		SSM(sci, SCI_CALLTIPSETFORE, invert(common_style_set.styling[GCS_CALLTIPS].foreground), 1);
803 	if (common_style_set.styling[GCS_CALLTIPS].italic)
804 		SSM(sci, SCI_CALLTIPSETBACK, invert(common_style_set.styling[GCS_CALLTIPS].background), 1);
805 }
806 
807 
808 /* Merge & assign global typedefs and user secondary keywords.
809  * keyword_idx is used for both style_sets[].keywords and scintilla keyword style number */
merge_type_keywords(ScintillaObject * sci,guint ft_id,guint keyword_idx)810 static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword_idx)
811 {
812 	const gchar *user_words = style_sets[ft_id].keywords[keyword_idx];
813 	GString *s;
814 
815 	s = symbols_find_typenames_as_string(filetypes[ft_id]->lang, TRUE);
816 	if (G_UNLIKELY(s == NULL))
817 		s = g_string_sized_new(200);
818 	else
819 		g_string_append_c(s, ' '); /* append a space as delimiter to the existing list of words */
820 
821 	g_string_append(s, user_words);
822 
823 	sci_set_keywords(sci, keyword_idx, s->str);
824 	g_string_free(s, TRUE);
825 }
826 
827 
styleset_init_from_mapping(guint ft_id,GKeyFile * config,GKeyFile * config_home,const HLStyle * styles,gsize n_styles,const HLKeyword * keywords,gsize n_keywords)828 static void styleset_init_from_mapping(guint ft_id, GKeyFile *config, GKeyFile *config_home,
829 		const HLStyle *styles, gsize n_styles,
830 		const HLKeyword *keywords, gsize n_keywords)
831 {
832 	gsize i;
833 
834 	/* styles */
835 	new_styleset(ft_id, n_styles);
836 	foreach_range(i, n_styles)
837 	{
838 		GeanyLexerStyle *style = &style_sets[ft_id].styling[i];
839 
840 		get_keyfile_style(config, config_home, styles[i].name, style);
841 	}
842 
843 	/* keywords */
844 	if (n_keywords < 1)
845 		style_sets[ft_id].keywords = NULL;
846 	else
847 	{
848 		style_sets[ft_id].keywords = g_new(gchar*, n_keywords + 1);
849 		foreach_range(i, n_keywords)
850 			get_keyfile_keywords(config, config_home, keywords[i].key, ft_id, i);
851 		style_sets[ft_id].keywords[i] = NULL;
852 	}
853 }
854 
855 
856 /* STYLE_DEFAULT will be set to match the first style. */
styleset_from_mapping(ScintillaObject * sci,guint ft_id,guint lexer,const HLStyle * styles,gsize n_styles,const HLKeyword * keywords,gsize n_keywords,const HLProperty * properties,gsize n_properties)857 static void styleset_from_mapping(ScintillaObject *sci, guint ft_id, guint lexer,
858 		const HLStyle *styles, gsize n_styles,
859 		const HLKeyword *keywords, gsize n_keywords,
860 		const HLProperty *properties, gsize n_properties)
861 {
862 	gsize i;
863 
864 	g_assert(ft_id != GEANY_FILETYPES_NONE);
865 
866 	/* lexer */
867 	sci_set_lexer(sci, lexer);
868 
869 	/* styles */
870 	styleset_common(sci, ft_id);
871 	if (n_styles > 0)
872 	{
873 		/* first style is also default one */
874 		set_sci_style(sci, STYLE_DEFAULT, ft_id, 0);
875 		foreach_range(i, n_styles)
876 		{
877 			if (styles[i].fill_eol)
878 				SSM(sci, SCI_STYLESETEOLFILLED, styles[i].style, TRUE);
879 			set_sci_style(sci, styles[i].style, ft_id, i);
880 		}
881 	}
882 
883 	/* keywords */
884 	foreach_range(i, n_keywords)
885 	{
886 		if (keywords[i].merge)
887 			merge_type_keywords(sci, ft_id, i);
888 		else
889 			sci_set_keywords(sci, keywords[i].id, style_sets[ft_id].keywords[i]);
890 	}
891 
892 	/* properties */
893 	foreach_range(i, n_properties)
894 		sci_set_property(sci, properties[i].property, properties[i].value);
895 }
896 
897 
898 
styleset_default(ScintillaObject * sci,guint ft_id)899 static void styleset_default(ScintillaObject *sci, guint ft_id)
900 {
901 	sci_set_lexer(sci, SCLEX_NULL);
902 
903 	/* we need to set STYLE_DEFAULT before we call SCI_STYLECLEARALL in styleset_common() */
904 	set_sci_style(sci, STYLE_DEFAULT, GEANY_FILETYPES_NONE, GCS_DEFAULT);
905 
906 	styleset_common(sci, ft_id);
907 }
908 
909 
get_key_values(GKeyFile * config,const gchar * group,gchar ** keys,gchar ** values)910 static void get_key_values(GKeyFile *config, const gchar *group, gchar **keys, gchar **values)
911 {
912 	while (*keys)
913 	{
914 		gchar *str = g_key_file_get_string(config, group, *keys, NULL);
915 
916 		if (str)
917 			SETPTR(*values, str);
918 
919 		keys++;
920 		values++;
921 	}
922 }
923 
924 
read_properties(GeanyFiletype * ft,GKeyFile * config,GKeyFile * configh)925 static void read_properties(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
926 {
927 	gchar group[] = "lexer_properties";
928 	gchar **keys;
929 	gchar **keysh = g_key_file_get_keys(configh, group, NULL, NULL);
930 	gchar **ptr;
931 
932 	/* remove overridden keys from system keyfile */
933 	foreach_strv(ptr, keysh)
934 		g_key_file_remove_key(config, group, *ptr, NULL);
935 
936 	/* merge sys and user keys */
937 	keys = g_key_file_get_keys(config, group, NULL, NULL);
938 	keys = utils_strv_join(keys, keysh);
939 
940 	if (keys)
941 	{
942 		gchar **values = g_new0(gchar*, g_strv_length(keys) + 1);
943 
944 		style_sets[ft->id].property_keys = keys;
945 		style_sets[ft->id].property_values = values;
946 
947 		get_key_values(config, group, keys, values);
948 		get_key_values(configh, group, keys, values);
949 	}
950 }
951 
952 
get_lexer_filetype(GeanyFiletype * ft)953 static guint get_lexer_filetype(GeanyFiletype *ft)
954 {
955 	ft = FALLBACK(ft->lexer_filetype, ft);
956 	return ft->id;
957 }
958 
959 
960 #define init_styleset_case(LANG_NAME) \
961 	case (GEANY_FILETYPES_##LANG_NAME): \
962 		styleset_init_from_mapping(filetype_idx, config, configh, \
963 				highlighting_styles_##LANG_NAME, \
964 				HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
965 				highlighting_keywords_##LANG_NAME, \
966 				HL_N_ENTRIES(highlighting_keywords_##LANG_NAME)); \
967 		break
968 
969 /* Called by filetypes_load_config(). */
highlighting_init_styles(guint filetype_idx,GKeyFile * config,GKeyFile * configh)970 void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *configh)
971 {
972 	GeanyFiletype *ft = filetypes[filetype_idx];
973 	guint lexer_id = get_lexer_filetype(ft);
974 	gchar *default_str;
975 
976 	if (!style_sets)
977 		style_sets = g_new0(StyleSet, filetypes_array->len);
978 
979 	/* Clear old information if necessary - e.g. when reloading config */
980 	free_styleset(filetype_idx);
981 
982 	read_properties(ft, config, configh);
983 	/* If a default style exists, check it uses a named style
984 	 * Note: almost all filetypes have a "default" style, except HTML ones */
985 	default_str = utils_get_setting(string, configh, config,
986 		"styling", "default", "default");
987 	ft->priv->warn_color_scheme = !g_ascii_isalpha(*default_str);
988 	g_free(default_str);
989 
990 	/* None filetype handled specially */
991 	if (filetype_idx == GEANY_FILETYPES_NONE)
992 	{
993 		styleset_common_init(config, configh);
994 		return;
995 	}
996 	/* All stylesets depend on filetypes.common */
997 	filetypes_load_config(GEANY_FILETYPES_NONE, FALSE);
998 
999 	switch (lexer_id)
1000 	{
1001 		init_styleset_case(ABAQUS);
1002 		init_styleset_case(ADA);
1003 		init_styleset_case(ASM);
1004 		init_styleset_case(BASIC);
1005 		init_styleset_case(BATCH);
1006 		init_styleset_case(C);
1007 		init_styleset_case(CAML);
1008 		init_styleset_case(CMAKE);
1009 		init_styleset_case(COBOL);
1010 		init_styleset_case(COFFEESCRIPT);
1011 		init_styleset_case(CONF);
1012 		init_styleset_case(CSS);
1013 		init_styleset_case(D);
1014 		init_styleset_case(DIFF);
1015 		init_styleset_case(LISP);
1016 		init_styleset_case(ERLANG);
1017 		init_styleset_case(DOCBOOK);
1018 		init_styleset_case(FERITE);
1019 		init_styleset_case(F77);
1020 		init_styleset_case(FORTH);
1021 		init_styleset_case(FORTRAN);
1022 		init_styleset_case(GO);
1023 		init_styleset_case(HASKELL);
1024 		init_styleset_case(HAXE);
1025 		init_styleset_case(AS);
1026 		init_styleset_case(HTML);
1027 		init_styleset_case(JAVA);
1028 		init_styleset_case(JS);
1029 		init_styleset_case(LATEX);
1030 		init_styleset_case(LUA);
1031 		init_styleset_case(MAKE);
1032 		init_styleset_case(MATLAB);
1033 		init_styleset_case(MARKDOWN);
1034 		init_styleset_case(NSIS);
1035 		init_styleset_case(OBJECTIVEC);
1036 		init_styleset_case(PASCAL);
1037 		init_styleset_case(PERL);
1038 		init_styleset_case(PHP);
1039 		init_styleset_case(PO);
1040 		init_styleset_case(POWERSHELL);
1041 		init_styleset_case(PYTHON);
1042 		init_styleset_case(R);
1043 		init_styleset_case(RUBY);
1044 		init_styleset_case(RUST);
1045 		init_styleset_case(SH);
1046 		init_styleset_case(SMALLTALK);
1047 		init_styleset_case(SQL);
1048 		init_styleset_case(TCL);
1049 		init_styleset_case(TXT2TAGS);
1050 		init_styleset_case(VHDL);
1051 		init_styleset_case(VERILOG);
1052 		init_styleset_case(XML);
1053 		init_styleset_case(YAML);
1054 		init_styleset_case(ZEPHIR);
1055 		default:
1056 			if (ft->lexer_filetype)
1057 				geany_debug("Filetype %s has a recursive lexer_filetype %s set!",
1058 					ft->name, ft->lexer_filetype->name);
1059 	}
1060 
1061 	/* should be done in filetypes.c really: */
1062 	get_keyfile_wordchars(config, configh, &style_sets[filetype_idx].wordchars,
1063 			common_style_set.wordchars);
1064 }
1065 
1066 
1067 #define styleset_case(LANG_NAME) \
1068 	case (GEANY_FILETYPES_##LANG_NAME): \
1069 		styleset_from_mapping(sci, ft->id, highlighting_lexer_##LANG_NAME, \
1070 				highlighting_styles_##LANG_NAME, \
1071 				HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
1072 				highlighting_keywords_##LANG_NAME, \
1073 				HL_N_ENTRIES(highlighting_keywords_##LANG_NAME), \
1074 				highlighting_properties_##LANG_NAME, \
1075 				HL_N_ENTRIES(highlighting_properties_##LANG_NAME)); \
1076 		break
1077 
1078 /** Sets up highlighting and other visual settings.
1079  * @param sci Scintilla widget.
1080  * @param ft Filetype settings to use. */
1081 GEANY_API_SYMBOL
highlighting_set_styles(ScintillaObject * sci,GeanyFiletype * ft)1082 void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft)
1083 {
1084 	guint lexer_id = get_lexer_filetype(ft);
1085 
1086 	filetypes_load_config(ft->id, FALSE);	/* load filetypes.ext */
1087 
1088 	switch (lexer_id)
1089 	{
1090 		styleset_case(ABAQUS);
1091 		styleset_case(ADA);
1092 		styleset_case(ASM);
1093 		styleset_case(BASIC);
1094 		styleset_case(BATCH);
1095 		styleset_case(C);
1096 		styleset_case(CAML);
1097 		styleset_case(CMAKE);
1098 		styleset_case(COBOL);
1099 		styleset_case(COFFEESCRIPT);
1100 		styleset_case(CONF);
1101 		styleset_case(CSS);
1102 		styleset_case(D);
1103 		styleset_case(DIFF);
1104 		styleset_case(LISP);
1105 		styleset_case(ERLANG);
1106 		styleset_case(DOCBOOK);
1107 		styleset_case(FERITE);
1108 		styleset_case(F77);
1109 		styleset_case(FORTH);
1110 		styleset_case(FORTRAN);
1111 		styleset_case(GO);
1112 		styleset_case(HASKELL);
1113 		styleset_case(HAXE);
1114 		styleset_case(AS);
1115 		styleset_case(HTML);
1116 		styleset_case(JAVA);
1117 		styleset_case(JS);
1118 		styleset_case(LATEX);
1119 		styleset_case(LUA);
1120 		styleset_case(MAKE);
1121 		styleset_case(MARKDOWN);
1122 		styleset_case(MATLAB);
1123 		styleset_case(NSIS);
1124 		styleset_case(OBJECTIVEC);
1125 		styleset_case(PASCAL);
1126 		styleset_case(PERL);
1127 		styleset_case(PHP);
1128 		styleset_case(PO);
1129 		styleset_case(POWERSHELL);
1130 		styleset_case(PYTHON);
1131 		styleset_case(R);
1132 		styleset_case(RUBY);
1133 		styleset_case(RUST);
1134 		styleset_case(SH);
1135 		styleset_case(SMALLTALK);
1136 		styleset_case(SQL);
1137 		styleset_case(TCL);
1138 		styleset_case(TXT2TAGS);
1139 		styleset_case(VHDL);
1140 		styleset_case(VERILOG);
1141 		styleset_case(XML);
1142 		styleset_case(YAML);
1143 		styleset_case(ZEPHIR);
1144 		case GEANY_FILETYPES_NONE:
1145 		default:
1146 			styleset_default(sci, ft->id);
1147 	}
1148 	/* [lexer_properties] settings */
1149 	if (style_sets[ft->id].property_keys)
1150 	{
1151 		gchar **prop = style_sets[ft->id].property_keys;
1152 		gchar **val = style_sets[ft->id].property_values;
1153 
1154 		while (*prop)
1155 		{
1156 			sci_set_property(sci, *prop, *val);
1157 			prop++;
1158 			val++;
1159 		}
1160 	}
1161 }
1162 
1163 
1164 /** Retrieves a style @a style_id for the filetype @a ft_id.
1165  * If the style was not already initialised
1166  * (e.g. by by opening a file of this type), it will be initialised. The returned pointer is
1167  * owned by Geany and must not be freed.
1168  * @param ft_id Filetype ID, e.g. @c GEANY_FILETYPES_DIFF.
1169  * @param style_id A Scintilla lexer style, e.g. @c SCE_DIFF_ADDED. See scintilla/include/SciLexer.h.
1170  * @return A pointer to the style struct.
1171  * @see Scintilla messages @c SCI_STYLEGETFORE, etc, for use with scintilla_send_message(). */
1172 GEANY_API_SYMBOL
highlighting_get_style(gint ft_id,gint style_id)1173 const GeanyLexerStyle *highlighting_get_style(gint ft_id, gint style_id)
1174 {
1175 	g_return_val_if_fail(ft_id >= 0 && (guint) ft_id < filetypes_array->len, NULL);
1176 	g_return_val_if_fail(style_id >= 0, NULL);
1177 
1178 	/* ensure filetype loaded */
1179 	filetypes_load_config((guint) ft_id, FALSE);
1180 
1181 	/* TODO: style_id might not be the real array index (Scintilla styles are not always synced
1182 	 * with array indices) */
1183 	return get_style((guint) ft_id, (guint) style_id);
1184 }
1185 
1186 
1187 enum
1188 {
1189 	SCHEME_MARKUP,
1190 	SCHEME_FILE,
1191 	SCHEME_COLUMNS
1192 };
1193 
on_color_scheme_changed(GtkTreeSelection * treesel,gpointer dummy)1194 static void on_color_scheme_changed(GtkTreeSelection *treesel, gpointer dummy)
1195 {
1196 	GtkTreeModel *model;
1197 	GtkTreeIter iter;
1198 	gchar *fname;
1199 	gchar *path;
1200 
1201 	if (!gtk_tree_selection_get_selected(treesel, &model, &iter))
1202 		return;
1203 	gtk_tree_model_get(model, &iter, SCHEME_FILE, &fname, -1);
1204 
1205 	/* check if default item */
1206 	if (!fname)
1207 	{
1208 		SETPTR(editor_prefs.color_scheme, NULL);
1209 		filetypes_reload();
1210 		return;
1211 	}
1212 	SETPTR(fname, utils_get_locale_from_utf8(fname));
1213 
1214 	/* fname is just the basename from the menu item, so prepend the custom files path */
1215 	path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1216 	if (!g_file_test(path, G_FILE_TEST_EXISTS))
1217 	{
1218 		/* try the system path */
1219 		g_free(path);
1220 		path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1221 	}
1222 	if (g_file_test(path, G_FILE_TEST_EXISTS))
1223 	{
1224 		SETPTR(editor_prefs.color_scheme, fname);
1225 		fname = NULL;
1226 		filetypes_reload();
1227 	}
1228 	else
1229 	{
1230 		SETPTR(fname, utils_get_utf8_from_locale(fname));
1231 		ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname);
1232 	}
1233 	g_free(path);
1234 	g_free(fname);
1235 }
1236 
1237 
utils_get_setting_locale_string(GKeyFile * keyfile,const gchar * group,const gchar * key,const gchar * default_value)1238 static gchar *utils_get_setting_locale_string(GKeyFile *keyfile,
1239 		const gchar *group, const gchar *key, const gchar *default_value)
1240 {
1241 	gchar *result = g_key_file_get_locale_string(keyfile, group, key, NULL, NULL);
1242 
1243 	return FALLBACK(result, g_strdup(default_value));
1244 }
1245 
1246 
add_color_scheme_item(GtkListStore * store,gchar * name,gchar * desc,const gchar * fn,GtkTreeIter * current_iter)1247 static void add_color_scheme_item(GtkListStore *store,
1248 	gchar *name, gchar *desc, const gchar *fn, GtkTreeIter *current_iter)
1249 {
1250 	GtkTreeIter iter;
1251 	gchar *markup;
1252 
1253 	/* reuse parameters */
1254 	name = g_markup_escape_text(name, -1);
1255 	desc = g_markup_escape_text(desc, -1);
1256 	markup = g_strdup_printf("<big><b>%s</b></big>\n%s", name, desc);
1257 	g_free(name);
1258 	g_free(desc);
1259 
1260 	gtk_list_store_append(store, &iter);
1261 	gtk_list_store_set(store, &iter, SCHEME_MARKUP, markup,
1262 		SCHEME_FILE, fn, -1);
1263 	g_free(markup);
1264 
1265 	/* select the current iter if the the color scheme matches, or if it's the
1266 	 * default (fn == NULL), in case of bad config file.  the default theme is
1267 	 * first anyway so if a later scheme matches it will override default */
1268 	if ((! fn || utils_str_equal(fn, editor_prefs.color_scheme)) && current_iter)
1269 		*current_iter = iter;
1270 }
1271 
1272 
add_color_scheme_file(GtkListStore * store,const gchar * fname,GtkTreeIter * current_iter)1273 static void add_color_scheme_file(GtkListStore *store, const gchar *fname, GtkTreeIter *current_iter)
1274 {
1275 	GKeyFile *hkeyfile, *skeyfile;
1276 	gchar *path, *theme_name, *theme_desc;
1277 	gchar *theme_fn = utils_get_utf8_from_locale(fname);
1278 
1279 	path = g_build_filename(app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1280 	hkeyfile = utils_key_file_new(path);
1281 	SETPTR(path, g_build_filename(app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL));
1282 	skeyfile = utils_key_file_new(path);
1283 
1284 	theme_name = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "name", theme_fn);
1285 	theme_desc = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "description", NULL);
1286 	add_color_scheme_item(store, theme_name, theme_desc, theme_fn, current_iter);
1287 
1288 	g_free(path);
1289 	g_free(theme_fn);
1290 	g_free(theme_name);
1291 	g_free(theme_desc);
1292 	g_key_file_free(hkeyfile);
1293 	g_key_file_free(skeyfile);
1294 }
1295 
1296 
add_color_scheme_items(GtkListStore * store,GtkTreeIter * current_iter)1297 static gboolean add_color_scheme_items(GtkListStore *store, GtkTreeIter *current_iter)
1298 {
1299 	GSList *list, *node;
1300 
1301 	add_color_scheme_item(store, _("Default"), _("Default"), NULL, current_iter);
1302 	list = utils_get_config_files(GEANY_COLORSCHEMES_SUBDIR);
1303 
1304 	foreach_slist(node, list)
1305 	{
1306 		gchar *fname = node->data;
1307 
1308 		if (g_str_has_suffix(fname, ".conf"))
1309 			add_color_scheme_file(store, fname, current_iter);
1310 
1311 		g_free(fname);
1312 	}
1313 	g_slist_free(list);
1314 	return list != NULL;
1315 }
1316 
1317 
on_color_scheme_dialog_response(GtkWidget * dialog,gint response,gpointer * dialog_ptr)1318 static void on_color_scheme_dialog_response(GtkWidget *dialog,
1319 	gint response, gpointer *dialog_ptr)
1320 {
1321 	*dialog_ptr = NULL;
1322 	gtk_widget_destroy(dialog);
1323 }
1324 
1325 
highlighting_show_color_scheme_dialog(void)1326 void highlighting_show_color_scheme_dialog(void)
1327 {
1328 	static GtkWidget *dialog = NULL;
1329 	GtkListStore *store = gtk_list_store_new(SCHEME_COLUMNS,
1330 		G_TYPE_STRING, G_TYPE_STRING);
1331 	GtkCellRenderer *text_renderer;
1332 	GtkTreeViewColumn *column;
1333 	GtkTreeSelection *treesel;
1334 	GtkTreeIter current_iter;
1335 	GtkTreePath *path;
1336 	GtkWidget *vbox, *swin, *tree;
1337 	GeanyDocument *doc;
1338 
1339 	doc = document_get_current();
1340 	if (doc && doc->file_type->priv->warn_color_scheme)
1341 		dialogs_show_msgbox_with_secondary(GTK_MESSAGE_WARNING,
1342 			_("The current filetype overrides the default style."),
1343 			_("This may cause color schemes to display incorrectly."));
1344 
1345 	tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1346 	g_object_unref(store);
1347 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
1348 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
1349 
1350 	text_renderer = gtk_cell_renderer_text_new();
1351 	g_object_set(text_renderer, "wrap-mode", PANGO_WRAP_WORD, NULL);
1352 	column = gtk_tree_view_column_new_with_attributes(
1353 		NULL, text_renderer, "markup", SCHEME_MARKUP, NULL);
1354 	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
1355 
1356 	add_color_scheme_items(store, &current_iter);
1357 
1358 	treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1359 	gtk_tree_selection_select_iter(treesel, &current_iter);
1360 	path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &current_iter);
1361 	gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree), path, NULL, FALSE, 0, 0);
1362 	gtk_tree_path_free(path);
1363 	g_signal_connect(treesel, "changed", G_CALLBACK(on_color_scheme_changed), NULL);
1364 
1365 	/* old dialog may still be showing */
1366 	if (dialog)
1367 		gtk_widget_destroy(dialog);
1368 	dialog = gtk_dialog_new_with_buttons(_("Color Schemes"),
1369 		GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
1370 		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
1371 	vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1372 	gtk_box_set_spacing(GTK_BOX(vbox), 6);
1373 	gtk_widget_set_name(dialog, "GeanyDialog");
1374 	gtk_window_set_default_size(GTK_WINDOW(dialog),
1375 		GEANY_DEFAULT_DIALOG_HEIGHT * 7/4, GEANY_DEFAULT_DIALOG_HEIGHT);
1376 
1377 	swin = gtk_scrolled_window_new(NULL, NULL);
1378 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin), GTK_SHADOW_IN);
1379 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
1380 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1381 	gtk_container_add(GTK_CONTAINER(swin), tree);
1382 	gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0);
1383 	g_signal_connect(dialog, "response", G_CALLBACK(on_color_scheme_dialog_response), &dialog);
1384 	gtk_widget_show_all(dialog);
1385 }
1386 
1387 
1388 /** Checks whether the given style is a string for the given lexer.
1389  *
1390  * @param lexer Scintilla lexer type (@c SCLEX_*).
1391  * @param style Scintilla style (@c SCE_*).
1392  *
1393  * @return @c TRUE if the style is a string, @c FALSE otherwise.
1394  */
1395 GEANY_API_SYMBOL
highlighting_is_string_style(gint lexer,gint style)1396 gboolean highlighting_is_string_style(gint lexer, gint style)
1397 {
1398 	/* Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char. */
1399 
1400 	switch (lexer)
1401 	{
1402 		case SCLEX_CPP:
1403 			return (style == SCE_C_CHARACTER ||
1404 				style == SCE_C_STRING ||
1405 				style == SCE_C_STRINGEOL ||
1406 				style == SCE_C_STRINGRAW ||
1407 				style == SCE_C_VERBATIM ||
1408 				style == SCE_C_USERLITERAL ||
1409 				style == SCE_C_TRIPLEVERBATIM ||
1410 				style == SCE_C_REGEX ||
1411 				style == SCE_C_HASHQUOTEDSTRING ||
1412 				style == SCE_C_ESCAPESEQUENCE);
1413 
1414 		case SCLEX_PASCAL:
1415 			return (style == SCE_PAS_CHARACTER ||
1416 				style == SCE_PAS_STRING ||
1417 				style == SCE_PAS_STRINGEOL);
1418 
1419 		case SCLEX_D:
1420 			return (style == SCE_D_STRING ||
1421 				style == SCE_D_STRINGEOL ||
1422 				style == SCE_D_CHARACTER ||
1423 				style == SCE_D_STRINGB ||
1424 				style == SCE_D_STRINGR);
1425 
1426 		case SCLEX_PYTHON:
1427 			return (style == SCE_P_STRING ||
1428 				style == SCE_P_TRIPLE ||
1429 				style == SCE_P_TRIPLEDOUBLE ||
1430 				style == SCE_P_CHARACTER ||
1431 				style == SCE_P_FSTRING ||
1432 				style == SCE_P_FCHARACTER ||
1433 				style == SCE_P_FTRIPLE ||
1434 				style == SCE_P_FTRIPLEDOUBLE ||
1435 				style == SCE_P_STRINGEOL);
1436 
1437 		case SCLEX_F77:
1438 		case SCLEX_FORTRAN:
1439 			return (style == SCE_F_STRING1 ||
1440 				style == SCE_F_STRING2 ||
1441 				style == SCE_F_STRINGEOL);
1442 
1443 		case SCLEX_PERL:
1444 			return (style == SCE_PL_STRING ||
1445 				style == SCE_PL_CHARACTER ||
1446 				style == SCE_PL_HERE_DELIM ||
1447 				style == SCE_PL_HERE_Q ||
1448 				style == SCE_PL_HERE_QQ ||
1449 				style == SCE_PL_HERE_QX ||
1450 				style == SCE_PL_POD ||
1451 				style == SCE_PL_STRING_Q ||
1452 				style == SCE_PL_STRING_QQ ||
1453 				style == SCE_PL_STRING_QX ||
1454 				style == SCE_PL_STRING_QR ||
1455 				style == SCE_PL_STRING_QW ||
1456 				style == SCE_PL_POD_VERB ||
1457 				style == SCE_PL_REGEX ||
1458 				style == SCE_PL_REGEX_VAR ||
1459 				style == SCE_PL_XLAT
1460 				/* we don't include any STRING_*_VAR for autocompletion */);
1461 
1462 		case SCLEX_PO:
1463 			return (style == SCE_PO_MSGCTXT_TEXT ||
1464 				style == SCE_PO_MSGCTXT_TEXT_EOL ||
1465 				style == SCE_PO_MSGID_TEXT ||
1466 				style == SCE_PO_MSGID_TEXT_EOL ||
1467 				style == SCE_PO_MSGSTR_TEXT ||
1468 				style == SCE_PO_MSGSTR_TEXT_EOL);
1469 
1470 		case SCLEX_R:
1471 			return (style == SCE_R_STRING);
1472 
1473 		case SCLEX_RUBY:
1474 			return (style == SCE_RB_CHARACTER ||
1475 				style == SCE_RB_STRING ||
1476 				style == SCE_RB_HERE_DELIM ||
1477 				style == SCE_RB_HERE_Q ||
1478 				style == SCE_RB_HERE_QQ ||
1479 				style == SCE_RB_HERE_QX ||
1480 				style == SCE_RB_REGEX ||
1481 				style == SCE_RB_STRING_Q ||
1482 				style == SCE_RB_STRING_QQ ||
1483 				style == SCE_RB_STRING_QX ||
1484 				style == SCE_RB_STRING_QR ||
1485 				style == SCE_RB_STRING_QW ||
1486 				style == SCE_RB_POD);
1487 
1488 		case SCLEX_BASH:
1489 			return (style == SCE_SH_STRING);
1490 
1491 		case SCLEX_SQL:
1492 			return (style == SCE_SQL_STRING);
1493 
1494 		case SCLEX_TCL:
1495 			return (style == SCE_TCL_IN_QUOTE);
1496 
1497 		case SCLEX_LUA:
1498 			return (style == SCE_LUA_LITERALSTRING ||
1499 				style == SCE_LUA_CHARACTER ||
1500 				style == SCE_LUA_STRINGEOL ||
1501 				style == SCE_LUA_STRING);
1502 
1503 		case SCLEX_HASKELL:
1504 		case SCLEX_LITERATEHASKELL:
1505 			return (style == SCE_HA_CHARACTER ||
1506 				style == SCE_HA_STRINGEOL ||
1507 				style == SCE_HA_STRING);
1508 
1509 		case SCLEX_FREEBASIC:
1510 			return (style == SCE_B_STRING ||
1511 				style == SCE_B_STRINGEOL);
1512 
1513 		case SCLEX_OCTAVE:
1514 			return (style == SCE_MATLAB_STRING ||
1515 				style == SCE_MATLAB_DOUBLEQUOTESTRING);
1516 
1517 		case SCLEX_XML:
1518 		case SCLEX_HTML:
1519 		case SCLEX_PHPSCRIPT:
1520 			return (
1521 				style == SCE_HBA_STRING ||
1522 				style == SCE_HBA_STRINGEOL ||
1523 				style == SCE_HB_STRING ||
1524 				style == SCE_HB_STRINGEOL ||
1525 				style == SCE_H_CDATA ||
1526 				style == SCE_H_DOUBLESTRING ||
1527 				style == SCE_HJA_DOUBLESTRING ||
1528 				style == SCE_HJA_SINGLESTRING ||
1529 				style == SCE_HJA_STRINGEOL ||
1530 				style == SCE_HJA_REGEX ||
1531 				style == SCE_HJ_DOUBLESTRING ||
1532 				style == SCE_HJ_SINGLESTRING ||
1533 				style == SCE_HJ_STRINGEOL ||
1534 				style == SCE_HJ_REGEX ||
1535 				style == SCE_HPA_CHARACTER ||
1536 				style == SCE_HPA_STRING ||
1537 				style == SCE_HPA_TRIPLE ||
1538 				style == SCE_HPA_TRIPLEDOUBLE ||
1539 				style == SCE_HP_CHARACTER ||
1540 				style == SCE_HPHP_HSTRING ||  /* HSTRING is a heredoc */
1541 				style == SCE_HPHP_HSTRING_VARIABLE ||
1542 				style == SCE_HPHP_SIMPLESTRING ||
1543 				style == SCE_HP_STRING ||
1544 				style == SCE_HP_TRIPLE ||
1545 				style == SCE_HP_TRIPLEDOUBLE ||
1546 				style == SCE_H_SGML_DOUBLESTRING ||
1547 				style == SCE_H_SGML_SIMPLESTRING ||
1548 				style == SCE_H_SINGLESTRING);
1549 
1550 		case SCLEX_CMAKE:
1551 			return (style == SCE_CMAKE_STRINGDQ ||
1552 				style == SCE_CMAKE_STRINGLQ ||
1553 				style == SCE_CMAKE_STRINGRQ ||
1554 				style == SCE_CMAKE_STRINGVAR);
1555 
1556 		case SCLEX_NSIS:
1557 			return (style == SCE_NSIS_STRINGDQ ||
1558 				style == SCE_NSIS_STRINGLQ ||
1559 				style == SCE_NSIS_STRINGRQ ||
1560 				style == SCE_NSIS_STRINGVAR);
1561 
1562 		case SCLEX_ADA:
1563 			return (style == SCE_ADA_CHARACTER ||
1564 				style == SCE_ADA_STRING ||
1565 				style == SCE_ADA_CHARACTEREOL ||
1566 				style == SCE_ADA_STRINGEOL);
1567 
1568 		case SCLEX_ABAQUS:
1569 			return (style == SCE_ABAQUS_STRING);
1570 
1571 		case SCLEX_RUST:
1572 			return (style == SCE_RUST_CHARACTER ||
1573 				style == SCE_RUST_BYTECHARACTER ||
1574 				style == SCE_RUST_STRING ||
1575 				style == SCE_RUST_STRINGR ||
1576 				style == SCE_RUST_BYTESTRING ||
1577 				style == SCE_RUST_BYTESTRINGR ||
1578 				style == SCE_RUST_LEXERROR);
1579 
1580 		case SCLEX_COFFEESCRIPT:
1581 			return (style == SCE_COFFEESCRIPT_CHARACTER ||
1582 				style == SCE_COFFEESCRIPT_STRING ||
1583 				style == SCE_COFFEESCRIPT_REGEX ||
1584 				style == SCE_COFFEESCRIPT_VERBOSE_REGEX ||
1585 				style == SCE_COFFEESCRIPT_STRINGEOL);
1586 
1587 		case SCLEX_VERILOG:
1588 			return (style == SCE_V_STRING);
1589 
1590 		case SCLEX_VHDL:
1591 			return (style == SCE_VHDL_STRING ||
1592 				style == SCE_VHDL_STRINGEOL);
1593 
1594 		case SCLEX_CAML:
1595 			return (style == SCE_CAML_CHAR ||
1596 				style == SCE_CAML_STRING);
1597 
1598 		case SCLEX_CSS:
1599 			return (style == SCE_CSS_DOUBLESTRING ||
1600 				style == SCE_CSS_SINGLESTRING);
1601 
1602 		case SCLEX_ERLANG:
1603 			return (style == SCE_ERLANG_STRING ||
1604 				style == SCE_ERLANG_CHARACTER);
1605 
1606 		case SCLEX_LISP:
1607 			return (style == SCE_LISP_STRING ||
1608 				style == SCE_LISP_STRINGEOL);
1609 
1610 		case SCLEX_FORTH:
1611 			return (style == SCE_FORTH_STRING);
1612 
1613 		case SCLEX_POWERSHELL:
1614 			return (style == SCE_POWERSHELL_STRING ||
1615 				style == SCE_POWERSHELL_CHARACTER);
1616 
1617 		case SCLEX_BATCH:
1618 		case SCLEX_DIFF:
1619 		case SCLEX_LATEX:
1620 		case SCLEX_MAKEFILE:
1621 		case SCLEX_MARKDOWN:
1622 		case SCLEX_PROPERTIES:
1623 		case SCLEX_TXT2TAGS:
1624 		case SCLEX_YAML:
1625 			/* there is no string type in those lexers, listing here just for completeness */
1626 			return FALSE;
1627 	}
1628 	return FALSE;
1629 }
1630 
1631 
1632 /** Checks whether the given style is a comment for the given lexer.
1633  *
1634  * @param lexer Scintilla lexer type (@c SCLEX_*).
1635  * @param style Scintilla style (@c SCE_*).
1636  *
1637  * @return @c TRUE if the style is a comment, @c FALSE otherwise.
1638  */
1639 GEANY_API_SYMBOL
highlighting_is_comment_style(gint lexer,gint style)1640 gboolean highlighting_is_comment_style(gint lexer, gint style)
1641 {
1642 	switch (lexer)
1643 	{
1644 		case SCLEX_COBOL:
1645 		case SCLEX_CPP:
1646 			return (style == SCE_C_COMMENT ||
1647 				style == SCE_C_COMMENTLINE ||
1648 				style == SCE_C_COMMENTDOC ||
1649 				style == SCE_C_PREPROCESSORCOMMENT ||
1650 				style == SCE_C_PREPROCESSORCOMMENTDOC ||
1651 				style == SCE_C_COMMENTLINEDOC ||
1652 				style == SCE_C_COMMENTDOCKEYWORD ||
1653 				style == SCE_C_COMMENTDOCKEYWORDERROR ||
1654 				style == SCE_C_TASKMARKER);
1655 
1656 		case SCLEX_PASCAL:
1657 			return (style == SCE_PAS_COMMENT ||
1658 				style == SCE_PAS_COMMENT2 ||
1659 				style == SCE_PAS_COMMENTLINE);
1660 
1661 		case SCLEX_D:
1662 			return (style == SCE_D_COMMENT ||
1663 				style == SCE_D_COMMENTLINE ||
1664 				style == SCE_D_COMMENTDOC ||
1665 				style == SCE_D_COMMENTNESTED ||
1666 				style == SCE_D_COMMENTLINEDOC ||
1667 				style == SCE_D_COMMENTDOCKEYWORD ||
1668 				style == SCE_D_COMMENTDOCKEYWORDERROR);
1669 
1670 		case SCLEX_PYTHON:
1671 			return (style == SCE_P_COMMENTLINE ||
1672 				style == SCE_P_COMMENTBLOCK);
1673 
1674 		case SCLEX_F77:
1675 		case SCLEX_FORTRAN:
1676 			return (style == SCE_F_COMMENT);
1677 
1678 		case SCLEX_PERL:
1679 			return (style == SCE_PL_COMMENTLINE);
1680 
1681 		case SCLEX_PROPERTIES:
1682 			return (style == SCE_PROPS_COMMENT);
1683 
1684 		case SCLEX_PO:
1685 			return (style == SCE_PO_COMMENT ||
1686 				style == SCE_PO_PROGRAMMER_COMMENT);
1687 
1688 		case SCLEX_LATEX:
1689 			return (style == SCE_L_COMMENT ||
1690 				style == SCE_L_COMMENT2);
1691 
1692 		case SCLEX_MAKEFILE:
1693 			return (style == SCE_MAKE_COMMENT);
1694 
1695 		case SCLEX_RUBY:
1696 			return (style == SCE_RB_COMMENTLINE);
1697 
1698 		case SCLEX_BASH:
1699 			return (style == SCE_SH_COMMENTLINE);
1700 
1701 		case SCLEX_R:
1702 			return (style == SCE_R_COMMENT);
1703 
1704 		case SCLEX_SQL:
1705 			return (style == SCE_SQL_COMMENT ||
1706 				style == SCE_SQL_COMMENTLINE ||
1707 				style == SCE_SQL_COMMENTDOC ||
1708 				style == SCE_SQL_COMMENTLINEDOC ||
1709 				style == SCE_SQL_COMMENTDOCKEYWORD ||
1710 				style == SCE_SQL_COMMENTDOCKEYWORDERROR);
1711 
1712 		case SCLEX_TCL:
1713 			return (style == SCE_TCL_COMMENT ||
1714 				style == SCE_TCL_COMMENTLINE ||
1715 				style == SCE_TCL_COMMENT_BOX ||
1716 				style == SCE_TCL_BLOCK_COMMENT);
1717 
1718 		case SCLEX_OCTAVE:
1719 			return (style == SCE_MATLAB_COMMENT);
1720 
1721 		case SCLEX_LUA:
1722 			return (style == SCE_LUA_COMMENT ||
1723 				style == SCE_LUA_COMMENTLINE ||
1724 				style == SCE_LUA_COMMENTDOC);
1725 
1726 		case SCLEX_HASKELL:
1727 		case SCLEX_LITERATEHASKELL:
1728 			return (style == SCE_HA_COMMENTLINE ||
1729 				style == SCE_HA_COMMENTBLOCK ||
1730 				style == SCE_HA_COMMENTBLOCK2 ||
1731 				style == SCE_HA_COMMENTBLOCK3 ||
1732 				style == SCE_HA_LITERATE_COMMENT ||
1733 				style == SCE_HA_LITERATE_CODEDELIM);
1734 
1735 		case SCLEX_FREEBASIC:
1736 			return (style == SCE_B_COMMENT ||
1737 				style == SCE_B_COMMENTBLOCK ||
1738 				style == SCE_B_DOCLINE ||
1739 				style == SCE_B_DOCBLOCK ||
1740 				style == SCE_B_DOCKEYWORD);
1741 
1742 		case SCLEX_YAML:
1743 			return (style == SCE_YAML_COMMENT);
1744 
1745 		case SCLEX_XML:
1746 		case SCLEX_HTML:
1747 		case SCLEX_PHPSCRIPT:
1748 			return (
1749 				style == SCE_HBA_COMMENTLINE ||
1750 				style == SCE_HB_COMMENTLINE ||
1751 				style == SCE_H_COMMENT ||
1752 				style == SCE_HJA_COMMENT ||
1753 				style == SCE_HJA_COMMENTDOC ||
1754 				style == SCE_HJA_COMMENTLINE ||
1755 				style == SCE_HJ_COMMENT ||
1756 				style == SCE_HJ_COMMENTDOC ||
1757 				style == SCE_HJ_COMMENTLINE ||
1758 				style == SCE_HPA_COMMENTLINE ||
1759 				style == SCE_HP_COMMENTLINE ||
1760 				style == SCE_HPHP_COMMENT ||
1761 				style == SCE_HPHP_COMMENTLINE ||
1762 				style == SCE_H_SGML_COMMENT);
1763 
1764 		case SCLEX_CMAKE:
1765 			return (style == SCE_CMAKE_COMMENT);
1766 
1767 		case SCLEX_NSIS:
1768 			return (style == SCE_NSIS_COMMENT ||
1769 				style == SCE_NSIS_COMMENTBOX);
1770 
1771 		case SCLEX_ADA:
1772 			return (style == SCE_ADA_COMMENTLINE);
1773 
1774 		case SCLEX_ABAQUS:
1775 			return (style == SCE_ABAQUS_COMMENT ||
1776 				 style == SCE_ABAQUS_COMMENTBLOCK);
1777 
1778 		case SCLEX_ASM:
1779 			return (style == SCE_ASM_COMMENT ||
1780 				style == SCE_ASM_COMMENTBLOCK ||
1781 				style == SCE_ASM_COMMENTDIRECTIVE);
1782 
1783 		case SCLEX_RUST:
1784 			return (style == SCE_RUST_COMMENTBLOCK ||
1785 				style == SCE_RUST_COMMENTLINE ||
1786 				style == SCE_RUST_COMMENTBLOCKDOC ||
1787 				style == SCE_RUST_COMMENTLINEDOC);
1788 
1789 		case SCLEX_COFFEESCRIPT:
1790 			return (style == SCE_COFFEESCRIPT_COMMENTLINE ||
1791 				style == SCE_COFFEESCRIPT_COMMENTBLOCK ||
1792 				style == SCE_COFFEESCRIPT_VERBOSE_REGEX_COMMENT);
1793 
1794 		case SCLEX_VERILOG:
1795 			return (style == SCE_V_COMMENT ||
1796 				style == SCE_V_COMMENTLINE ||
1797 				style == SCE_V_COMMENTLINEBANG ||
1798 				style == SCE_V_COMMENT_WORD);
1799 
1800 		case SCLEX_VHDL:
1801 			return (style == SCE_VHDL_COMMENT ||
1802 				style == SCE_VHDL_COMMENTLINEBANG ||
1803 				style == SCE_VHDL_BLOCK_COMMENT);
1804 
1805 		case SCLEX_BATCH:
1806 			return (style == SCE_BAT_COMMENT);
1807 
1808 		case SCLEX_CAML:
1809 			return (style == SCE_CAML_COMMENT ||
1810 				style == SCE_CAML_COMMENT1 ||
1811 				style == SCE_CAML_COMMENT2 ||
1812 				style == SCE_CAML_COMMENT3);
1813 
1814 		case SCLEX_ERLANG:
1815 			return (style == SCE_ERLANG_COMMENT ||
1816 				style == SCE_ERLANG_COMMENT_FUNCTION ||
1817 				style == SCE_ERLANG_COMMENT_MODULE ||
1818 				style == SCE_ERLANG_COMMENT_DOC ||
1819 				style == SCE_ERLANG_COMMENT_DOC_MACRO);
1820 
1821 		case SCLEX_FORTH:
1822 			return (style == SCE_FORTH_COMMENT ||
1823 				style == SCE_FORTH_COMMENT_ML);
1824 
1825 		case SCLEX_CSS:
1826 			return (style == SCE_CSS_COMMENT);
1827 
1828 		case SCLEX_DIFF:
1829 			return (style == SCE_DIFF_COMMENT);
1830 
1831 		case SCLEX_LISP:
1832 			return (style == SCE_LISP_COMMENT ||
1833 				style == SCE_LISP_MULTI_COMMENT);
1834 
1835 		case SCLEX_POWERSHELL:
1836 			return (style == SCE_POWERSHELL_COMMENT ||
1837 				style == SCE_POWERSHELL_COMMENTSTREAM ||
1838 				style == SCE_POWERSHELL_COMMENTDOCKEYWORD);
1839 
1840 		case SCLEX_TXT2TAGS:
1841 			return (style == SCE_TXT2TAGS_COMMENT);
1842 
1843 		case SCLEX_MARKDOWN:
1844 			/* there is no comment type in those lexers, listing here just for completeness */
1845 			return FALSE;
1846 	}
1847 	return FALSE;
1848 }
1849 
1850 
1851 /** Checks whether the given style is normal code (not string, comment, preprocessor, etc).
1852  *
1853  * @param lexer Scintilla lexer type (@c SCLEX_*).
1854  * @param style Scintilla style (@c SCE_*).
1855  *
1856  * @return @c TRUE if the style is code, @c FALSE otherwise.
1857  */
1858 GEANY_API_SYMBOL
highlighting_is_code_style(gint lexer,gint style)1859 gboolean highlighting_is_code_style(gint lexer, gint style)
1860 {
1861 	switch (lexer)
1862 	{
1863 		case SCLEX_CPP:
1864 		{
1865 			if (style == SCE_C_PREPROCESSOR)
1866 				return FALSE;
1867 			break;
1868 		}
1869 		case SCLEX_HASKELL:
1870 		case SCLEX_LITERATEHASKELL:
1871 		{
1872 			if (style == SCE_HA_PREPROCESSOR)
1873 				return FALSE;
1874 			break;
1875 		}
1876 		case SCLEX_VERILOG:
1877 		{
1878 			if (style == SCE_V_PREPROCESSOR)
1879 				return FALSE;
1880 			break;
1881 		}
1882 	}
1883 	return !(highlighting_is_comment_style(lexer, style) ||
1884 		highlighting_is_string_style(lexer, style));
1885 }
1886