/* * highlighting.c - this file is part of Geany, a fast and lightweight IDE * * Copyright 2005-2011 Enrico Tröger * Copyright 2006-2011 Nick Treleaven * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file highlighting.h * Syntax highlighting for the different filetypes, using the Scintilla lexers. */ #include "geany.h" #include #include #include #include "SciLexer.h" #include "highlighting.h" #include "editor.h" #include "utils.h" #include "filetypes.h" #include "symbols.h" #include "ui_utils.h" #include "utils.h" #include "main.h" #include "support.h" #include "sciwrappers.h" #include "highlightingmappings.h" #define GEANY_COLORSCHEMES_SUBDIR "colorschemes" /* Whitespace has to be set after setting wordchars. */ #define GEANY_WHITESPACE_CHARS " \t" "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~" static gchar *whitespace_chars; typedef struct { gsize count; /* number of styles */ GeanyLexerStyle *styling; /* array of styles, NULL if not used or uninitialised */ gchar **keywords; gchar *wordchars; /* NULL used for style sets with no styles */ gchar **property_keys; gchar **property_values; } StyleSet; /* each filetype has a styleset but GEANY_FILETYPES_NONE uses common_style_set for styling */ static StyleSet *style_sets = NULL; enum /* Geany common styling */ { GCS_DEFAULT, GCS_SELECTION, GCS_BRACE_GOOD, GCS_BRACE_BAD, GCS_MARGIN_LINENUMBER, GCS_MARGIN_FOLDING, GCS_FOLD_SYMBOL_HIGHLIGHT, GCS_CURRENT_LINE, GCS_CARET, GCS_INDENT_GUIDE, GCS_WHITE_SPACE, GCS_LINE_WRAP_VISUALS, GCS_LINE_WRAP_INDENT, GCS_TRANSLUCENCY, GCS_MARKER_LINE, GCS_MARKER_SEARCH, GCS_MARKER_MARK, GCS_MARKER_TRANSLUCENCY, GCS_LINE_HEIGHT, GCS_CALLTIPS, GCS_MAX }; static struct { GeanyLexerStyle styling[GCS_MAX]; /* icon style, 1-4 */ gint fold_marker; /* vertical line style, 0-2 */ gint fold_lines; /* horizontal line when folded, 0-2 */ gint fold_draw_line; gchar *wordchars; } common_style_set; /* For filetypes.common [named_styles] section. * 0xBBGGRR format. * e.g. "comment" => &GeanyLexerStyle{0x0000d0, 0xffffff, FALSE, FALSE} */ static GHashTable *named_style_hash = NULL; /* 0xBBGGRR format, set by "default" named style. */ static GeanyLexerStyle gsd_default = {0x000000, 0xffffff, FALSE, FALSE}; /* Note: use sciwrappers.h instead where possible. * Do not use SSM in files unrelated to scintilla. */ #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l) /* filetypes should use the filetypes.foo [lexer_properties] group instead of hardcoding */ static void sci_set_property(ScintillaObject *sci, const gchar *name, const gchar *value) { SSM(sci, SCI_SETPROPERTY, (uptr_t) name, (sptr_t) value); } static void new_styleset(guint file_type_id, gsize styling_count) { StyleSet *set = &style_sets[file_type_id]; set->count = styling_count; set->styling = g_new0(GeanyLexerStyle, styling_count); } static void free_styleset(guint file_type_id) { StyleSet *style_ptr; style_ptr = &style_sets[file_type_id]; style_ptr->count = 0; g_free(style_ptr->styling); style_ptr->styling = NULL; g_strfreev(style_ptr->keywords); style_ptr->keywords = NULL; g_free(style_ptr->wordchars); style_ptr->wordchars = NULL; g_strfreev(style_ptr->property_keys); style_ptr->property_keys = NULL; g_strfreev(style_ptr->property_values); style_ptr->property_values = NULL; } static void get_keyfile_keywords(GKeyFile *config, GKeyFile *configh, const gchar *key, guint ft_id, guint pos) { style_sets[ft_id].keywords[pos] = utils_get_setting(string, configh, config, "keywords", key, ""); } static void get_keyfile_wordchars(GKeyFile *config, GKeyFile *configh, gchar **wordchars) { *wordchars = utils_get_setting(string, configh, config, "settings", "wordchars", GEANY_WORDCHARS); } static gboolean read_named_style(const gchar *named_style, GeanyLexerStyle *style) { GeanyLexerStyle *cs; gchar *comma, *name = NULL; const gchar *bold = NULL; const gchar *italic = NULL; g_return_val_if_fail(named_style, FALSE); name = utils_strdupa(named_style); /* named_style must not be written to, may be a static string */ comma = strstr(name, ","); if (comma) { bold = strstr(comma, ",bold"); italic = strstr(comma, ",italic"); *comma = '\0'; /* terminate name to make lookup work */ } cs = g_hash_table_lookup(named_style_hash, name); if (cs) { *style = *cs; if (bold) style->bold = !style->bold; if (italic) style->italic = !style->italic; } else { *style = gsd_default; } return (cs != NULL); } static void parse_color(const gchar *str, gint *clr) { gint c; /* ignore empty strings */ if (G_UNLIKELY(! NZV(str))) return; c = utils_strtod(str, NULL, FALSE); if (c > -1) { *clr = c; return; } geany_debug("Bad color '%s'", str); } static void parse_keyfile_style(gchar **list, const GeanyLexerStyle *default_style, GeanyLexerStyle *style) { gsize len; gchar *str; g_return_if_fail(default_style); g_return_if_fail(style); *style = *default_style; if (!list) return; len = g_strv_length(list); str = list[0]; if (len == 1 && isalpha(str[0])) { if (!read_named_style(str, style)) geany_debug( "No named style '%s'! Check filetype styles or %s color scheme.", str, NVL(editor_prefs.color_scheme, "filetypes.common")); } else { switch (len) { case 4: style->italic = utils_atob(list[3]); case 3: style->bold = utils_atob(list[2]); case 2: parse_color(list[1], &style->background); case 1: parse_color(list[0], &style->foreground); } } } static void get_keyfile_style(GKeyFile *config, GKeyFile *configh, const gchar *key_name, GeanyLexerStyle *style) { gchar **list; gsize len; g_return_if_fail(config); g_return_if_fail(configh); g_return_if_fail(key_name); g_return_if_fail(style); list = g_key_file_get_string_list(configh, "styling", key_name, &len, NULL); if (list == NULL) list = g_key_file_get_string_list(config, "styling", key_name, &len, NULL); parse_keyfile_style(list, &gsd_default, style); g_strfreev(list); } /* Convert 0xRRGGBB to 0xBBGGRR, which scintilla expects. */ static gint rotate_rgb(gint color) { return ((color & 0xFF0000) >> 16) + (color & 0x00FF00) + ((color & 0x0000FF) << 16); } static void convert_int(const gchar *int_str, gint *val) { gchar *end; gint v = strtol(int_str, &end, 10); if (int_str != end) *val = v; } /* Get first and second integer numbers, store in foreground and background fields of @a style. */ static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *section, const gchar *key, gint fdefault_val, gint sdefault_val, GeanyLexerStyle *style) { gchar **list; gsize len; GeanyLexerStyle def = {fdefault_val, sdefault_val, FALSE, FALSE}; g_return_if_fail(config); g_return_if_fail(configh); g_return_if_fail(section); g_return_if_fail(key); list = g_key_file_get_string_list(configh, section, key, &len, NULL); if (list == NULL) list = g_key_file_get_string_list(config, section, key, &len, NULL); *style = def; if (!list) return; if (list[0]) { convert_int(list[0], &style->foreground); if (list[1]) { convert_int(list[1], &style->background); } } g_strfreev(list); } /* first or second can be NULL. */ static void get_keyfile_ints(GKeyFile *config, GKeyFile *configh, const gchar *section, const gchar *key, gint fdefault_val, gint sdefault_val, gint *first, gint *second) { GeanyLexerStyle tmp_style; get_keyfile_int(config, configh, section, key, fdefault_val, sdefault_val, &tmp_style); if (first) *first = tmp_style.foreground; if (second) *second = tmp_style.background; } static guint invert(guint icolour) { if (interface_prefs.highlighting_invert_all) return utils_invert_color(icolour); return icolour; } static GeanyLexerStyle *get_style(guint ft_id, guint styling_index) { g_assert(ft_id < filetypes_array->len); if (G_UNLIKELY(ft_id == GEANY_FILETYPES_NONE)) { g_assert(styling_index < GCS_MAX); return &common_style_set.styling[styling_index]; } else { StyleSet *set = &style_sets[ft_id]; g_assert(styling_index < set->count); return &set->styling[styling_index]; } } static void set_sci_style(ScintillaObject *sci, guint style, guint ft_id, guint styling_index) { GeanyLexerStyle *style_ptr = get_style(ft_id, styling_index); SSM(sci, SCI_STYLESETFORE, style, invert(style_ptr->foreground)); SSM(sci, SCI_STYLESETBACK, style, invert(style_ptr->background)); SSM(sci, SCI_STYLESETBOLD, style, style_ptr->bold); SSM(sci, SCI_STYLESETITALIC, style, style_ptr->italic); } void highlighting_free_styles() { guint i; for (i = 0; i < filetypes_array->len; i++) free_styleset(i); if (named_style_hash) g_hash_table_destroy(named_style_hash); g_free(style_sets); } static GString *get_global_typenames(gint lang) { GString *s = NULL; if (app->tm_workspace) { GPtrArray *tags_array = app->tm_workspace->global_tags; if (tags_array) { s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang); } } return s; } static gchar* get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh) { return utils_get_setting(string, configh, config, "settings", "whitespace_chars", GEANY_WHITESPACE_CHARS); } static void add_named_style(GKeyFile *config, const gchar *key) { const gchar group[] = "named_styles"; gchar **list; gsize len; list = g_key_file_get_string_list(config, group, key, &len, NULL); /* we allow a named style to reference another style above it */ if (list && len >= 1) { GeanyLexerStyle *style = g_new0(GeanyLexerStyle, 1); parse_keyfile_style(list, &gsd_default, style); g_hash_table_insert(named_style_hash, g_strdup(key), style); } g_strfreev(list); } static void get_named_styles(GKeyFile *config) { const gchar group[] = "named_styles"; gchar **keys = g_key_file_get_keys(config, group, NULL, NULL); gchar **ptr = keys; if (!ptr) return; while (1) { const gchar *key = *ptr; if (!key) break; /* don't replace already read default style with system one */ if (!g_str_equal(key, "default")) add_named_style(config, key); ptr++; } g_strfreev(keys); } static GKeyFile *utils_key_file_new(const gchar *filename) { GKeyFile *config = g_key_file_new(); g_key_file_load_from_file(config, filename, G_KEY_FILE_KEEP_COMMENTS, NULL); return config; } static void load_named_styles(GKeyFile *config, GKeyFile *config_home) { const gchar *scheme = editor_prefs.color_scheme; gboolean free_kf = FALSE; if (named_style_hash) g_hash_table_destroy(named_style_hash); /* reloading */ named_style_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); if (NZV(scheme)) { gchar *path, *path_home; path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL); path_home = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL); if (g_file_test(path, G_FILE_TEST_EXISTS) || g_file_test(path_home, G_FILE_TEST_EXISTS)) { config = utils_key_file_new(path); config_home = utils_key_file_new(path_home); free_kf = TRUE; } /* if color scheme is missing, use default */ g_free(path); g_free(path_home); } /* first set default to the "default" named style */ add_named_style(config, "default"); read_named_style("default", &gsd_default); /* in case user overrides but not with both colors */ add_named_style(config_home, "default"); read_named_style("default", &gsd_default); get_named_styles(config); /* home overrides any system named style */ get_named_styles(config_home); if (free_kf) { g_key_file_free(config); g_key_file_free(config_home); } } static void styleset_common_init(guint ft_id, GKeyFile *config, GKeyFile *config_home) { load_named_styles(config, config_home); get_keyfile_style(config, config_home, "default", &common_style_set.styling[GCS_DEFAULT]); get_keyfile_style(config, config_home, "selection", &common_style_set.styling[GCS_SELECTION]); get_keyfile_style(config, config_home, "brace_good", &common_style_set.styling[GCS_BRACE_GOOD]); get_keyfile_style(config, config_home, "brace_bad", &common_style_set.styling[GCS_BRACE_BAD]); get_keyfile_style(config, config_home, "margin_linenumber", &common_style_set.styling[GCS_MARGIN_LINENUMBER]); get_keyfile_style(config, config_home, "margin_folding", &common_style_set.styling[GCS_MARGIN_FOLDING]); get_keyfile_style(config, config_home, "fold_symbol_highlight", &common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT]); get_keyfile_style(config, config_home, "current_line", &common_style_set.styling[GCS_CURRENT_LINE]); get_keyfile_style(config, config_home, "caret", &common_style_set.styling[GCS_CARET]); get_keyfile_style(config, config_home, "indent_guide", &common_style_set.styling[GCS_INDENT_GUIDE]); get_keyfile_style(config, config_home, "white_space", &common_style_set.styling[GCS_WHITE_SPACE]); get_keyfile_style(config, config_home, "marker_line", &common_style_set.styling[GCS_MARKER_LINE]); get_keyfile_style(config, config_home, "marker_search", &common_style_set.styling[GCS_MARKER_SEARCH]); get_keyfile_style(config, config_home, "marker_mark", &common_style_set.styling[GCS_MARKER_MARK]); get_keyfile_style(config, config_home, "calltips", &common_style_set.styling[GCS_CALLTIPS]); get_keyfile_ints(config, config_home, "styling", "folding_style", 1, 1, &common_style_set.fold_marker, &common_style_set.fold_lines); get_keyfile_ints(config, config_home, "styling", "folding_horiz_line", 2, 0, &common_style_set.fold_draw_line, NULL); get_keyfile_ints(config, config_home, "styling", "caret_width", 1, 0, &common_style_set.styling[GCS_CARET].background, NULL); /* caret.foreground used earlier */ get_keyfile_int(config, config_home, "styling", "line_wrap_visuals", 3, 0, &common_style_set.styling[GCS_LINE_WRAP_VISUALS]); get_keyfile_int(config, config_home, "styling", "line_wrap_indent", 0, 0, &common_style_set.styling[GCS_LINE_WRAP_INDENT]); get_keyfile_int(config, config_home, "styling", "translucency", 256, 256, &common_style_set.styling[GCS_TRANSLUCENCY]); get_keyfile_int(config, config_home, "styling", "marker_translucency", 256, 256, &common_style_set.styling[GCS_MARKER_TRANSLUCENCY]); get_keyfile_int(config, config_home, "styling", "line_height", 0, 0, &common_style_set.styling[GCS_LINE_HEIGHT]); get_keyfile_wordchars(config, config_home, &common_style_set.wordchars); whitespace_chars = get_keyfile_whitespace_chars(config, config_home); } static void styleset_common(ScintillaObject *sci, guint ft_id) { SSM(sci, SCI_STYLECLEARALL, 0, 0); SSM(sci, SCI_SETWORDCHARS, 0, (sptr_t) (ft_id == GEANY_FILETYPES_NONE ? common_style_set.wordchars : style_sets[ft_id].wordchars)); /* have to set whitespace after setting wordchars */ SSM(sci, SCI_SETWHITESPACECHARS, 0, (sptr_t) whitespace_chars); /* caret colour, style and width */ SSM(sci, SCI_SETCARETFORE, invert(common_style_set.styling[GCS_CARET].foreground), 0); SSM(sci, SCI_SETCARETWIDTH, common_style_set.styling[GCS_CARET].background, 0); if (common_style_set.styling[GCS_CARET].bold) SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_BLOCK, 0); else SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_LINE, 0); /* line height */ SSM(sci, SCI_SETEXTRAASCENT, common_style_set.styling[GCS_LINE_HEIGHT].foreground, 0); SSM(sci, SCI_SETEXTRADESCENT, common_style_set.styling[GCS_LINE_HEIGHT].background, 0); /* colourise the current line */ SSM(sci, SCI_SETCARETLINEBACK, invert(common_style_set.styling[GCS_CURRENT_LINE].background), 0); /* bold=enable current line */ SSM(sci, SCI_SETCARETLINEVISIBLE, common_style_set.styling[GCS_CURRENT_LINE].bold, 0); /* Translucency for current line and selection */ SSM(sci, SCI_SETCARETLINEBACKALPHA, common_style_set.styling[GCS_TRANSLUCENCY].foreground, 0); SSM(sci, SCI_SETSELALPHA, common_style_set.styling[GCS_TRANSLUCENCY].background, 0); /* line wrapping visuals */ SSM(sci, SCI_SETWRAPVISUALFLAGS, common_style_set.styling[GCS_LINE_WRAP_VISUALS].foreground, 0); SSM(sci, SCI_SETWRAPVISUALFLAGSLOCATION, common_style_set.styling[GCS_LINE_WRAP_VISUALS].background, 0); SSM(sci, SCI_SETWRAPSTARTINDENT, common_style_set.styling[GCS_LINE_WRAP_INDENT].foreground, 0); SSM(sci, SCI_SETWRAPINDENTMODE, common_style_set.styling[GCS_LINE_WRAP_INDENT].background, 0); /* Error indicator */ SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_ERROR, INDIC_SQUIGGLE); SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_ERROR, invert(rotate_rgb(0xff0000))); /* Search indicator, used for 'Mark' matches */ SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_SEARCH, INDIC_ROUNDBOX); SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_SEARCH, invert(common_style_set.styling[GCS_MARKER_SEARCH].background)); SSM(sci, SCI_INDICSETALPHA, GEANY_INDICATOR_SEARCH, 60); /* define marker symbols * 0 -> line marker */ SSM(sci, SCI_MARKERDEFINE, 0, SC_MARK_SHORTARROW); SSM(sci, SCI_MARKERSETFORE, 0, invert(common_style_set.styling[GCS_MARKER_LINE].foreground)); SSM(sci, SCI_MARKERSETBACK, 0, invert(common_style_set.styling[GCS_MARKER_LINE].background)); SSM(sci, SCI_MARKERSETALPHA, 0, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].foreground); /* 1 -> user marker */ SSM(sci, SCI_MARKERDEFINE, 1, SC_MARK_PLUS); SSM(sci, SCI_MARKERSETFORE, 1, invert(common_style_set.styling[GCS_MARKER_MARK].foreground)); SSM(sci, SCI_MARKERSETBACK, 1, invert(common_style_set.styling[GCS_MARKER_MARK].background)); SSM(sci, SCI_MARKERSETALPHA, 1, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].background); /* 2 -> folding marker, other folding settings */ SSM(sci, SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL); SSM(sci, SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS); /* drawing a horizontal line when text if folded */ switch (common_style_set.fold_draw_line) { case 1: { SSM(sci, SCI_SETFOLDFLAGS, 4, 0); break; } case 2: { SSM(sci, SCI_SETFOLDFLAGS, 16, 0); break; } default: { SSM(sci, SCI_SETFOLDFLAGS, 0, 0); break; } } /* choose the folding style - boxes or circles, I prefer boxes, so it is default ;-) */ SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY); switch (common_style_set.fold_marker) { case 2: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED); break; default: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED); break; case 3: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW); break; case 4: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_MINUS); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PLUS); break; } /* choose the folding style - straight or curved, I prefer straight, so it is default ;-) */ switch (common_style_set.fold_lines) { case 2: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); break; default: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); break; case 0: SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY); SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY); break; } { gint markers[] = { SC_MARKNUM_FOLDEROPEN, SC_MARKNUM_FOLDER, SC_MARKNUM_FOLDERSUB, SC_MARKNUM_FOLDERTAIL, SC_MARKNUM_FOLDEREND, SC_MARKNUM_FOLDEROPENMID, SC_MARKNUM_FOLDERMIDTAIL }; guint i; foreach_range(i, G_N_ELEMENTS(markers)) { SSM(sci, SCI_MARKERSETFORE, markers[i], invert(common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT].foreground)); SSM(sci, SCI_MARKERSETBACK, markers[i], invert(common_style_set.styling[GCS_MARGIN_FOLDING].foreground)); } } /* set some common defaults */ sci_set_property(sci, "fold", "1"); sci_set_property(sci, "fold.compact", "0"); sci_set_property(sci, "fold.comment", "1"); sci_set_property(sci, "fold.preprocessor", "1"); sci_set_property(sci, "fold.at.else", "1"); /* bold (3rd argument) is whether to override default foreground selection */ if (common_style_set.styling[GCS_SELECTION].bold) SSM(sci, SCI_SETSELFORE, 1, invert(common_style_set.styling[GCS_SELECTION].foreground)); /* italic (4th argument) is whether to override default background selection */ if (common_style_set.styling[GCS_SELECTION].italic) SSM(sci, SCI_SETSELBACK, 1, invert(common_style_set.styling[GCS_SELECTION].background)); SSM(sci, SCI_SETSTYLEBITS, SSM(sci, SCI_GETSTYLEBITSNEEDED, 0, 0), 0); SSM(sci, SCI_SETFOLDMARGINCOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background)); SSM(sci, SCI_SETFOLDMARGINHICOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background)); set_sci_style(sci, STYLE_LINENUMBER, GEANY_FILETYPES_NONE, GCS_MARGIN_LINENUMBER); set_sci_style(sci, STYLE_BRACELIGHT, GEANY_FILETYPES_NONE, GCS_BRACE_GOOD); set_sci_style(sci, STYLE_BRACEBAD, GEANY_FILETYPES_NONE, GCS_BRACE_BAD); set_sci_style(sci, STYLE_INDENTGUIDE, GEANY_FILETYPES_NONE, GCS_INDENT_GUIDE); /* bold = common whitespace settings enabled */ SSM(sci, SCI_SETWHITESPACEFORE, common_style_set.styling[GCS_WHITE_SPACE].bold, invert(common_style_set.styling[GCS_WHITE_SPACE].foreground)); SSM(sci, SCI_SETWHITESPACEBACK, common_style_set.styling[GCS_WHITE_SPACE].italic, invert(common_style_set.styling[GCS_WHITE_SPACE].background)); if (common_style_set.styling[GCS_CALLTIPS].bold) SSM(sci, SCI_CALLTIPSETFORE, invert(common_style_set.styling[GCS_CALLTIPS].foreground), 1); if (common_style_set.styling[GCS_CALLTIPS].italic) SSM(sci, SCI_CALLTIPSETBACK, invert(common_style_set.styling[GCS_CALLTIPS].background), 1); } /* Merge & assign global typedefs and user secondary keywords. * keyword_idx is used for both style_sets[].keywords and scintilla keyword style number */ static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword_idx) { const gchar *user_words = style_sets[ft_id].keywords[keyword_idx]; GString *s; s = get_global_typenames(filetypes[ft_id]->lang); if (G_UNLIKELY(s == NULL)) s = g_string_sized_new(200); else g_string_append_c(s, ' '); /* append a space as delimiter to the existing list of words */ g_string_append(s, user_words); sci_set_keywords(sci, keyword_idx, s->str); g_string_free(s, TRUE); } static void styleset_init_from_mapping(guint ft_id, GKeyFile *config, GKeyFile *config_home, const HLStyle *styles, gsize n_styles, const HLKeyword *keywords, gsize n_keywords) { gsize i; /* styles */ new_styleset(ft_id, n_styles); foreach_range(i, n_styles) { GeanyLexerStyle *style = &style_sets[ft_id].styling[i]; get_keyfile_style(config, config_home, styles[i].name, style); } /* keywords */ if (n_keywords < 1) style_sets[ft_id].keywords = NULL; else { style_sets[ft_id].keywords = g_new(gchar*, n_keywords + 1); foreach_range(i, n_keywords) get_keyfile_keywords(config, config_home, keywords[i].key, ft_id, i); style_sets[ft_id].keywords[i] = NULL; } } /* STYLE_DEFAULT will be set to match the first style. */ static void 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) { gsize i; g_assert(ft_id != GEANY_FILETYPES_NONE); /* lexer */ SSM(sci, SCI_SETLEXER, lexer, 0); /* styles */ styleset_common(sci, ft_id); if (n_styles > 0) { /* first style is also default one */ set_sci_style(sci, STYLE_DEFAULT, ft_id, 0); foreach_range(i, n_styles) { if (styles[i].fill_eol) SSM(sci, SCI_STYLESETEOLFILLED, styles[i].style, TRUE); set_sci_style(sci, styles[i].style, ft_id, i); } } /* keywords */ foreach_range(i, n_keywords) { if (keywords[i].merge) merge_type_keywords(sci, ft_id, i); else sci_set_keywords(sci, keywords[i].id, style_sets[ft_id].keywords[i]); } /* properties */ foreach_range(i, n_properties) sci_set_property(sci, properties[i].property, properties[i].value); } static void styleset_default(ScintillaObject *sci, guint ft_id) { SSM(sci, SCI_SETLEXER, SCLEX_NULL, 0); /* we need to set STYLE_DEFAULT before we call SCI_STYLECLEARALL in styleset_common() */ set_sci_style(sci, STYLE_DEFAULT, GEANY_FILETYPES_NONE, GCS_DEFAULT); styleset_common(sci, ft_id); } static void get_key_values(GKeyFile *config, const gchar *group, gchar **keys, gchar **values) { while (*keys) { gchar *str = g_key_file_get_string(config, group, *keys, NULL); if (str) setptr(*values, str); keys++; values++; } } static void read_properties(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh) { gchar group[] = "lexer_properties"; gchar **keys; gchar **keysh = g_key_file_get_keys(configh, group, NULL, NULL); gchar **ptr; /* remove overridden keys from system keyfile */ foreach_strv(ptr, keysh) g_key_file_remove_key(config, group, *ptr, NULL); /* merge sys and user keys */ keys = g_key_file_get_keys(config, group, NULL, NULL); keys = utils_strv_join(keys, keysh); if (keys) { gchar **values = g_new0(gchar*, g_strv_length(keys) + 1); style_sets[ft->id].property_keys = keys; style_sets[ft->id].property_values = values; get_key_values(config, group, keys, values); get_key_values(configh, group, keys, values); } } static guint get_lexer_filetype(GeanyFiletype *ft) { ft = NVL(ft->lexer_filetype, ft); return ft->id; } #define init_styleset_case(LANG_NAME) \ case (GEANY_FILETYPES_##LANG_NAME): \ styleset_init_from_mapping(filetype_idx, config, configh, \ highlighting_styles_##LANG_NAME, \ HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \ highlighting_keywords_##LANG_NAME, \ HL_N_ENTRIES(highlighting_keywords_##LANG_NAME)); \ break /* Called by filetypes_load_config(). */ void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *configh) { GeanyFiletype *ft = filetypes[filetype_idx]; guint lexer_id = get_lexer_filetype(ft); if (!style_sets) style_sets = g_new0(StyleSet, filetypes_array->len); /* Clear old information if necessary - e.g. when reloading config */ free_styleset(filetype_idx); read_properties(ft, config, configh); /* None filetype handled specially */ if (filetype_idx == GEANY_FILETYPES_NONE) { styleset_common_init(GEANY_FILETYPES_NONE, config, configh); return; } /* All stylesets depend on filetypes.common */ filetypes_load_config(GEANY_FILETYPES_NONE, FALSE); switch (lexer_id) { init_styleset_case(ADA); init_styleset_case(ASM); init_styleset_case(BASIC); init_styleset_case(C); init_styleset_case(CAML); init_styleset_case(CMAKE); init_styleset_case(COBOL); init_styleset_case(CONF); init_styleset_case(CSS); init_styleset_case(D); init_styleset_case(DIFF); init_styleset_case(LISP); init_styleset_case(ERLANG); init_styleset_case(DOCBOOK); init_styleset_case(FERITE); init_styleset_case(F77); init_styleset_case(FORTH); init_styleset_case(FORTRAN); init_styleset_case(HASKELL); init_styleset_case(HAXE); init_styleset_case(AS); init_styleset_case(HTML); init_styleset_case(JAVA); init_styleset_case(JS); init_styleset_case(LATEX); init_styleset_case(LUA); init_styleset_case(MAKE); init_styleset_case(MATLAB); init_styleset_case(MARKDOWN); init_styleset_case(NSIS); init_styleset_case(PASCAL); init_styleset_case(PERL); init_styleset_case(PHP); init_styleset_case(PO); init_styleset_case(PYTHON); init_styleset_case(R); init_styleset_case(RUBY); init_styleset_case(SH); init_styleset_case(SQL); init_styleset_case(TCL); init_styleset_case(TXT2TAGS); init_styleset_case(VHDL); init_styleset_case(VERILOG); init_styleset_case(XML); init_styleset_case(YAML); default: if (ft->lexer_filetype) geany_debug("Filetype %s has a recursive lexer_filetype %s set!", ft->name, ft->lexer_filetype->name); } /* should be done in filetypes.c really: */ get_keyfile_wordchars(config, configh, &style_sets[filetype_idx].wordchars); } #define styleset_case(LANG_NAME) \ case (GEANY_FILETYPES_##LANG_NAME): \ styleset_from_mapping(sci, ft->id, highlighting_lexer_##LANG_NAME, \ highlighting_styles_##LANG_NAME, \ HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \ highlighting_keywords_##LANG_NAME, \ HL_N_ENTRIES(highlighting_keywords_##LANG_NAME), \ highlighting_properties_##LANG_NAME, \ HL_N_ENTRIES(highlighting_properties_##LANG_NAME)); \ break /** Sets up highlighting and other visual settings. * @param sci Scintilla widget. * @param ft Filetype settings to use. */ void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft) { guint lexer_id = get_lexer_filetype(ft); filetypes_load_config(ft->id, FALSE); /* load filetypes.ext */ switch (lexer_id) { styleset_case(ADA); styleset_case(ASM); styleset_case(BASIC); styleset_case(C); styleset_case(CAML); styleset_case(CMAKE); styleset_case(COBOL); styleset_case(CONF); styleset_case(CSS); styleset_case(D); styleset_case(DIFF); styleset_case(LISP); styleset_case(ERLANG); styleset_case(DOCBOOK); styleset_case(FERITE); styleset_case(F77); styleset_case(FORTH); styleset_case(FORTRAN); styleset_case(HASKELL); styleset_case(HAXE); styleset_case(AS); styleset_case(HTML); styleset_case(JAVA); styleset_case(JS); styleset_case(LATEX); styleset_case(LUA); styleset_case(MAKE); styleset_case(MARKDOWN); styleset_case(MATLAB); styleset_case(NSIS); styleset_case(PASCAL); styleset_case(PERL); styleset_case(PHP); styleset_case(PO); styleset_case(PYTHON); styleset_case(R); styleset_case(RUBY); styleset_case(SH); styleset_case(SQL); styleset_case(TCL); styleset_case(TXT2TAGS); styleset_case(VHDL); styleset_case(VERILOG); styleset_case(XML); styleset_case(YAML); case GEANY_FILETYPES_NONE: default: styleset_default(sci, ft->id); } /* [lexer_properties] settings */ if (style_sets[ft->id].property_keys) { gchar **prop = style_sets[ft->id].property_keys; gchar **val = style_sets[ft->id].property_values; while (*prop) { sci_set_property(sci, *prop, *val); prop++; val++; } } } /** Retrieves a style @a style_id for the filetype @a ft_id. * If the style was not already initialised * (e.g. by by opening a file of this type), it will be initialised. The returned pointer is * owned by Geany and must not be freed. * @param ft_id Filetype ID, e.g. @c GEANY_FILETYPES_DIFF. * @param style_id A Scintilla lexer style, e.g. @c SCE_DIFF_ADDED. See scintilla/include/SciLexer.h. * @return A pointer to the style struct. * @see Scintilla messages @c SCI_STYLEGETFORE, etc, for use with scintilla_send_message(). */ const GeanyLexerStyle *highlighting_get_style(gint ft_id, gint style_id) { g_return_val_if_fail(ft_id >= 0 && (guint) ft_id < filetypes_array->len, NULL); g_return_val_if_fail(style_id >= 0, NULL); /* ensure filetype loaded */ filetypes_load_config((guint) ft_id, FALSE); /* TODO: style_id might not be the real array index (Scintilla styles are not always synced * with array indices) */ return get_style((guint) ft_id, (guint) style_id); } static void on_color_scheme_clicked(GtkMenuItem *menuitem, gpointer user_data) { gchar *fname; gchar *path; /* prevent callback on setting initial value */ if (!GTK_WIDGET_MAPPED(menuitem)) return; /* check if default item */ if (!user_data) { setptr(editor_prefs.color_scheme, NULL); filetypes_reload(); return; } fname = g_strdup(g_object_get_data(G_OBJECT(menuitem), "colorscheme_file")); setptr(fname, utils_get_locale_from_utf8(fname)); /* fname is just the basename from the menu item, so prepend the custom files path */ path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL); if (!g_file_test(path, G_FILE_TEST_EXISTS)) { /* try the system path */ g_free(path); path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL); } if (g_file_test(path, G_FILE_TEST_EXISTS)) { setptr(editor_prefs.color_scheme, fname); fname = NULL; filetypes_reload(); } else { setptr(fname, utils_get_utf8_from_locale(fname)); ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname); } g_free(path); g_free(fname); } static gchar *utils_get_setting_locale_string(GKeyFile *keyfile, const gchar *group, const gchar *key, const gchar *default_value) { gchar *result = g_key_file_get_locale_string(keyfile, group, key, NULL, NULL); return NVL(result, g_strdup(default_value)); } static void add_color_scheme_item(GtkWidget *menu, const gchar *fname) { static GSList *group = NULL; GtkWidget *item; if (!fname) { item = gtk_radio_menu_item_new_with_mnemonic(group, _("_Default")); } else { GKeyFile *hkeyfile, *skeyfile; gchar *path, *theme_name, *tooltip; gchar *theme_fn = utils_get_utf8_from_locale(fname); path = utils_build_path(app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL); hkeyfile = utils_key_file_new(path); setptr(path, utils_build_path(app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL)); skeyfile = utils_key_file_new(path); theme_name = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "name", theme_fn); item = gtk_radio_menu_item_new_with_label(group, theme_name); g_object_set_data_full(G_OBJECT(item), "colorscheme_file", theme_fn, g_free); tooltip = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "description", NULL); if (tooltip != NULL) { gtk_widget_set_tooltip_text(item, tooltip); g_free(tooltip); } g_free(path); g_free(theme_name); g_key_file_free(hkeyfile); g_key_file_free(skeyfile); } group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); if (utils_str_equal(editor_prefs.color_scheme, fname)) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(menu), item); g_signal_connect(item, "activate", G_CALLBACK(on_color_scheme_clicked), GINT_TO_POINTER(fname != NULL)); } static gboolean add_color_scheme_items(GtkWidget *menu) { GSList *list, *node; g_return_val_if_fail(menu, FALSE); add_color_scheme_item(menu, NULL); list = utils_get_config_files(GEANY_COLORSCHEMES_SUBDIR); foreach_slist(node, list) { gchar *fname = node->data; if (g_str_has_suffix(fname, ".conf")) add_color_scheme_item(menu, fname); g_free(fname); } g_slist_free(list); return list != NULL; } static void create_color_scheme_menu(void) { GtkWidget *item, *menu, *root; menu = ui_lookup_widget(main_widgets.window, "menu_view_editor1_menu"); item = ui_image_menu_item_new(GTK_STOCK_SELECT_COLOR, _("_Color Schemes")); gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item); root = item; menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); add_color_scheme_items(menu); gtk_widget_show_all(root); } void highlighting_init(void) { create_color_scheme_menu(); } /** Checks whether the given style is a string for the given lexer. * * @param lexer Scintilla lexer type (@c SCLEX_*). * @param style Scintilla style (@c SCE_*). * * @return @c TRUE if the style is a string, @c FALSE otherwise. */ gboolean highlighting_is_string_style(gint lexer, gint style) { /* Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char. */ switch (lexer) { case SCLEX_CPP: return (style == SCE_C_CHARACTER || style == SCE_C_STRING || style == SCE_C_STRINGEOL || style == SCE_C_STRINGRAW || style == SCE_C_VERBATIM || style == SCE_C_TRIPLEVERBATIM); case SCLEX_PASCAL: return (style == SCE_PAS_CHARACTER || style == SCE_PAS_STRING || style == SCE_PAS_STRINGEOL); case SCLEX_D: return (style == SCE_D_STRING || style == SCE_D_STRINGEOL || style == SCE_D_CHARACTER || style == SCE_D_STRINGB || style == SCE_D_STRINGR); case SCLEX_PYTHON: return (style == SCE_P_STRING || style == SCE_P_TRIPLE || style == SCE_P_TRIPLEDOUBLE || style == SCE_P_CHARACTER || style == SCE_P_STRINGEOL); case SCLEX_F77: case SCLEX_FORTRAN: return (style == SCE_F_STRING1 || style == SCE_F_STRING2 || style == SCE_F_STRINGEOL); case SCLEX_PERL: return (style == SCE_PL_STRING || style == SCE_PL_CHARACTER || style == SCE_PL_HERE_DELIM || style == SCE_PL_HERE_Q || style == SCE_PL_HERE_QQ || style == SCE_PL_HERE_QX || style == SCE_PL_POD || style == SCE_PL_STRING_Q || style == SCE_PL_STRING_QQ || style == SCE_PL_STRING_QX || style == SCE_PL_STRING_QR || style == SCE_PL_STRING_QW || style == SCE_PL_POD_VERB || style == SCE_PL_XLAT /* we don't include any STRING_*_VAR for autocompletion */); case SCLEX_R: return (style == SCE_R_STRING); case SCLEX_RUBY: return (style == SCE_RB_CHARACTER || style == SCE_RB_STRING || style == SCE_RB_HERE_DELIM || style == SCE_RB_HERE_Q || style == SCE_RB_HERE_QQ || style == SCE_RB_HERE_QX || style == SCE_RB_POD); case SCLEX_BASH: return (style == SCE_SH_STRING); case SCLEX_SQL: return (style == SCE_SQL_STRING); case SCLEX_TCL: return (style == SCE_TCL_IN_QUOTE); case SCLEX_LUA: return (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_CHARACTER || style == SCE_LUA_STRINGEOL || style == SCE_LUA_STRING); case SCLEX_HASKELL: return (style == SCE_HA_CHARACTER || style == SCE_HA_STRING); case SCLEX_FREEBASIC: return (style == SCE_B_STRING || style == SCE_B_STRINGEOL); case SCLEX_OCTAVE: return (style == SCE_MATLAB_STRING || style == SCE_MATLAB_DOUBLEQUOTESTRING); case SCLEX_HTML: return ( style == SCE_HBA_STRING || style == SCE_HBA_STRINGEOL || style == SCE_HB_STRING || style == SCE_HB_STRINGEOL || style == SCE_H_CDATA || style == SCE_H_DOUBLESTRING || style == SCE_HJA_DOUBLESTRING || style == SCE_HJA_SINGLESTRING || style == SCE_HJA_STRINGEOL || style == SCE_HJ_DOUBLESTRING || style == SCE_HJ_SINGLESTRING || style == SCE_HJ_STRINGEOL || style == SCE_HPA_CHARACTER || style == SCE_HPA_STRING || style == SCE_HPA_TRIPLE || style == SCE_HPA_TRIPLEDOUBLE || style == SCE_HP_CHARACTER || style == SCE_HPHP_HSTRING || /* HSTRING is a heredoc */ style == SCE_HPHP_HSTRING_VARIABLE || style == SCE_HPHP_SIMPLESTRING || style == SCE_HP_STRING || style == SCE_HP_TRIPLE || style == SCE_HP_TRIPLEDOUBLE || style == SCE_H_SGML_DOUBLESTRING || style == SCE_H_SGML_SIMPLESTRING || style == SCE_H_SINGLESTRING); case SCLEX_CMAKE: return (style == SCE_CMAKE_STRINGDQ || style == SCE_CMAKE_STRINGLQ || style == SCE_CMAKE_STRINGRQ || style == SCE_CMAKE_STRINGVAR); case SCLEX_NSIS: return (style == SCE_NSIS_STRINGDQ || style == SCE_NSIS_STRINGLQ || style == SCE_NSIS_STRINGRQ || style == SCE_NSIS_STRINGVAR); case SCLEX_ADA: return (style == SCE_ADA_CHARACTER || style == SCE_ADA_STRING || style == SCE_ADA_CHARACTEREOL || style == SCE_ADA_STRINGEOL); } return FALSE; } /** Checks whether the given style is a comment for the given lexer. * * @param lexer Scintilla lexer type (@c SCLEX_*). * @param style Scintilla style (@c SCE_*). * * @return @c TRUE if the style is a comment, @c FALSE otherwise. */ gboolean highlighting_is_comment_style(gint lexer, gint style) { switch (lexer) { case SCLEX_COBOL: case SCLEX_CPP: return (style == SCE_C_COMMENT || style == SCE_C_COMMENTLINE || style == SCE_C_COMMENTDOC || style == SCE_C_COMMENTLINEDOC || style == SCE_C_COMMENTDOCKEYWORD || style == SCE_C_COMMENTDOCKEYWORDERROR); case SCLEX_PASCAL: return (style == SCE_PAS_COMMENT || style == SCE_PAS_COMMENT2 || style == SCE_PAS_COMMENTLINE); case SCLEX_D: return (style == SCE_D_COMMENT || style == SCE_D_COMMENTLINE || style == SCE_D_COMMENTDOC || style == SCE_D_COMMENTNESTED || style == SCE_D_COMMENTLINEDOC || style == SCE_D_COMMENTDOCKEYWORD || style == SCE_D_COMMENTDOCKEYWORDERROR); case SCLEX_PYTHON: return (style == SCE_P_COMMENTLINE || style == SCE_P_COMMENTBLOCK); case SCLEX_F77: case SCLEX_FORTRAN: return (style == SCE_F_COMMENT); case SCLEX_PERL: return (style == SCE_PL_COMMENTLINE); case SCLEX_PROPERTIES: return (style == SCE_PROPS_COMMENT); case SCLEX_PO: return (style == SCE_PO_COMMENT); case SCLEX_LATEX: return (style == SCE_L_COMMENT || style == SCE_L_COMMENT2); case SCLEX_MAKEFILE: return (style == SCE_MAKE_COMMENT); case SCLEX_RUBY: return (style == SCE_RB_COMMENTLINE); case SCLEX_BASH: return (style == SCE_SH_COMMENTLINE); case SCLEX_R: return (style == SCE_R_COMMENT); case SCLEX_SQL: return (style == SCE_SQL_COMMENT || style == SCE_SQL_COMMENTLINE || style == SCE_SQL_COMMENTDOC || style == SCE_SQL_COMMENTLINEDOC || style == SCE_SQL_COMMENTDOCKEYWORD || style == SCE_SQL_COMMENTDOCKEYWORDERROR); case SCLEX_TCL: return (style == SCE_TCL_COMMENT || style == SCE_TCL_COMMENTLINE || style == SCE_TCL_COMMENT_BOX || style == SCE_TCL_BLOCK_COMMENT); case SCLEX_OCTAVE: return (style == SCE_MATLAB_COMMENT); case SCLEX_LUA: return (style == SCE_LUA_COMMENT || style == SCE_LUA_COMMENTLINE || style == SCE_LUA_COMMENTDOC); case SCLEX_HASKELL: return (style == SCE_HA_COMMENTLINE || style == SCE_HA_COMMENTBLOCK || style == SCE_HA_COMMENTBLOCK2 || style == SCE_HA_COMMENTBLOCK3); case SCLEX_FREEBASIC: return (style == SCE_B_COMMENT); case SCLEX_YAML: return (style == SCE_YAML_COMMENT); case SCLEX_HTML: return ( style == SCE_HBA_COMMENTLINE || style == SCE_HB_COMMENTLINE || style == SCE_H_COMMENT || style == SCE_HJA_COMMENT || style == SCE_HJA_COMMENTDOC || style == SCE_HJA_COMMENTLINE || style == SCE_HJ_COMMENT || style == SCE_HJ_COMMENTDOC || style == SCE_HJ_COMMENTLINE || style == SCE_HPA_COMMENTLINE || style == SCE_HP_COMMENTLINE || style == SCE_HPHP_COMMENT || style == SCE_HPHP_COMMENTLINE || style == SCE_H_SGML_COMMENT); case SCLEX_CMAKE: return (style == SCE_CMAKE_COMMENT); case SCLEX_NSIS: return (style == SCE_NSIS_COMMENT || style == SCE_NSIS_COMMENTBOX); case SCLEX_ADA: return (style == SCE_ADA_COMMENTLINE || style == SCE_NSIS_COMMENTBOX); case SCLEX_ASM: return (style == SCE_ASM_COMMENT || style == SCE_ASM_COMMENTBLOCK || style == SCE_ASM_COMMENTDIRECTIVE); } return FALSE; } /** Checks whether the given style is normal code (not string, comment, preprocessor, etc). * * @param lexer Scintilla lexer type (@c SCLEX_*). * @param style Scintilla style (@c SCE_*). * * @return @c TRUE if the style is code, @c FALSE otherwise. */ gboolean highlighting_is_code_style(gint lexer, gint style) { switch (lexer) { case SCLEX_CPP: if (style == SCE_C_PREPROCESSOR) return FALSE; break; } return !(highlighting_is_comment_style(lexer, style) || highlighting_is_string_style(lexer, style)); }