"Fix" passing const arguments to spawn functions
[geany-mirror.git] / src / filetypes.c
blob6e2917a96e641129a2588a269b4405f27b90d033
1 /*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005 The Geany contributors
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.
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.
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.
21 /**
22 * @file filetypes.h
23 * Filetype detection, file extensions and filetype menu items.
26 /* Note: we use GeanyFiletypeID for some function arguments, but GeanyFiletype is better; we should
27 * only use GeanyFiletype for API functions. */
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include "filetypes.h"
35 #include "app.h"
36 #include "callbacks.h" /* FIXME: for ignore_callback */
37 #include "document.h"
38 #include "filetypesprivate.h"
39 #include "geany.h"
40 #include "geanyobject.h"
41 #include "highlighting.h"
42 #include "projectprivate.h"
43 #include "sciwrappers.h"
44 #include "support.h"
45 #include "symbols.h"
46 #include "tm_parser.h"
47 #include "utils.h"
48 #include "ui_utils.h"
50 #include <stdlib.h>
51 #include <string.h>
53 #include <glib/gstdio.h>
55 #define GEANY_FILETYPE_SEARCH_LINES 2 /* lines of file to search for filetype */
57 GPtrArray *filetypes_array = NULL;
58 static GHashTable *filetypes_hash = NULL; /* Hash of filetype pointers based on name keys */
59 GSList *filetypes_by_title = NULL;
62 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype);
64 static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft);
65 static void read_filetype_config(void);
66 static void create_set_filetype_menu(gboolean config);
67 static gchar *filetypes_get_filename(GeanyFiletype *ft, gboolean user);
70 enum TitleType
72 TITLE_NONE,
73 TITLE_SOURCE_FILE,
74 TITLE_FILE,
75 TITLE_SCRIPT,
76 TITLE_DOCUMENT
79 /* Save adding many translation strings if the filetype name doesn't need translating */
80 static gchar *filetype_make_title(const char *name, enum TitleType type)
82 g_return_val_if_fail(name != NULL, NULL);
84 switch (type)
86 case TITLE_SOURCE_FILE: return g_strdup_printf(_("%s source file"), name);
87 case TITLE_FILE: return g_strdup_printf(_("%s file"), name);
88 case TITLE_SCRIPT: return g_strdup_printf(_("%s script"), name);
89 case TITLE_DOCUMENT: return g_strdup_printf(_("%s document"), name);
90 case TITLE_NONE: /* fall through */
91 default: return g_strdup(name);
96 /* name argument (ie filetype name) must not be translated as it is used for
97 * filetype lookup. Use filetypes_get_display_name() instead.*/
98 static void ft_init(GeanyFiletypeID ft_id, TMParserType lang, const char *name,
99 const char *title_name, enum TitleType title_type,
100 GeanyFiletypeGroupID group_id)
102 GeanyFiletype *ft = filetypes[ft_id];
103 ft->lang = lang;
104 ft->name = g_strdup(name);
105 ft->title = filetype_make_title((title_name != NULL) ? title_name : ft->name, title_type);
106 ft->group = group_id;
109 /* Evil macro to save typing and make init_builtin_filetypes() more readable */
110 #define FT_INIT(ft_id, parser_id, name, title_name, title_type, group_id) \
111 ft_init(GEANY_FILETYPES_##ft_id, TM_PARSER_##parser_id, name, title_name, \
112 TITLE_##title_type, GEANY_FILETYPE_GROUP_##group_id)
115 /* Note: remember to update HACKING if this function is renamed. */
116 static void init_builtin_filetypes(void)
118 /* Column legend:
119 * [0] = Filetype constant (GEANY_FILETYPES_*)
120 * [1] = CTags parser (TM_PARSER_*)
121 * [2] = Non-translated filetype name (*not* label for display)
122 * [3] = Translatable human filetype title prefix or NULL to use [2]
123 * [4] = Title type (TITLE_*) constant (ex. TITLE_SOURCE_FILE is 'source file' suffix)
124 * [5] = The filetype group constant (GEANY_FILETYPE_GROUP_*)
125 * --------------------------------------------------------------------------------------------------------------------------
126 * [0] [1] [2] [3] [4] [5] */
127 FT_INIT( NONE, NONE, "None", _("None"), NONE, NONE );
128 FT_INIT( C, C, "C", NULL, SOURCE_FILE, COMPILED );
129 FT_INIT( CPP, CPP, "C++", NULL, SOURCE_FILE, COMPILED );
130 FT_INIT( OBJECTIVEC, OBJC, "Objective-C", NULL, SOURCE_FILE, COMPILED );
131 FT_INIT( CS, CSHARP, "C#", NULL, SOURCE_FILE, COMPILED );
132 FT_INIT( VALA, VALA, "Vala", NULL, SOURCE_FILE, COMPILED );
133 FT_INIT( D, D, "D", NULL, SOURCE_FILE, COMPILED );
134 FT_INIT( JAVA, JAVA, "Java", NULL, SOURCE_FILE, COMPILED );
135 FT_INIT( PASCAL, PASCAL, "Pascal", NULL, SOURCE_FILE, COMPILED );
136 FT_INIT( ASM, ASM, "ASM", "Assembler", SOURCE_FILE, COMPILED );
137 FT_INIT( BASIC, FREEBASIC, "FreeBasic", NULL, SOURCE_FILE, COMPILED );
138 FT_INIT( FORTRAN, FORTRAN, "Fortran", "Fortran (F90)", SOURCE_FILE, COMPILED );
139 FT_INIT( F77, FORTRAN, "F77", "Fortran (F77)", SOURCE_FILE, COMPILED );
140 FT_INIT( GLSL, C, "GLSL", NULL, SOURCE_FILE, COMPILED );
141 FT_INIT( CAML, NONE, "CAML", "(O)Caml", SOURCE_FILE, COMPILED );
142 FT_INIT( PERL, PERL, "Perl", NULL, SOURCE_FILE, SCRIPT );
143 FT_INIT( PHP, PHP, "PHP", NULL, SOURCE_FILE, SCRIPT );
144 FT_INIT( JS, JAVASCRIPT, "Javascript", NULL, SOURCE_FILE, SCRIPT );
145 FT_INIT( PYTHON, PYTHON, "Python", NULL, SOURCE_FILE, SCRIPT );
146 FT_INIT( RUBY, RUBY, "Ruby", NULL, SOURCE_FILE, SCRIPT );
147 FT_INIT( TCL, TCLOO, "Tcl", NULL, SOURCE_FILE, SCRIPT );
148 FT_INIT( LUA, LUA, "Lua", NULL, SOURCE_FILE, SCRIPT );
149 FT_INIT( GDSCRIPT, GDSCRIPT, "GDScript", NULL, SOURCE_FILE, SCRIPT );
150 FT_INIT( HASKELL, HASKELL, "Haskell", NULL, SOURCE_FILE, COMPILED );
151 FT_INIT( MARKDOWN, MARKDOWN, "Markdown", NULL, SOURCE_FILE, MARKUP );
152 FT_INIT( TXT2TAGS, TXT2TAGS, "Txt2tags", NULL, SOURCE_FILE, MARKUP );
153 FT_INIT( ABC, ABC, "Abc", NULL, FILE, MISC );
154 FT_INIT( SH, SH, "Sh", _("Shell"), SCRIPT, SCRIPT );
155 FT_INIT( MAKE, MAKEFILE, "Make", _("Makefile"), NONE, SCRIPT );
156 FT_INIT( XML, NONE, "XML", NULL, DOCUMENT, MARKUP );
157 FT_INIT( DOCBOOK, DOCBOOK, "Docbook", NULL, DOCUMENT, MARKUP );
158 FT_INIT( HTML, HTML, "HTML", NULL, DOCUMENT, MARKUP );
159 FT_INIT( CSS, CSS, "CSS", _("Cascading Stylesheet"), NONE, MARKUP ); /* not really markup but fit quite well to HTML */
160 FT_INIT( SQL, SQL, "SQL", NULL, FILE, MISC );
161 FT_INIT( COBOL, COBOL, "COBOL", NULL, SOURCE_FILE, COMPILED );
162 FT_INIT( LATEX, LATEX, "LaTeX", NULL, SOURCE_FILE, MARKUP );
163 FT_INIT( BIBTEX, BIBTEX, "BibTeX", NULL, SOURCE_FILE, MARKUP );
164 FT_INIT( VHDL, VHDL, "VHDL", NULL, SOURCE_FILE, COMPILED );
165 FT_INIT( VERILOG, VERILOG, "Verilog", NULL, SOURCE_FILE, COMPILED );
166 FT_INIT( DIFF, DIFF, "Diff", NULL, FILE, MISC );
167 FT_INIT( LISP, LISP, "Lisp", NULL, SOURCE_FILE, SCRIPT );
168 FT_INIT( ERLANG, ERLANG, "Erlang", NULL, SOURCE_FILE, COMPILED );
169 FT_INIT( CONF, CONF, "Conf", _("Config"), FILE, MISC );
170 FT_INIT( PO, NONE, "Po", _("Gettext translation"), FILE, MISC );
171 FT_INIT( HAXE, HAXE, "Haxe", NULL, SOURCE_FILE, COMPILED );
172 FT_INIT( AS, ACTIONSCRIPT, "ActionScript", NULL, SOURCE_FILE, SCRIPT );
173 FT_INIT( R, R, "R", NULL, SOURCE_FILE, SCRIPT );
174 FT_INIT( REST, REST, "reStructuredText", NULL, SOURCE_FILE, MARKUP );
175 FT_INIT( MATLAB, MATLAB, "Matlab/Octave", NULL, SOURCE_FILE, SCRIPT );
176 FT_INIT( YAML, NONE, "YAML", NULL, FILE, MISC );
177 FT_INIT( CMAKE, NONE, "CMake", NULL, SOURCE_FILE, SCRIPT );
178 FT_INIT( NSIS, NSIS, "NSIS", NULL, SOURCE_FILE, SCRIPT );
179 FT_INIT( ADA, ADA, "Ada", NULL, SOURCE_FILE, COMPILED );
180 FT_INIT( FORTH, NONE, "Forth", NULL, SOURCE_FILE, SCRIPT );
181 FT_INIT( ASCIIDOC, ASCIIDOC, "Asciidoc", NULL, SOURCE_FILE, MARKUP );
182 FT_INIT( ABAQUS, ABAQUS, "Abaqus", NULL, SOURCE_FILE, SCRIPT );
183 FT_INIT( BATCH, BATCH, "Batch", NULL, SCRIPT, SCRIPT );
184 FT_INIT( POWERSHELL, POWERSHELL, "PowerShell", NULL, SOURCE_FILE, SCRIPT );
185 FT_INIT( RUST, RUST, "Rust", NULL, SOURCE_FILE, COMPILED );
186 FT_INIT( COFFEESCRIPT, NONE, "CoffeeScript", NULL, SOURCE_FILE, SCRIPT );
187 FT_INIT( GO, GO, "Go", NULL, SOURCE_FILE, COMPILED );
188 FT_INIT( ZEPHIR, ZEPHIR, "Zephir", NULL, SOURCE_FILE, COMPILED );
189 FT_INIT( SMALLTALK, NONE, "Smalltalk", NULL, SOURCE_FILE, SCRIPT );
190 FT_INIT( JULIA, JULIA, "Julia", NULL, SOURCE_FILE, SCRIPT );
191 FT_INIT( AU3, AUTOIT, "AutoIt", NULL, SCRIPT, SCRIPT );
195 /* initialize fields. */
196 static GeanyFiletype *filetype_new(void)
198 GeanyFiletype *ft = g_new0(GeanyFiletype, 1);
200 ft->group = GEANY_FILETYPE_GROUP_NONE;
201 ft->lang = TM_PARSER_NONE; /* assume no tagmanager parser */
202 /* pattern must not be null */
203 ft->pattern = g_new0(gchar*, 1);
204 ft->indent_width = -1;
205 ft->indent_type = -1;
207 ft->priv = g_new0(GeanyFiletypePrivate, 1);
208 ft->priv->project_list_entry = -1; /* no entry */
210 return ft;
214 static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2, gpointer data)
216 gboolean by_name = GPOINTER_TO_INT(data);
217 const GeanyFiletype *ft1 = pft1, *ft2 = pft2;
219 if (G_UNLIKELY(ft1->id == GEANY_FILETYPES_NONE))
220 return -1;
221 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
222 return 1;
224 return by_name ?
225 utils_str_casecmp(ft1->name, ft2->name) :
226 utils_str_casecmp(ft1->title, ft2->title);
230 /** Gets a list of filetype pointers sorted by name.
231 * The list does not change on subsequent calls.
232 * @return @elementtype{GeanyFiletype} @transfer{none} The list - do not free.
233 * @see filetypes_by_title. */
234 GEANY_API_SYMBOL
235 const GSList *filetypes_get_sorted_by_name(void)
237 static GSList *list = NULL;
239 g_return_val_if_fail(filetypes_by_title, NULL);
241 if (!list)
243 list = g_slist_copy(filetypes_by_title);
244 list = g_slist_sort_with_data(list, cmp_filetype, GINT_TO_POINTER(TRUE));
246 return list;
250 /* Add a filetype pointer to the lists of available filetypes,
251 * and set the filetype::id field. */
252 static void filetype_add(GeanyFiletype *ft)
254 g_return_if_fail(ft);
255 g_return_if_fail(ft->name);
257 ft->id = filetypes_array->len; /* len will be the index for filetype_array */
258 g_ptr_array_add(filetypes_array, ft);
259 g_hash_table_insert(filetypes_hash, ft->name, ft);
261 /* list will be sorted later */
262 filetypes_by_title = g_slist_prepend(filetypes_by_title, ft);
266 static void add_custom_filetype(const gchar *filename)
268 gchar *fn = utils_strdupa(strstr(filename, ".") + 1);
269 gchar *dot = g_strrstr(fn, ".conf");
270 GeanyFiletype *ft;
272 g_return_if_fail(dot);
274 *dot = 0x0;
276 if (g_hash_table_lookup(filetypes_hash, fn))
277 return;
279 ft = filetype_new();
280 ft->name = g_strdup(fn);
281 ft->title = filetype_make_title(ft->name, TITLE_FILE);
282 ft->priv->custom = TRUE;
283 filetype_add(ft);
284 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
288 static void init_custom_filetypes(const gchar *path)
290 GDir *dir;
291 const gchar *filename;
293 g_return_if_fail(path);
295 dir = g_dir_open(path, 0, NULL);
296 if (dir == NULL)
297 return;
299 foreach_dir(filename, dir)
301 const gchar prefix[] = "filetypes.";
303 if (g_str_has_prefix(filename, prefix) &&
304 g_str_has_suffix(filename + strlen(prefix), ".conf"))
306 add_custom_filetype(filename);
309 g_dir_close(dir);
313 /* Create the filetypes array and fill it with the known filetypes.
314 * Warning: GTK isn't necessarily initialized yet. */
315 void filetypes_init_types(void)
317 GeanyFiletypeID ft_id;
318 gchar *f;
320 g_return_if_fail(filetypes_array == NULL);
321 g_return_if_fail(filetypes_hash == NULL);
323 filetypes_array = g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES);
324 filetypes_hash = g_hash_table_new(g_str_hash, g_str_equal);
326 /* Create built-in filetypes */
327 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
329 filetypes[ft_id] = filetype_new();
331 init_builtin_filetypes();
333 /* Add built-in filetypes to the hash now the name fields are set */
334 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
336 filetype_add(filetypes[ft_id]);
338 f = g_build_filename(app->datadir, GEANY_FILEDEFS_SUBDIR, NULL);
339 init_custom_filetypes(f);
340 g_free(f);
341 f = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, NULL);
342 init_custom_filetypes(f);
343 g_free(f);
345 /* sort last instead of on insertion to prevent exponential time */
346 filetypes_by_title = g_slist_sort_with_data(filetypes_by_title,
347 cmp_filetype, GINT_TO_POINTER(FALSE));
349 read_filetype_config();
353 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
355 gchar *f, *basename;
357 g_return_if_fail(!EMPTY(doc->real_path));
359 f = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
360 if (utils_str_equal(doc->real_path, f))
361 filetypes_reload_extensions();
362 g_free(f);
364 basename = g_path_get_basename(doc->real_path);
365 if (g_str_has_prefix(basename, "filetypes."))
367 guint i;
369 for (i = 0; i < filetypes_array->len; i++)
371 GeanyFiletype *ft = filetypes[i];
373 f = filetypes_get_filename(ft, TRUE);
374 if (utils_str_equal(doc->real_path, f))
376 guint j;
378 /* Note: we don't reload other filetypes, even though the named styles may have changed.
379 * The user can do this manually with 'Tools->Reload Configuration' */
380 filetypes_load_config(i, TRUE);
382 foreach_document(j)
383 document_reload_config(documents[j]);
385 g_free(f);
386 break;
388 g_free(f);
391 g_free(basename);
395 static void setup_config_file_menus(void)
397 gchar *f;
399 f = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
400 ui_add_config_file_menu_item(f, NULL, NULL);
401 SETPTR(f, g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL));
402 ui_add_config_file_menu_item(f, NULL, NULL);
403 g_free(f);
405 create_set_filetype_menu(TRUE);
407 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
411 static GtkWidget *create_sub_menu(GtkWidget *parent, const gchar *title)
413 GtkWidget *menu, *item;
415 menu = gtk_menu_new();
416 item = gtk_menu_item_new_with_mnemonic((title));
417 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
418 gtk_container_add(GTK_CONTAINER(parent), item);
419 gtk_widget_show(item);
421 return menu;
425 static void create_set_filetype_menu(gboolean config)
427 GtkWidget *group_menus[GEANY_FILETYPE_GROUP_COUNT] = {NULL};
428 GSList *node;
429 GtkWidget *menu;
431 menu = config ? ui_widgets.config_files_filetype_menu :
432 ui_lookup_widget(main_widgets.window, "set_filetype1_menu");
434 group_menus[GEANY_FILETYPE_GROUP_COMPILED] = create_sub_menu(menu, _("_Programming Languages"));
435 group_menus[GEANY_FILETYPE_GROUP_SCRIPT] = create_sub_menu(menu, _("_Scripting Languages"));
436 group_menus[GEANY_FILETYPE_GROUP_MARKUP] = create_sub_menu(menu, _("_Markup Languages"));
437 group_menus[GEANY_FILETYPE_GROUP_MISC] = create_sub_menu(menu, _("M_iscellaneous"));
439 /* Append all filetypes to the menu */
440 foreach_slist(node, filetypes_by_title)
442 GeanyFiletype *ft = node->data;
443 GtkWidget *parent = (ft->group != GEANY_FILETYPE_GROUP_NONE) ? group_menus[ft->group] : menu;
445 /* we already have filetypes.common config entry */
446 if (config && ft->id == GEANY_FILETYPES_NONE)
447 continue;
449 if (config)
451 gchar *filename = filetypes_get_filename(ft, TRUE);
453 ui_add_config_file_menu_item(filename, NULL, GTK_CONTAINER(parent));
454 g_free(filename);
456 else
457 create_radio_menu_item(parent, ft);
462 void filetypes_init(void)
464 filetypes_init_types();
465 create_set_filetype_menu(FALSE);
466 setup_config_file_menus();
470 static guint match_basename(const GeanyFiletype *ft, const gchar *base_filename)
472 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
473 return 0;
475 for (guint j = 0; ft->pattern[j] != NULL; j++)
477 gchar *pat = ft->pattern[j];
479 if (g_pattern_match_simple(pat, base_filename))
481 return strlen(pat);
484 return 0;
488 static GeanyFiletype *detect_filetype_conf_file(const gchar *utf8_filename)
490 gchar *lfn = NULL;
491 gchar *path;
492 gboolean found = FALSE;
494 #ifdef G_OS_WIN32
495 /* use lower case basename */
496 lfn = g_utf8_strdown(utf8_filename, -1);
497 #else
498 lfn = g_strdup(utf8_filename);
499 #endif
500 SETPTR(lfn, utils_get_locale_from_utf8(lfn));
502 path = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL);
503 if (g_str_has_prefix(lfn, path))
504 found = TRUE;
506 SETPTR(path, g_build_filename(app->datadir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL));
507 if (g_str_has_prefix(lfn, path))
508 found = TRUE;
510 g_free(path);
511 g_free(lfn);
512 return found ? filetypes[GEANY_FILETYPES_CONF] : NULL;
516 /* Detect filetype only based on the filename extension.
517 * utf8_filename can include the full path.
518 * Returns: non-NULL */
519 GeanyFiletype *filetypes_detect_from_extension(const gchar *utf8_filename)
521 gchar *base_filename;
522 GeanyFiletype *ft;
523 guint plen = 0;
525 ft = detect_filetype_conf_file(utf8_filename);
526 if (ft)
527 return ft;
529 /* to match against the basename of the file (because of Makefile*) */
530 base_filename = g_path_get_basename(utf8_filename);
531 #ifdef G_OS_WIN32
532 /* use lower case basename */
533 SETPTR(base_filename, g_utf8_strdown(base_filename, -1));
534 #endif
536 for (guint i = 0; i < filetypes_array->len; i++)
538 guint mlen = match_basename(filetypes[i], base_filename);
540 if (mlen > plen)
541 { // longest pattern match wins
542 plen = mlen;
543 ft = filetypes[i];
545 else if (mlen == plen && ft && !ft->priv->user_extensions &&
546 filetypes[i]->priv->user_extensions)
547 { // user config overrides system if pattern len same
548 ft = filetypes[i];
551 if (ft == NULL)
552 ft = filetypes[GEANY_FILETYPES_NONE];
554 g_free(base_filename);
555 return ft;
559 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
560 * terminated by -1.
561 * The detected filetype of the file is checked against every id in the passed list and if
562 * there is a match, TRUE is returned. */
563 static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first, ...)
565 GeanyFiletype *ft = NULL;
566 gint test;
567 gboolean result = FALSE;
568 va_list args;
570 ft = filetypes_detect_from_extension(utf8_filename);
571 if (ft == NULL || ft->id >= filetypes_array->len)
572 return FALSE;
574 va_start(args, first);
575 test = first;
576 while (1)
578 if (test == -1)
579 break;
581 if (ft->id == (guint) test)
583 result = TRUE;
584 break;
586 test = va_arg(args, gint);
588 va_end(args);
590 return result;
594 static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line)
596 GeanyFiletype *ft = NULL;
598 if (strlen(line) > 2 && line[0] == '#' && line[1] == '!')
600 static const struct {
601 const gchar *name;
602 GeanyFiletypeID filetype;
603 } intepreter_map[] = {
604 { "sh", GEANY_FILETYPES_SH },
605 { "bash", GEANY_FILETYPES_SH },
606 { "dash", GEANY_FILETYPES_SH },
607 { "perl", GEANY_FILETYPES_PERL },
608 { "python", GEANY_FILETYPES_PYTHON },
609 { "php", GEANY_FILETYPES_PHP },
610 { "ruby", GEANY_FILETYPES_RUBY },
611 { "tcl", GEANY_FILETYPES_TCL },
612 { "make", GEANY_FILETYPES_MAKE },
613 { "zsh", GEANY_FILETYPES_SH },
614 { "ksh", GEANY_FILETYPES_SH },
615 { "mksh", GEANY_FILETYPES_SH },
616 { "csh", GEANY_FILETYPES_SH },
617 { "tcsh", GEANY_FILETYPES_SH },
618 { "ash", GEANY_FILETYPES_SH },
619 { "dmd", GEANY_FILETYPES_D },
620 { "wish", GEANY_FILETYPES_TCL },
621 { "node", GEANY_FILETYPES_JS },
622 { "rust", GEANY_FILETYPES_RUST }
624 gchar *tmp = g_path_get_basename(line + 2);
625 gchar *basename_interpreter = tmp;
626 guint i;
628 if (g_str_has_prefix(tmp, "env "))
629 { /* skip "env" and read the following interpreter */
630 basename_interpreter += 4;
633 for (i = 0; ! ft && i < G_N_ELEMENTS(intepreter_map); i++)
635 if (g_str_has_prefix(basename_interpreter, intepreter_map[i].name))
636 ft = filetypes[intepreter_map[i].filetype];
638 g_free(tmp);
640 /* detect HTML files */
641 if (g_str_has_prefix(line, "<!DOCTYPE html") || g_str_has_prefix(line, "<html"))
643 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
644 * extension and use the detected filetype, else assume HTML */
645 if (! shebang_find_and_match_filetype(utf8_filename,
646 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
648 ft = filetypes[GEANY_FILETYPES_HTML];
651 /* detect XML files */
652 else if (utf8_filename && g_str_has_prefix(line, "<?xml"))
654 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
655 * extension and use the detected filetype, else assume XML */
656 if (! shebang_find_and_match_filetype(utf8_filename,
657 GEANY_FILETYPES_HTML, GEANY_FILETYPES_DOCBOOK,
658 /* Perl, Python and PHP only to be safe */
659 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
661 ft = filetypes[GEANY_FILETYPES_XML];
664 else if (g_str_has_prefix(line, "<?php"))
666 ft = filetypes[GEANY_FILETYPES_PHP];
668 return ft;
672 /* Detect the filetype checking for a shebang, then filename extension.
673 * @lines: an strv of the lines to scan (must containing at least one line) */
674 static GeanyFiletype *filetypes_detect_from_file_internal(const gchar *utf8_filename,
675 gchar **lines)
677 GeanyFiletype *ft;
678 gint i;
679 GRegex *ft_regex;
680 GMatchInfo *match;
681 GError *regex_error = NULL;
683 /* try to find a shebang and if found use it prior to the filename extension
684 * also checks for <?xml */
685 ft = find_shebang(utf8_filename, lines[0]);
686 if (ft != NULL)
687 return ft;
689 /* try to extract the filetype using a regex capture */
690 ft_regex = g_regex_new(file_prefs.extract_filetype_regex,
691 G_REGEX_RAW | G_REGEX_MULTILINE, 0, &regex_error);
692 if (ft_regex != NULL)
694 for (i = 0; ft == NULL && lines[i] != NULL; i++)
696 if (g_regex_match(ft_regex, lines[i], 0, &match))
698 gchar *capture = g_match_info_fetch(match, 1);
699 if (capture != NULL)
701 ft = filetypes_lookup_by_name(capture);
702 g_free(capture);
705 g_match_info_free(match);
707 g_regex_unref(ft_regex);
709 else if (regex_error != NULL)
711 geany_debug("Filetype extract regex ignored: %s", regex_error->message);
712 g_error_free(regex_error);
714 if (ft != NULL)
715 return ft;
717 if (utf8_filename == NULL)
718 return filetypes[GEANY_FILETYPES_NONE];
720 return filetypes_detect_from_extension(utf8_filename);
724 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
725 GeanyFiletype *filetypes_detect_from_document(GeanyDocument *doc)
727 GeanyFiletype *ft;
728 gchar *lines[GEANY_FILETYPE_SEARCH_LINES + 1];
729 gint i;
731 g_return_val_if_fail(doc == NULL || doc->is_valid, filetypes[GEANY_FILETYPES_NONE]);
733 if (doc == NULL)
734 return filetypes[GEANY_FILETYPES_NONE];
736 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
738 lines[i] = sci_get_line(doc->editor->sci, i);
740 lines[i] = NULL;
741 ft = filetypes_detect_from_file_internal(doc->file_name, lines);
742 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
744 g_free(lines[i]);
746 return ft;
750 #ifdef HAVE_PLUGINS
751 /* Currently only used by external plugins (e.g. geanyprj). */
753 * Detects filetype based on a shebang line in the file or the filename extension.
755 * @param utf8_filename The filename in UTF-8 encoding.
757 * @return @transfer{none} The detected filetype for @a utf8_filename or
758 * @c filetypes[GEANY_FILETYPES_NONE] if it could not be detected.
760 GEANY_API_SYMBOL
761 GeanyFiletype *filetypes_detect_from_file(const gchar *utf8_filename)
763 gchar line[1024];
764 gchar *lines[2];
765 FILE *f;
766 gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
768 f = g_fopen(locale_name, "r");
769 g_free(locale_name);
770 if (f != NULL)
772 if (fgets(line, sizeof(line), f) != NULL)
774 fclose(f);
775 lines[0] = line;
776 lines[1] = NULL;
777 return filetypes_detect_from_file_internal(utf8_filename, lines);
779 fclose(f);
781 return filetypes_detect_from_extension(utf8_filename);
783 #endif
786 void filetypes_select_radio_item(const GeanyFiletype *ft)
788 /* ignore_callback has to be set by the caller */
789 g_return_if_fail(ignore_callback);
791 if (ft == NULL)
792 ft = filetypes[GEANY_FILETYPES_NONE];
794 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
798 static void
799 on_filetype_change(GtkCheckMenuItem *menuitem, gpointer user_data)
801 GeanyDocument *doc = document_get_current();
802 if (ignore_callback || doc == NULL || ! gtk_check_menu_item_get_active(menuitem))
803 return;
805 document_set_filetype(doc, (GeanyFiletype*)user_data);
809 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
811 static GSList *group = NULL;
812 GtkWidget *tmp;
814 tmp = gtk_radio_menu_item_new_with_label(group, ftype->title);
815 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp));
816 ftype->priv->menu_item = tmp;
817 gtk_widget_show(tmp);
818 gtk_container_add(GTK_CONTAINER(menu), tmp);
819 g_signal_connect(tmp, "activate", G_CALLBACK(on_filetype_change), (gpointer) ftype);
823 static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
825 GeanyFiletype *ft = data;
827 g_return_if_fail(ft != NULL);
829 g_free(ft->name);
830 g_free(ft->title);
831 g_free(ft->extension);
832 g_free(ft->mime_type);
833 g_free(ft->comment_open);
834 g_free(ft->comment_close);
835 g_free(ft->comment_single);
836 g_free(ft->context_action_cmd);
837 g_free(ft->priv->filecmds);
838 g_free(ft->priv->ftdefcmds);
839 g_free(ft->priv->execcmds);
840 g_free(ft->error_regex_string);
841 if (ft->icon)
842 g_object_unref(ft->icon);
843 g_strfreev(ft->pattern);
845 if (ft->priv->error_regex)
846 g_regex_unref(ft->priv->error_regex);
847 g_slist_foreach(ft->priv->tag_files, (GFunc) g_free, NULL);
848 g_slist_free(ft->priv->tag_files);
850 g_free(ft->priv);
851 g_free(ft);
855 /* frees the array and all related pointers */
856 void filetypes_free_types(void)
858 g_return_if_fail(filetypes_array != NULL);
859 g_return_if_fail(filetypes_hash != NULL);
861 g_ptr_array_foreach(filetypes_array, filetype_free, NULL);
862 g_ptr_array_free(filetypes_array, TRUE);
863 g_hash_table_destroy(filetypes_hash);
867 static void load_indent_settings(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
869 ft->indent_width = utils_get_setting(integer, configh, config, "indentation", "width", -1);
870 ft->indent_type = utils_get_setting(integer, configh, config, "indentation", "type", -1);
871 /* check whether the indent type is OK */
872 switch (ft->indent_type)
874 case GEANY_INDENT_TYPE_TABS:
875 case GEANY_INDENT_TYPE_SPACES:
876 case GEANY_INDENT_TYPE_BOTH:
877 case -1:
878 break;
880 default:
881 g_warning("Invalid indent type %d in file type %s", ft->indent_type, ft->name);
882 ft->indent_type = -1;
883 break;
888 static void load_settings(guint ft_id, GKeyFile *config, GKeyFile *configh)
890 GeanyFiletype *ft = filetypes[ft_id];
891 gchar *result;
893 /* default extension */
894 result = utils_get_setting(string, configh, config, "settings", "extension", NULL);
895 if (result != NULL)
897 SETPTR(filetypes[ft_id]->extension, result);
900 /* MIME type */
901 result = utils_get_setting(string, configh, config, "settings", "mime_type", "text/plain");
902 SETPTR(filetypes[ft_id]->mime_type, result);
904 /* read comment notes */
905 result = utils_get_setting(string, configh, config, "settings", "comment_open", NULL);
906 if (result != NULL)
908 SETPTR(filetypes[ft_id]->comment_open, result);
911 result = utils_get_setting(string, configh, config, "settings", "comment_close", NULL);
912 if (result != NULL)
914 SETPTR(filetypes[ft_id]->comment_close, result);
917 result = utils_get_setting(string, configh, config, "settings", "comment_single", NULL);
918 if (result != NULL)
920 SETPTR(filetypes[ft_id]->comment_single, result);
922 /* import correctly filetypes that use old-style single comments */
923 else if (EMPTY(filetypes[ft_id]->comment_close))
925 SETPTR(filetypes[ft_id]->comment_single, filetypes[ft_id]->comment_open);
926 filetypes[ft_id]->comment_open = NULL;
929 filetypes[ft_id]->comment_use_indent = utils_get_setting(boolean, configh, config,
930 "settings", "comment_use_indent", FALSE);
932 /* read context action */
933 result = utils_get_setting(string, configh, config, "settings", "context_action_cmd", NULL);
934 if (result != NULL)
936 SETPTR(filetypes[ft_id]->context_action_cmd, result);
939 result = utils_get_setting(string, configh, config, "settings", "tag_parser", NULL);
940 if (result != NULL)
942 ft->lang = tm_source_file_get_named_lang(result);
943 if (ft->lang == TM_PARSER_NONE)
944 geany_debug("Cannot find tags parser '%s' for custom filetype '%s'.", result, ft->name);
945 g_free(result);
948 result = utils_get_setting(string, configh, config, "settings", "lexer_filetype", NULL);
949 if (result != NULL)
951 ft->lexer_filetype = filetypes_lookup_by_name(result);
952 if (!ft->lexer_filetype)
953 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result, ft->name);
954 g_free(result);
957 ft->priv->symbol_list_sort_mode = utils_get_setting(integer, configh, config, "settings",
958 "symbol_list_sort_mode", SYMBOLS_SORT_USE_PREVIOUS);
959 ft->priv->xml_indent_tags = utils_get_setting(boolean, configh, config, "settings",
960 "xml_indent_tags", FALSE);
962 /* read indent settings */
963 load_indent_settings(ft, config, configh);
965 /* read build settings */
966 build_load_menu(config, GEANY_BCS_FT, (gpointer)ft);
967 build_load_menu(configh, GEANY_BCS_HOME_FT, (gpointer)ft);
971 static void copy_keys(GKeyFile *dest, const gchar *dest_group,
972 GKeyFile *src, const gchar *src_group)
974 gchar **keys = g_key_file_get_keys(src, src_group, NULL, NULL);
975 gchar **ptr;
977 foreach_strv(ptr, keys)
979 gchar *key = *ptr;
980 gchar *value = g_key_file_get_value(src, src_group, key, NULL);
982 g_key_file_set_value(dest, dest_group, key, value);
983 g_free(value);
985 g_strfreev(keys);
989 static gchar *filetypes_get_filename(GeanyFiletype *ft, gboolean user)
991 gchar *ext = filetypes_get_conf_extension(ft);
992 gchar *base_name = g_strconcat("filetypes.", ext, NULL);
993 gchar *file_name;
995 if (user)
996 file_name = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, base_name, NULL);
997 else
998 file_name = g_build_filename(app->datadir, GEANY_FILEDEFS_SUBDIR, base_name, NULL);
1000 g_free(ext);
1001 g_free(base_name);
1003 return file_name;
1007 static void add_group_keys(GKeyFile *kf, const gchar *group, GeanyFiletype *ft)
1009 gchar *files[2];
1010 gboolean loaded = FALSE;
1011 guint i;
1013 files[0] = filetypes_get_filename(ft, FALSE);
1014 files[1] = filetypes_get_filename(ft, TRUE);
1016 for (i = 0; i < G_N_ELEMENTS(files); i++)
1018 GKeyFile *src = g_key_file_new();
1020 if (g_key_file_load_from_file(src, files[i], G_KEY_FILE_NONE, NULL))
1022 copy_keys(kf, group, src, group);
1023 loaded = TRUE;
1025 g_key_file_free(src);
1028 if (!loaded)
1029 geany_debug("Could not read config file %s for [%s=%s]!", files[0], group, ft->name);
1031 g_free(files[0]);
1032 g_free(files[1]);
1036 static void copy_ft_groups(GKeyFile *kf)
1038 gchar **groups = g_key_file_get_groups(kf, NULL);
1039 gchar **ptr;
1041 foreach_strv(ptr, groups)
1043 gchar *group = *ptr;
1044 gchar *old_group;
1045 gchar *name = strchr(*ptr, '=');
1046 GeanyFiletype *ft;
1048 if (!name || !name[1]) /* no name or no parent name */
1049 continue;
1051 old_group = g_strdup(group);
1053 /* terminate group at '=' */
1054 *name = 0;
1055 name++;
1057 ft = filetypes_lookup_by_name(name);
1058 if (ft)
1060 add_group_keys(kf, group, ft);
1061 /* move old group keys (foo=bar) to proper group name (foo) */
1062 copy_keys(kf, group, kf, old_group);
1064 g_free(old_group);
1066 g_strfreev(groups);
1070 /* simple wrapper function to print file errors in DEBUG mode */
1071 static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags,
1072 GeanyFiletype *ft)
1074 GError *error = NULL;
1075 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1077 if (error != NULL)
1079 if (!done && !ft->priv->custom)
1080 geany_debug("Failed to open %s (%s)", file, error->message);
1082 g_error_free(error);
1083 error = NULL;
1088 /* Load the configuration file for the associated filetype id.
1089 * This should only be called when the filetype is needed, to save loading
1090 * 20+ configuration files all at once. */
1091 void filetypes_load_config(guint ft_id, gboolean reload)
1093 GKeyFile *config, *config_home;
1094 GeanyFiletypePrivate *pft;
1095 GeanyFiletype *ft;
1097 g_return_if_fail(ft_id < filetypes_array->len);
1099 ft = filetypes[ft_id];
1100 pft = ft->priv;
1102 /* when reloading, proceed only if the settings were already loaded */
1103 if (G_UNLIKELY(reload && ! pft->keyfile_loaded))
1104 return;
1106 /* when not reloading, load the settings only once */
1107 if (G_LIKELY(! reload && pft->keyfile_loaded))
1108 return;
1109 pft->keyfile_loaded = TRUE;
1111 config = g_key_file_new();
1112 config_home = g_key_file_new();
1114 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1115 gchar *f;
1117 f = filetypes_get_filename(ft, FALSE);
1118 load_system_keyfile(config, f, G_KEY_FILE_KEEP_COMMENTS, ft);
1120 SETPTR(f, filetypes_get_filename(ft, TRUE));
1121 g_key_file_load_from_file(config_home, f, G_KEY_FILE_KEEP_COMMENTS, NULL);
1122 g_free(f);
1124 /* Copy keys for any groups with [group=C] from system keyfile */
1125 copy_ft_groups(config);
1126 copy_ft_groups(config_home);
1128 load_settings(ft_id, config, config_home);
1129 highlighting_init_styles(ft_id, config, config_home);
1131 if (ft->icon)
1132 g_object_unref(ft->icon);
1133 ft->icon = ui_get_mime_icon(ft->mime_type);
1135 g_key_file_free(config);
1136 g_key_file_free(config_home);
1140 static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft)
1142 gchar *result;
1144 if (ft->priv->custom)
1145 return g_strconcat(ft->name, ".conf", NULL);
1147 /* Handle any special extensions different from lowercase filetype->name */
1148 switch (ft->id)
1150 case GEANY_FILETYPES_CPP: result = g_strdup("cpp"); break;
1151 case GEANY_FILETYPES_CS: result = g_strdup("cs"); break;
1152 case GEANY_FILETYPES_MAKE: result = g_strdup("makefile"); break;
1153 case GEANY_FILETYPES_NONE: result = g_strdup("common"); break;
1154 /* name is Matlab/Octave */
1155 case GEANY_FILETYPES_MATLAB: result = g_strdup("matlab"); break;
1156 /* name is Objective-C, and we don't want the hyphen */
1157 case GEANY_FILETYPES_OBJECTIVEC: result = g_strdup("objectivec"); break;
1158 default:
1159 result = g_ascii_strdown(ft->name, -1);
1160 break;
1162 return result;
1166 void filetypes_save_commands(GeanyFiletype *ft)
1168 GKeyFile *config_home;
1169 gchar *fname, *data;
1171 fname = filetypes_get_filename(ft, TRUE);
1172 config_home = g_key_file_new();
1173 g_key_file_load_from_file(config_home, fname, G_KEY_FILE_KEEP_COMMENTS, NULL);
1174 build_save_menu(config_home, ft, GEANY_BCS_HOME_FT);
1175 data = g_key_file_to_data(config_home, NULL, NULL);
1176 utils_write_file(fname, data);
1177 g_free(data);
1178 g_key_file_free(config_home);
1179 g_free(fname);
1183 /* create one file filter which has each file pattern of each filetype */
1184 GtkFileFilter *filetypes_create_file_filter_all_source(void)
1186 GtkFileFilter *new_filter;
1187 guint i, j;
1189 new_filter = gtk_file_filter_new();
1190 gtk_file_filter_set_name(new_filter, _("All Source"));
1192 for (i = 0; i < filetypes_array->len; i++)
1194 if (G_UNLIKELY(i == GEANY_FILETYPES_NONE))
1195 continue;
1197 for (j = 0; filetypes[i]->pattern[j]; j++)
1199 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1202 return new_filter;
1206 GtkFileFilter *filetypes_create_file_filter(const GeanyFiletype *ft)
1208 GtkFileFilter *new_filter;
1209 gint i;
1210 const gchar *title;
1212 g_return_val_if_fail(ft != NULL, NULL);
1214 new_filter = gtk_file_filter_new();
1215 title = ft->id == GEANY_FILETYPES_NONE ? _("All files") : ft->title;
1216 gtk_file_filter_set_name(new_filter, title);
1218 for (i = 0; ft->pattern[i]; i++)
1220 gtk_file_filter_add_pattern(new_filter, ft->pattern[i]);
1223 return new_filter;
1227 /* Indicates whether there is a tag parser for the filetype or not.
1228 * Only works for custom filetypes if the filetype settings have been loaded. */
1229 gboolean filetype_has_tags(GeanyFiletype *ft)
1231 g_return_val_if_fail(ft != NULL, FALSE);
1233 return ft->lang != TM_PARSER_NONE;
1237 /** Finds a filetype pointer from its @a name field.
1238 * @param name Filetype name.
1239 * @return @transfer{none} @nullable The filetype found, or @c NULL.
1241 * @since 0.15
1243 GEANY_API_SYMBOL
1244 GeanyFiletype *filetypes_lookup_by_name(const gchar *name)
1246 GeanyFiletype *ft;
1248 g_return_val_if_fail(!EMPTY(name), NULL);
1250 ft = g_hash_table_lookup(filetypes_hash, name);
1251 if (G_UNLIKELY(ft == NULL))
1252 geany_debug("Could not find filetype '%s'.", name);
1253 return ft;
1257 static void compile_regex(GeanyFiletype *ft, gchar *regstr)
1259 GError *error = NULL;
1260 GRegex *regex = g_regex_new(regstr, 0, 0, &error);
1262 if (!regex)
1264 ui_set_statusbar(TRUE, _("Bad regex for filetype %s: %s"),
1265 filetypes_get_display_name(ft), error->message);
1266 g_error_free(error);
1268 if (ft->priv->error_regex)
1269 g_regex_unref(ft->priv->error_regex);
1270 ft->priv->error_regex = regex;
1274 gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message,
1275 gchar **filename, gint *line)
1277 gchar *regstr;
1278 gchar **tmp;
1279 GeanyDocument *doc;
1280 GMatchInfo *minfo;
1281 gint i, n_match_groups;
1282 gchar *first, *second;
1284 if (ft == NULL)
1286 doc = document_get_current();
1287 if (doc != NULL)
1288 ft = doc->file_type;
1290 tmp = build_get_regex(build_info.grp, ft, NULL);
1291 if (tmp == NULL)
1292 return FALSE;
1293 regstr = *tmp;
1295 *filename = NULL;
1296 *line = -1;
1298 if (G_UNLIKELY(EMPTY(regstr)))
1299 return FALSE;
1301 if (!ft->priv->error_regex || regstr != ft->priv->last_error_pattern)
1303 compile_regex(ft, regstr);
1304 ft->priv->last_error_pattern = regstr;
1306 if (!ft->priv->error_regex)
1307 return FALSE;
1309 if (!g_regex_match(ft->priv->error_regex, message, 0, &minfo))
1311 g_match_info_free(minfo);
1312 return FALSE;
1315 n_match_groups = g_match_info_get_match_count(minfo);
1316 first = second = NULL;
1318 for (i = 1; i < n_match_groups; i++)
1320 gint start_pos;
1322 g_match_info_fetch_pos(minfo, i, &start_pos, NULL);
1323 if (start_pos != -1)
1325 if (first == NULL)
1326 first = g_match_info_fetch(minfo, i);
1327 else
1329 second = g_match_info_fetch(minfo, i);
1330 break;
1335 if (second)
1337 gchar *end;
1338 glong l;
1340 l = strtol(first, &end, 10);
1341 if (*end == '\0') /* first is purely decimals */
1343 *line = l;
1344 g_free(first);
1345 *filename = second;
1347 else
1349 l = strtol(second, &end, 10);
1350 if (*end == '\0')
1352 *line = l;
1353 g_free(second);
1354 *filename = first;
1356 else
1358 g_free(first);
1359 g_free(second);
1363 else
1364 g_free(first);
1366 g_match_info_free(minfo);
1367 return *filename != NULL;
1371 #ifdef G_OS_WIN32
1372 static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1374 guint i;
1375 for (i = 0; i < len; i++)
1377 SETPTR(patterns[i], g_ascii_strdown(patterns[i], -1));
1380 #endif
1383 static void read_extensions(GKeyFile *sysconfig, GKeyFile *userconfig)
1385 guint i;
1386 gsize len = 0;
1388 /* read the keys */
1389 for (i = 0; i < filetypes_array->len; i++)
1391 gboolean userset =
1392 g_key_file_has_key(userconfig, "Extensions", filetypes[i]->name, NULL);
1393 gchar **list = g_key_file_get_string_list(
1394 (userset) ? userconfig : sysconfig, "Extensions", filetypes[i]->name, &len, NULL);
1396 filetypes[i]->priv->user_extensions = userset;
1397 g_strfreev(filetypes[i]->pattern);
1398 /* Note: we allow 'Foo=' to remove all patterns */
1399 if (!list)
1400 list = g_new0(gchar*, 1);
1401 filetypes[i]->pattern = list;
1403 #ifdef G_OS_WIN32
1404 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1405 #endif
1410 static void read_group(GKeyFile *config, const gchar *group_name, GeanyFiletypeGroupID group_id)
1412 gchar **names = g_key_file_get_string_list(config, "Groups", group_name, NULL, NULL);
1413 gchar **name;
1415 foreach_strv(name, names)
1417 GeanyFiletype *ft = filetypes_lookup_by_name(*name);
1419 if (ft)
1421 ft->group = group_id;
1422 if (ft->priv->custom &&
1423 (group_id == GEANY_FILETYPE_GROUP_COMPILED || group_id == GEANY_FILETYPE_GROUP_SCRIPT))
1425 SETPTR(ft->title, filetype_make_title(ft->name, TITLE_SOURCE_FILE));
1428 else
1429 geany_debug("Filetype '%s' not found for group '%s'!", *name, group_name);
1431 g_strfreev(names);
1435 static void read_groups(GKeyFile *config)
1437 read_group(config, "Programming", GEANY_FILETYPE_GROUP_COMPILED);
1438 read_group(config, "Script", GEANY_FILETYPE_GROUP_SCRIPT);
1439 read_group(config, "Markup", GEANY_FILETYPE_GROUP_MARKUP);
1440 read_group(config, "Misc", GEANY_FILETYPE_GROUP_MISC);
1441 read_group(config, "None", GEANY_FILETYPE_GROUP_NONE);
1445 static void read_filetype_config(void)
1447 gchar *sysconfigfile = g_build_filename(app->datadir, "filetype_extensions.conf", NULL);
1448 gchar *userconfigfile = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
1449 GKeyFile *sysconfig = g_key_file_new();
1450 GKeyFile *userconfig = g_key_file_new();
1452 g_key_file_load_from_file(sysconfig, sysconfigfile, G_KEY_FILE_NONE, NULL);
1453 g_key_file_load_from_file(userconfig, userconfigfile, G_KEY_FILE_NONE, NULL);
1455 read_extensions(sysconfig, userconfig);
1456 read_groups(sysconfig);
1457 read_groups(userconfig);
1459 g_free(sysconfigfile);
1460 g_free(userconfigfile);
1461 g_key_file_free(sysconfig);
1462 g_key_file_free(userconfig);
1466 void filetypes_reload_extensions(void)
1468 guint i;
1470 read_filetype_config();
1472 /* Redetect filetype of any documents with none set */
1473 foreach_document(i)
1475 GeanyDocument *doc = documents[i];
1476 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1477 continue;
1478 document_set_filetype(doc, filetypes_detect_from_document(doc));
1483 /** Accessor function for @ref GeanyData::filetypes_array items.
1484 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1485 * @param idx @c filetypes_array index.
1486 * @return @transfer{none} @nullable The filetype, or @c NULL if @a idx is out of range.
1488 * @since 0.16
1490 GEANY_API_SYMBOL
1491 GeanyFiletype *filetypes_index(gint idx)
1493 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1497 void filetypes_reload(void)
1499 guint i;
1500 GeanyDocument *current_doc;
1502 /* reload filetype configs */
1503 for (i = 0; i < filetypes_array->len; i++)
1505 /* filetypes_load_config() will skip not loaded filetypes */
1506 filetypes_load_config(i, TRUE);
1509 current_doc = document_get_current();
1510 if (!current_doc)
1511 return;
1513 /* update document styling */
1514 foreach_document(i)
1516 if (current_doc != documents[i])
1517 document_reload_config(documents[i]);
1519 /* process the current document at last */
1520 document_reload_config(current_doc);
1524 /** Gets @c ft->name or a translation for filetype None.
1525 * @param ft .
1526 * @return .
1527 * @since Geany 0.20 */
1528 GEANY_API_SYMBOL
1529 const gchar *filetypes_get_display_name(GeanyFiletype *ft)
1531 return ft->id == GEANY_FILETYPES_NONE ? _("None") : ft->name;
1535 /* gets comment_open/comment_close/comment_single strings from the filetype
1536 * @param single_first: whether single comment is preferred if both available
1537 * returns true if at least comment_open is set, false otherwise */
1538 gboolean filetype_get_comment_open_close(const GeanyFiletype *ft, gboolean single_first,
1539 const gchar **co, const gchar **cc)
1541 g_return_val_if_fail(ft != NULL, FALSE);
1542 g_return_val_if_fail(co != NULL, FALSE);
1543 g_return_val_if_fail(cc != NULL, FALSE);
1545 if (single_first)
1547 *co = ft->comment_single;
1548 if (!EMPTY(*co))
1549 *cc = NULL;
1550 else
1552 *co = ft->comment_open;
1553 *cc = ft->comment_close;
1556 else
1558 *co = ft->comment_open;
1559 if (!EMPTY(*co))
1560 *cc = ft->comment_close;
1561 else
1563 *co = ft->comment_single;
1564 *cc = NULL;
1568 return !EMPTY(*co);
1571 static void *copy_(void *src) { return src; }
1572 static void free_(void *doc) { }
1574 /** @gironly
1575 * Gets the GType of GeanyFiletype
1577 * @return the GeanyFiletype type */
1578 GEANY_API_SYMBOL
1579 GType filetype_get_type (void);
1581 G_DEFINE_BOXED_TYPE(GeanyFiletype, filetype, copy_, free_);