Put Makefile comments at start of line.
[geany-mirror.git] / src / filetypes.c
blob4650c2b27a65bfe61eb0393a956c5c256b98fd01
1 /*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /**
23 * @file filetypes.h
24 * Filetype detection, file extensions and filetype menu items.
27 /* Note: we use filetype_id for some function arguments, but GeanyFiletype is better; we should
28 * only use GeanyFiletype for API functions. */
30 #include <string.h>
31 #include <glib/gstdio.h>
33 #include "geany.h"
34 #include "filetypes.h"
35 #include "filetypesprivate.h"
36 #include "highlighting.h"
37 #include "support.h"
38 #include "templates.h"
39 #include "document.h"
40 #include "editor.h"
41 #include "msgwindow.h"
42 #include "utils.h"
43 #include "sciwrappers.h"
44 #include "ui_utils.h"
45 #include "symbols.h"
47 #include "tm_parser.h"
49 #include <stdlib.h>
51 #define GEANY_FILETYPE_SEARCH_LINES 2 /* lines of file to search for filetype */
53 GPtrArray *filetypes_array = NULL; /* Dynamic array of filetype pointers */
55 static GHashTable *filetypes_hash = NULL; /* Hash of filetype pointers based on name keys */
57 /** List of filetype pointers sorted by name, but with @c filetypes_index(GEANY_FILETYPES_NONE)
58 * first, as this is usually treated specially.
59 * The list does not change (after filetypes have been initialized), so you can use
60 * @code g_slist_nth_data(filetypes_by_title, n) @endcode and expect the same result at different times.
61 * @see filetypes_get_sorted_by_name(). */
62 GSList *filetypes_by_title = NULL;
64 static GtkWidget *group_menus[GEANY_FILETYPE_GROUP_COUNT] = {NULL};
67 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype);
69 static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft);
70 static void read_filetype_config(void);
73 enum TitleType
75 TITLE_NONE,
76 TITLE_SOURCE_FILE,
77 TITLE_FILE,
78 TITLE_SCRIPT,
79 TITLE_DOCUMENT
82 /* Save adding many translation strings if the filetype name doesn't need translating */
83 static gchar *filetype_make_title(const char *name, enum TitleType type)
85 const gchar *fmt = NULL;
87 g_return_val_if_fail(name != NULL, NULL);
89 switch (type)
91 case TITLE_SOURCE_FILE: fmt = _("%s source file"); break;
92 case TITLE_FILE: fmt = _("%s file"); break;
93 case TITLE_SCRIPT: fmt = _("%s script"); break;
94 case TITLE_DOCUMENT: fmt = _("%s document"); break;
95 case TITLE_NONE: /* fall through */
96 default: fmt = "%s"; break;
99 return g_strdup_printf(fmt, name);
103 /* name argument (ie filetype name) must not be translated as it is used for
104 * filetype lookup. Use filetypes_get_display_name() instead.*/
105 static void ft_init(filetype_id ft_id, int lang, const char *name,
106 const char *title_name, enum TitleType title_type, const char *mime_type,
107 GeanyFiletypeGroupID group_id)
109 GeanyFiletype *ft = filetypes[ft_id];
110 ft->lang = lang;
111 ft->name = g_strdup(name);
112 ft->title = filetype_make_title((title_name != NULL) ? title_name : ft->name, title_type);
113 if (mime_type != NULL)
114 SETPTR(ft->mime_type, g_strdup(mime_type));
115 ft->group = group_id;
118 /* Evil macro to save typing and make init_builtin_filetypes() more readable */
119 #define FT_INIT(ft_id, parser_id, name, title_name, title_type, mime_type, group_id) \
120 ft_init(GEANY_FILETYPES_##ft_id, TM_PARSER_##parser_id, name, title_name, \
121 TITLE_##title_type, mime_type, GEANY_FILETYPE_GROUP_##group_id)
124 /* Note: remember to update HACKING if this function is renamed. */
125 static void init_builtin_filetypes(void)
127 /* Column legend:
128 * [0] = Filetype constant (GEANY_FILETYPES_*)
129 * [1] = CTags parser (TM_PARSER_*)
130 * [2] = Non-translated filetype name (*not* label for display)
131 * [3] = Translatable human filetype title prefix or NULL to use [2]
132 * [4] = Title type (TITLE_*) constant (ex. TITLE_SOURCE_FILE is 'source file' suffix)
133 * [5] = Mime type or NULL for none/unknown
134 * [6] = The filetype group constant (GEANY_FILETYPE_GROUP_*)
135 * --------------------------------------------------------------------------------------------------------------------------
136 * [0] [1] [2] [3] [4] [5] [6] */
137 FT_INIT( NONE, NONE, "None", _("None"), NONE, NULL, NONE );
138 FT_INIT( C, C, "C", NULL, SOURCE_FILE, "text/x-csrc", COMPILED );
139 FT_INIT( CPP, CPP, "C++", NULL, SOURCE_FILE, "text/x-c++src", COMPILED );
140 FT_INIT( OBJECTIVEC, OBJC, "Objective-C", NULL, SOURCE_FILE, "text/x-objc", COMPILED );
141 FT_INIT( CS, CSHARP, "C#", NULL, SOURCE_FILE, "text/x-csharp", COMPILED );
142 FT_INIT( VALA, VALA, "Vala", NULL, SOURCE_FILE, "text/x-vala", COMPILED );
143 FT_INIT( D, D, "D", NULL, SOURCE_FILE, "text/x-dsrc", COMPILED );
144 FT_INIT( JAVA, JAVA, "Java", NULL, SOURCE_FILE, "text/x-java", COMPILED );
145 FT_INIT( PASCAL, PASCAL, "Pascal", NULL, SOURCE_FILE, "text/x-pascal", COMPILED );
146 FT_INIT( ASM, ASM, "ASM", "Assembler", SOURCE_FILE, NULL, COMPILED );
147 FT_INIT( BASIC, FREEBASIC, "FreeBasic", NULL, SOURCE_FILE, NULL, COMPILED );
148 FT_INIT( FORTRAN, FORTRAN, "Fortran", "Fortran (F90)", SOURCE_FILE, "text/x-fortran", COMPILED );
149 FT_INIT( F77, F77, "F77", "Fortran (F77)", SOURCE_FILE, "text/x-fortran", COMPILED );
150 FT_INIT( GLSL, GLSL, "GLSL", NULL, SOURCE_FILE, NULL, COMPILED );
151 FT_INIT( CAML, NONE, "CAML", "(O)Caml", SOURCE_FILE, "text/x-ocaml", COMPILED );
152 FT_INIT( PERL, PERL, "Perl", NULL, SOURCE_FILE, "application/x-perl", SCRIPT );
153 FT_INIT( PHP, PHP, "PHP", NULL, SOURCE_FILE, "application/x-php", SCRIPT );
154 FT_INIT( JS, JAVASCRIPT, "Javascript", NULL, SOURCE_FILE, "application/javascript", SCRIPT );
155 FT_INIT( PYTHON, PYTHON, "Python", NULL, SOURCE_FILE, "text/x-python", SCRIPT );
156 FT_INIT( RUBY, RUBY, "Ruby", NULL, SOURCE_FILE, "application/x-ruby", SCRIPT );
157 FT_INIT( TCL, TCL, "Tcl", NULL, SOURCE_FILE, "text/x-tcl", SCRIPT );
158 FT_INIT( LUA, LUA, "Lua", NULL, SOURCE_FILE, "text/x-lua", SCRIPT );
159 FT_INIT( FERITE, FERITE, "Ferite", NULL, SOURCE_FILE, NULL, SCRIPT );
160 FT_INIT( HASKELL, HASKELL, "Haskell", NULL, SOURCE_FILE, "text/x-haskell", COMPILED );
161 FT_INIT( MARKDOWN, MARKDOWN, "Markdown", NULL, SOURCE_FILE, "text/x-markdown", MARKUP );
162 FT_INIT( TXT2TAGS, TXT2TAGS, "Txt2tags", NULL, SOURCE_FILE, "text/x-txt2tags", MARKUP );
163 FT_INIT( ABC, ABC, "Abc", NULL, FILE, NULL, MISC );
164 FT_INIT( SH, SH, "Sh", _("Shell"), SCRIPT, "application/x-shellscript", SCRIPT );
165 FT_INIT( MAKE, MAKEFILE, "Make", _("Makefile"), NONE, "text/x-makefile", SCRIPT );
166 FT_INIT( XML, NONE, "XML", NULL, DOCUMENT, "application/xml", MARKUP );
167 FT_INIT( DOCBOOK, DOCBOOK, "Docbook", NULL, DOCUMENT, "application/docbook+xml", MARKUP );
168 FT_INIT( HTML, HTML, "HTML", NULL, DOCUMENT, "text/html", MARKUP );
169 FT_INIT( CSS, CSS, "CSS", _("Cascading Stylesheet"), NONE, "text/css", MARKUP ); /* not really markup but fit quite well to HTML */
170 FT_INIT( SQL, SQL, "SQL", NULL, FILE, "text/x-sql", MISC );
171 FT_INIT( COBOL, COBOL, "COBOL", NULL, SOURCE_FILE, "text/x-cobol", COMPILED );
172 FT_INIT( LATEX, LATEX, "LaTeX", NULL, SOURCE_FILE, "text/x-tex", MARKUP );
173 FT_INIT( VHDL, VHDL, "VHDL", NULL, SOURCE_FILE, "text/x-vhdl", COMPILED );
174 FT_INIT( VERILOG, VERILOG, "Verilog", NULL, SOURCE_FILE, "text/x-verilog", COMPILED );
175 FT_INIT( DIFF, DIFF, "Diff", NULL, FILE, "text/x-patch", MISC );
176 FT_INIT( LISP, NONE, "Lisp", NULL, SOURCE_FILE, NULL, SCRIPT );
177 FT_INIT( ERLANG, NONE, "Erlang", NULL, SOURCE_FILE, "text/x-erlang", COMPILED );
178 FT_INIT( CONF, CONF, "Conf", _("Config"), FILE, NULL, MISC );
179 FT_INIT( PO, NONE, "Po", _("Gettext translation"), FILE, "text/x-gettext-translation", MISC );
180 FT_INIT( HAXE, HAXE, "Haxe", NULL, SOURCE_FILE, NULL, COMPILED );
181 FT_INIT( AS, ACTIONSCRIPT, "ActionScript", NULL, SOURCE_FILE, "application/ecmascript", SCRIPT );
182 FT_INIT( R, R, "R", NULL, SOURCE_FILE, NULL, SCRIPT );
183 FT_INIT( REST, REST, "reStructuredText", NULL, SOURCE_FILE, NULL, MARKUP );
184 FT_INIT( MATLAB, MATLAB, "Matlab/Octave", NULL, SOURCE_FILE, "text/x-matlab", SCRIPT );
185 FT_INIT( YAML, NONE, "YAML", NULL, FILE, "application/x-yaml", MISC );
186 FT_INIT( CMAKE, NONE, "CMake", NULL, SOURCE_FILE, "text/x-cmake", SCRIPT );
187 FT_INIT( NSIS, NSIS, "NSIS", NULL, SOURCE_FILE, NULL, SCRIPT );
188 FT_INIT( ADA, NONE, "Ada", NULL, SOURCE_FILE, "text/x-adasrc", COMPILED );
189 FT_INIT( FORTH, NONE, "Forth", NULL, SOURCE_FILE, NULL, SCRIPT );
190 FT_INIT( ASCIIDOC, ASCIIDOC, "Asciidoc", NULL, SOURCE_FILE, NULL, MARKUP );
191 FT_INIT( ABAQUS, ABAQUS, "Abaqus", NULL, SOURCE_FILE, NULL, SCRIPT );
192 FT_INIT( BATCH, NONE, "Batch", NULL, SCRIPT, NULL, SCRIPT );
193 FT_INIT( POWERSHELL, NONE, "PowerShell", NULL, SOURCE_FILE, NULL, SCRIPT );
194 FT_INIT( RUST, RUST, "Rust", NULL, SOURCE_FILE, "text/x-rustsrc", COMPILED );
198 /* initialize fields. */
199 static GeanyFiletype *filetype_new(void)
201 GeanyFiletype *ft = g_new0(GeanyFiletype, 1);
203 ft->group = GEANY_FILETYPE_GROUP_NONE;
204 ft->lang = -2; /* assume no tagmanager parser */
205 /* pattern must not be null */
206 ft->pattern = g_new0(gchar*, 1);
207 ft->project_list_entry = -1; /* no entry */
208 ft->indent_width = -1;
209 ft->indent_type = -1;
211 ft->priv = g_new0(GeanyFiletypePrivate, 1);
212 return ft;
216 static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2, gpointer data)
218 gboolean by_name = GPOINTER_TO_INT(data);
219 const GeanyFiletype *ft1 = pft1, *ft2 = pft2;
221 if (G_UNLIKELY(ft1->id == GEANY_FILETYPES_NONE))
222 return -1;
223 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
224 return 1;
226 return by_name ?
227 utils_str_casecmp(ft1->name, ft2->name) :
228 utils_str_casecmp(ft1->title, ft2->title);
232 /** Gets a list of filetype pointers sorted by name.
233 * The list does not change on subsequent calls.
234 * @return The list - do not free.
235 * @see filetypes_by_title. */
236 const GSList *filetypes_get_sorted_by_name(void)
238 static GSList *list = NULL;
240 g_return_val_if_fail(filetypes_by_title, NULL);
242 if (!list)
244 list = g_slist_copy(filetypes_by_title);
245 list = g_slist_sort_with_data(list, cmp_filetype, GINT_TO_POINTER(TRUE));
247 return list;
251 /* Add a filetype pointer to the lists of available filetypes,
252 * and set the filetype::id field. */
253 static void filetype_add(GeanyFiletype *ft)
255 g_return_if_fail(ft);
256 g_return_if_fail(ft->name);
258 ft->id = filetypes_array->len; /* len will be the index for filetype_array */
259 g_ptr_array_add(filetypes_array, ft);
260 g_hash_table_insert(filetypes_hash, ft->name, ft);
262 /* list will be sorted later */
263 filetypes_by_title = g_slist_prepend(filetypes_by_title, ft);
265 if (!ft->mime_type)
266 ft->mime_type = g_strdup("text/plain");
270 static void add_custom_filetype(const gchar *filename)
272 gchar *fn = utils_strdupa(strstr(filename, ".") + 1);
273 gchar *dot = g_strrstr(fn, ".conf");
274 GeanyFiletype *ft;
276 g_return_if_fail(dot);
278 *dot = 0x0;
280 if (g_hash_table_lookup(filetypes_hash, fn))
281 return;
283 ft = filetype_new();
284 ft->name = g_strdup(fn);
285 ft->title = filetype_make_title(ft->name, TITLE_FILE);
286 ft->priv->custom = TRUE;
287 filetype_add(ft);
288 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
292 static void init_custom_filetypes(const gchar *path)
294 GDir *dir;
295 const gchar *filename;
297 g_return_if_fail(path);
299 dir = g_dir_open(path, 0, NULL);
300 if (dir == NULL)
301 return;
303 foreach_dir(filename, dir)
305 const gchar prefix[] = "filetypes.";
307 if (g_str_has_prefix(filename, prefix) &&
308 g_str_has_suffix(filename + strlen(prefix), ".conf"))
310 add_custom_filetype(filename);
313 g_dir_close(dir);
317 /* Create the filetypes array and fill it with the known filetypes.
318 * Warning: GTK isn't necessarily initialized yet. */
319 void filetypes_init_types(void)
321 filetype_id ft_id;
322 gchar *f;
324 g_return_if_fail(filetypes_array == NULL);
325 g_return_if_fail(filetypes_hash == NULL);
327 filetypes_array = g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES);
328 filetypes_hash = g_hash_table_new(g_str_hash, g_str_equal);
330 /* Create built-in filetypes */
331 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
333 filetypes[ft_id] = filetype_new();
335 init_builtin_filetypes();
337 /* Add built-in filetypes to the hash now the name fields are set */
338 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
340 filetype_add(filetypes[ft_id]);
342 init_custom_filetypes(app->datadir);
343 f = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, NULL);
344 init_custom_filetypes(f);
345 g_free(f);
347 /* sort last instead of on insertion to prevent exponential time */
348 filetypes_by_title = g_slist_sort_with_data(filetypes_by_title,
349 cmp_filetype, GINT_TO_POINTER(FALSE));
351 read_filetype_config();
355 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
357 gchar *f;
359 g_return_if_fail(!EMPTY(doc->real_path));
361 f = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
362 if (utils_str_equal(doc->real_path, f))
363 filetypes_reload_extensions();
365 g_free(f);
366 f = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL);
367 if (utils_str_equal(doc->real_path, f))
369 guint i;
371 /* Note: we don't reload other filetypes, even though the named styles may have changed.
372 * The user can do this manually with 'Tools->Reload Configuration' */
373 filetypes_load_config(GEANY_FILETYPES_NONE, TRUE);
375 foreach_document(i)
376 document_reload_config(documents[i]);
378 g_free(f);
382 static void setup_config_file_menus(void)
384 gchar *f;
386 f = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
387 ui_add_config_file_menu_item(f, NULL, NULL);
388 SETPTR(f, g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL));
389 ui_add_config_file_menu_item(f, NULL, NULL);
390 g_free(f);
392 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
396 static void create_sub_menu(GtkWidget *parent, GeanyFiletypeGroupID group_id, const gchar *title)
398 GtkWidget *menu, *item;
400 menu = gtk_menu_new();
401 item = gtk_menu_item_new_with_mnemonic((title));
402 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
403 gtk_container_add(GTK_CONTAINER(parent), item);
404 gtk_widget_show(item);
405 group_menus[group_id] = menu;
409 static void create_set_filetype_menu(void)
411 GSList *node;
412 GtkWidget *filetype_menu = ui_lookup_widget(main_widgets.window, "set_filetype1_menu");
414 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_COMPILED, _("_Programming Languages"));
415 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_SCRIPT, _("_Scripting Languages"));
416 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MARKUP, _("_Markup Languages"));
417 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MISC, _("M_iscellaneous"));
419 /* Append all filetypes to the filetype menu */
420 foreach_slist(node, filetypes_by_title)
422 GeanyFiletype *ft = node->data;
424 if (ft->group != GEANY_FILETYPE_GROUP_NONE)
425 create_radio_menu_item(group_menus[ft->group], ft);
426 else
427 create_radio_menu_item(filetype_menu, ft);
432 void filetypes_init(void)
434 GSList *node;
436 filetypes_init_types();
438 /* this has to be here as GTK isn't initialized in filetypes_init_types(). */
439 foreach_slist(node, filetypes_by_title)
441 GeanyFiletype *ft = node->data;
442 ft->icon = ui_get_mime_icon(ft->mime_type, GTK_ICON_SIZE_MENU);
444 create_set_filetype_menu();
445 setup_config_file_menus();
449 /* Find a filetype that predicate returns TRUE for, otherwise return NULL. */
450 GeanyFiletype *filetypes_find(GCompareFunc predicate, gpointer user_data)
452 guint i;
454 for (i = 0; i < filetypes_array->len; i++)
456 GeanyFiletype *ft = filetypes[i];
458 if (predicate(ft, user_data))
459 return ft;
461 return NULL;
465 static gboolean match_basename(gconstpointer pft, gconstpointer user_data)
467 const GeanyFiletype *ft = pft;
468 const gchar *base_filename = user_data;
469 gint j;
470 gboolean ret = FALSE;
472 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
473 return FALSE;
475 for (j = 0; ft->pattern[j] != NULL; j++)
477 GPatternSpec *pattern = g_pattern_spec_new(ft->pattern[j]);
479 if (g_pattern_match_string(pattern, base_filename))
481 ret = TRUE;
482 g_pattern_spec_free(pattern);
483 break;
485 g_pattern_spec_free(pattern);
487 return ret;
491 static GeanyFiletype *check_builtin_filenames(const gchar *utf8_filename)
493 gchar *lfn = NULL;
494 gchar *path;
495 gboolean found = FALSE;
497 #ifdef G_OS_WIN32
498 /* use lower case basename */
499 lfn = g_utf8_strdown(utf8_filename, -1);
500 #else
501 lfn = g_strdup(utf8_filename);
502 #endif
503 SETPTR(lfn, utils_get_locale_from_utf8(lfn));
505 path = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL);
506 if (g_str_has_prefix(lfn, path))
507 found = TRUE;
509 SETPTR(path, g_build_filename(app->datadir, "filetypes.", NULL));
510 if (g_str_has_prefix(lfn, path))
511 found = TRUE;
513 g_free(path);
514 g_free(lfn);
515 return found ? filetypes[GEANY_FILETYPES_CONF] : NULL;
519 /* Detect filetype only based on the filename extension.
520 * utf8_filename can include the full path. */
521 GeanyFiletype *filetypes_detect_from_extension(const gchar *utf8_filename)
523 gchar *base_filename;
524 GeanyFiletype *ft;
526 ft = check_builtin_filenames(utf8_filename);
527 if (ft)
528 return ft;
530 /* to match against the basename of the file (because of Makefile*) */
531 base_filename = g_path_get_basename(utf8_filename);
532 #ifdef G_OS_WIN32
533 /* use lower case basename */
534 SETPTR(base_filename, g_utf8_strdown(base_filename, -1));
535 #endif
537 ft = filetypes_find(match_basename, base_filename);
538 if (ft == NULL)
539 ft = filetypes[GEANY_FILETYPES_NONE];
541 g_free(base_filename);
542 return ft;
546 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
547 * terminated by -1.
548 * The detected filetype of the file is checked against every id in the passed list and if
549 * there is a match, TRUE is returned. */
550 static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first, ...)
552 GeanyFiletype *ft = NULL;
553 gint test;
554 gboolean result = FALSE;
555 va_list args;
557 ft = filetypes_detect_from_extension(utf8_filename);
558 if (ft == NULL || ft->id >= filetypes_array->len)
559 return FALSE;
561 va_start(args, first);
562 test = first;
563 while (1)
565 if (test == -1)
566 break;
568 if (ft->id == (guint) test)
570 result = TRUE;
571 break;
573 test = va_arg(args, gint);
575 va_end(args);
577 return result;
581 static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line)
583 GeanyFiletype *ft = NULL;
585 if (strlen(line) > 2 && line[0] == '#' && line[1] == '!')
587 static const struct {
588 const gchar *name;
589 filetype_id filetype;
590 } intepreter_map[] = {
591 { "sh", GEANY_FILETYPES_SH },
592 { "bash", GEANY_FILETYPES_SH },
593 { "dash", GEANY_FILETYPES_SH },
594 { "perl", GEANY_FILETYPES_PERL },
595 { "python", GEANY_FILETYPES_PYTHON },
596 { "php", GEANY_FILETYPES_PHP },
597 { "ruby", GEANY_FILETYPES_RUBY },
598 { "tcl", GEANY_FILETYPES_TCL },
599 { "make", GEANY_FILETYPES_MAKE },
600 { "zsh", GEANY_FILETYPES_SH },
601 { "ksh", GEANY_FILETYPES_SH },
602 { "mksh", GEANY_FILETYPES_SH },
603 { "csh", GEANY_FILETYPES_SH },
604 { "tcsh", GEANY_FILETYPES_SH },
605 { "ash", GEANY_FILETYPES_SH },
606 { "dmd", GEANY_FILETYPES_D },
607 { "wish", GEANY_FILETYPES_TCL },
608 { "node", GEANY_FILETYPES_JS },
609 { "rust", GEANY_FILETYPES_RUST }
611 gchar *tmp = g_path_get_basename(line + 2);
612 gchar *basename_interpreter = tmp;
613 guint i;
615 if (g_str_has_prefix(tmp, "env "))
616 { /* skip "env" and read the following interpreter */
617 basename_interpreter += 4;
620 for (i = 0; ! ft && i < G_N_ELEMENTS(intepreter_map); i++)
622 if (g_str_has_prefix(basename_interpreter, intepreter_map[i].name))
623 ft = filetypes[intepreter_map[i].filetype];
625 g_free(tmp);
627 /* detect HTML files */
628 if (g_str_has_prefix(line, "<!DOCTYPE html") || g_str_has_prefix(line, "<html"))
630 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
631 * extension and use the detected filetype, else assume HTML */
632 if (! shebang_find_and_match_filetype(utf8_filename,
633 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
635 ft = filetypes[GEANY_FILETYPES_HTML];
638 /* detect XML files */
639 else if (utf8_filename && g_str_has_prefix(line, "<?xml"))
641 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
642 * extension and use the detected filetype, else assume XML */
643 if (! shebang_find_and_match_filetype(utf8_filename,
644 GEANY_FILETYPES_HTML, GEANY_FILETYPES_DOCBOOK,
645 /* Perl, Python and PHP only to be safe */
646 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
648 ft = filetypes[GEANY_FILETYPES_XML];
651 else if (g_str_has_prefix(line, "<?php"))
653 ft = filetypes[GEANY_FILETYPES_PHP];
655 return ft;
659 /* Detect the filetype checking for a shebang, then filename extension.
660 * @lines: an strv of the lines to scan (must containing at least one line) */
661 static GeanyFiletype *filetypes_detect_from_file_internal(const gchar *utf8_filename,
662 gchar **lines)
664 GeanyFiletype *ft;
665 gint i;
666 GRegex *ft_regex;
667 GMatchInfo *match;
668 GError *regex_error = NULL;
670 /* try to find a shebang and if found use it prior to the filename extension
671 * also checks for <?xml */
672 ft = find_shebang(utf8_filename, lines[0]);
673 if (ft != NULL)
674 return ft;
676 /* try to extract the filetype using a regex capture */
677 ft_regex = g_regex_new(file_prefs.extract_filetype_regex,
678 G_REGEX_RAW | G_REGEX_MULTILINE, 0, &regex_error);
679 if (ft_regex != NULL)
681 for (i = 0; ft == NULL && lines[i] != NULL; i++)
683 if (g_regex_match(ft_regex, lines[i], 0, &match))
685 gchar *capture = g_match_info_fetch(match, 1);
686 if (capture != NULL)
688 ft = filetypes_lookup_by_name(capture);
689 g_free(capture);
692 g_match_info_free(match);
694 g_regex_unref(ft_regex);
696 else if (regex_error != NULL)
698 geany_debug("Filetype extract regex ignored: %s", regex_error->message);
699 g_error_free(regex_error);
701 if (ft != NULL)
702 return ft;
704 if (utf8_filename == NULL)
705 return filetypes[GEANY_FILETYPES_NONE];
707 return filetypes_detect_from_extension(utf8_filename);
711 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
712 GeanyFiletype *filetypes_detect_from_document(GeanyDocument *doc)
714 GeanyFiletype *ft;
715 gchar *lines[GEANY_FILETYPE_SEARCH_LINES + 1];
716 gint i;
718 g_return_val_if_fail(doc == NULL || doc->is_valid, filetypes[GEANY_FILETYPES_NONE]);
720 if (doc == NULL)
721 return filetypes[GEANY_FILETYPES_NONE];
723 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
725 lines[i] = sci_get_line(doc->editor->sci, i);
727 lines[i] = NULL;
728 ft = filetypes_detect_from_file_internal(doc->file_name, lines);
729 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
731 g_free(lines[i]);
733 return ft;
737 #ifdef HAVE_PLUGINS
738 /* Currently only used by external plugins (e.g. geanyprj). */
740 * Detects filetype based on a shebang line in the file or the filename extension.
742 * @param utf8_filename The filename in UTF-8 encoding.
744 * @return The detected filetype for @a utf8_filename or @c filetypes[GEANY_FILETYPES_NONE]
745 * if it could not be detected.
747 GeanyFiletype *filetypes_detect_from_file(const gchar *utf8_filename)
749 gchar line[1024];
750 gchar *lines[2];
751 FILE *f;
752 gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
754 f = g_fopen(locale_name, "r");
755 g_free(locale_name);
756 if (f != NULL)
758 if (fgets(line, sizeof(line), f) != NULL)
760 fclose(f);
761 lines[0] = line;
762 lines[1] = NULL;
763 return filetypes_detect_from_file_internal(utf8_filename, lines);
765 fclose(f);
767 return filetypes_detect_from_extension(utf8_filename);
769 #endif
772 void filetypes_select_radio_item(const GeanyFiletype *ft)
774 /* ignore_callback has to be set by the caller */
775 g_return_if_fail(ignore_callback);
777 if (ft == NULL)
778 ft = filetypes[GEANY_FILETYPES_NONE];
780 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
784 static void
785 on_filetype_change(GtkCheckMenuItem *menuitem, gpointer user_data)
787 GeanyDocument *doc = document_get_current();
788 if (ignore_callback || doc == NULL || ! gtk_check_menu_item_get_active(menuitem))
789 return;
791 document_set_filetype(doc, (GeanyFiletype*)user_data);
795 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
797 static GSList *group = NULL;
798 GtkWidget *tmp;
800 tmp = gtk_radio_menu_item_new_with_label(group, ftype->title);
801 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp));
802 ftype->priv->menu_item = tmp;
803 gtk_widget_show(tmp);
804 gtk_container_add(GTK_CONTAINER(menu), tmp);
805 g_signal_connect(tmp, "activate", G_CALLBACK(on_filetype_change), (gpointer) ftype);
809 static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
811 GeanyFiletype *ft = data;
813 g_return_if_fail(ft != NULL);
815 g_free(ft->name);
816 g_free(ft->title);
817 g_free(ft->extension);
818 g_free(ft->mime_type);
819 g_free(ft->comment_open);
820 g_free(ft->comment_close);
821 g_free(ft->comment_single);
822 g_free(ft->context_action_cmd);
823 g_free(ft->filecmds);
824 g_free(ft->ftdefcmds);
825 g_free(ft->execcmds);
826 g_free(ft->error_regex_string);
827 if (ft->icon)
828 g_object_unref(ft->icon);
829 g_strfreev(ft->pattern);
831 if (ft->priv->error_regex)
832 g_regex_unref(ft->priv->error_regex);
833 g_slist_foreach(ft->priv->tag_files, (GFunc) g_free, NULL);
834 g_slist_free(ft->priv->tag_files);
836 g_free(ft->priv);
837 g_free(ft);
841 /* frees the array and all related pointers */
842 void filetypes_free_types(void)
844 g_return_if_fail(filetypes_array != NULL);
845 g_return_if_fail(filetypes_hash != NULL);
847 g_ptr_array_foreach(filetypes_array, filetype_free, NULL);
848 g_ptr_array_free(filetypes_array, TRUE);
849 g_hash_table_destroy(filetypes_hash);
853 static void load_indent_settings(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
855 ft->indent_width = utils_get_setting(integer, configh, config, "indentation", "width", -1);
856 ft->indent_type = utils_get_setting(integer, configh, config, "indentation", "type", -1);
857 /* check whether the indent type is OK */
858 switch (ft->indent_type)
860 case GEANY_INDENT_TYPE_TABS:
861 case GEANY_INDENT_TYPE_SPACES:
862 case GEANY_INDENT_TYPE_BOTH:
863 case -1:
864 break;
866 default:
867 g_warning("Invalid indent type %d in file type %s", ft->indent_type, ft->name);
868 ft->indent_type = -1;
869 break;
874 static void load_settings(guint ft_id, GKeyFile *config, GKeyFile *configh)
876 GeanyFiletype *ft = filetypes[ft_id];
877 gchar *result;
879 /* default extension */
880 result = utils_get_setting(string, configh, config, "settings", "extension", NULL);
881 if (result != NULL)
883 SETPTR(filetypes[ft_id]->extension, result);
886 /* read comment notes */
887 result = utils_get_setting(string, configh, config, "settings", "comment_open", NULL);
888 if (result != NULL)
890 SETPTR(filetypes[ft_id]->comment_open, result);
893 result = utils_get_setting(string, configh, config, "settings", "comment_close", NULL);
894 if (result != NULL)
896 SETPTR(filetypes[ft_id]->comment_close, result);
899 result = utils_get_setting(string, configh, config, "settings", "comment_single", NULL);
900 if (result != NULL)
902 SETPTR(filetypes[ft_id]->comment_single, result);
904 /* import correctly filetypes that use old-style single comments */
905 else if (EMPTY(filetypes[ft_id]->comment_close))
907 SETPTR(filetypes[ft_id]->comment_single, filetypes[ft_id]->comment_open);
908 filetypes[ft_id]->comment_open = NULL;
911 filetypes[ft_id]->comment_use_indent = utils_get_setting(boolean, configh, config,
912 "settings", "comment_use_indent", FALSE);
914 /* read context action */
915 result = utils_get_setting(string, configh, config, "settings", "context_action_cmd", NULL);
916 if (result != NULL)
918 SETPTR(filetypes[ft_id]->context_action_cmd, result);
921 result = utils_get_setting(string, configh, config, "settings", "tag_parser", NULL);
922 if (result != NULL)
924 ft->lang = tm_source_file_get_named_lang(result);
925 if (ft->lang < 0)
926 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result, ft->name);
927 g_free(result);
930 result = utils_get_setting(string, configh, config, "settings", "lexer_filetype", NULL);
931 if (result != NULL)
933 ft->lexer_filetype = filetypes_lookup_by_name(result);
934 if (!ft->lexer_filetype)
935 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result, ft->name);
936 g_free(result);
939 ft->priv->symbol_list_sort_mode = utils_get_setting(integer, configh, config, "settings",
940 "symbol_list_sort_mode", SYMBOLS_SORT_BY_NAME);
941 ft->priv->xml_indent_tags = utils_get_setting(boolean, configh, config, "settings",
942 "xml_indent_tags", FALSE);
944 /* read indent settings */
945 load_indent_settings(ft, config, configh);
947 /* read build settings */
948 build_load_menu(config, GEANY_BCS_FT, (gpointer)ft);
949 build_load_menu(configh, GEANY_BCS_HOME_FT, (gpointer)ft);
953 static void copy_keys(GKeyFile *dest, const gchar *dest_group,
954 GKeyFile *src, const gchar *src_group)
956 gchar **keys = g_key_file_get_keys(src, src_group, NULL, NULL);
957 gchar **ptr;
959 foreach_strv(ptr, keys)
961 gchar *key = *ptr;
962 gchar *value = g_key_file_get_value(src, src_group, key, NULL);
964 g_key_file_set_value(dest, dest_group, key, value);
965 g_free(value);
967 g_strfreev(keys);
971 static gchar *filetypes_get_filename(GeanyFiletype *ft, gboolean user)
973 gchar *ext = filetypes_get_conf_extension(ft);
974 gchar *base_name = g_strconcat("filetypes.", ext, NULL);
975 gchar *file_name;
977 if (user)
978 file_name = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, base_name, NULL);
979 else
980 file_name = g_build_filename(app->datadir, base_name, NULL);
982 g_free(ext);
983 g_free(base_name);
985 return file_name;
989 static void add_group_keys(GKeyFile *kf, const gchar *group, GeanyFiletype *ft)
991 gchar *files[2];
992 gboolean loaded = FALSE;
993 guint i;
995 files[0] = filetypes_get_filename(ft, FALSE);
996 files[1] = filetypes_get_filename(ft, TRUE);
998 for (i = 0; i < G_N_ELEMENTS(files); i++)
1000 GKeyFile *src = g_key_file_new();
1002 if (g_key_file_load_from_file(src, files[i], G_KEY_FILE_NONE, NULL))
1004 copy_keys(kf, group, src, group);
1005 loaded = TRUE;
1007 g_key_file_free(src);
1010 if (!loaded)
1011 geany_debug("Could not read config file %s for [%s=%s]!", files[0], group, ft->name);
1013 g_free(files[0]);
1014 g_free(files[1]);
1018 static void copy_ft_groups(GKeyFile *kf)
1020 gchar **groups = g_key_file_get_groups(kf, NULL);
1021 gchar **ptr;
1023 foreach_strv(ptr, groups)
1025 gchar *group = *ptr;
1026 gchar *old_group;
1027 gchar *name = strchr(*ptr, '=');
1028 GeanyFiletype *ft;
1030 if (!name || !name[1]) /* no name or no parent name */
1031 continue;
1033 old_group = g_strdup(group);
1035 /* terminate group at '=' */
1036 *name = 0;
1037 name++;
1039 ft = filetypes_lookup_by_name(name);
1040 if (ft)
1042 add_group_keys(kf, group, ft);
1043 /* move old group keys (foo=bar) to proper group name (foo) */
1044 copy_keys(kf, group, kf, old_group);
1046 g_free(old_group);
1048 g_strfreev(groups);
1052 /* simple wrapper function to print file errors in DEBUG mode */
1053 static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags,
1054 GeanyFiletype *ft)
1056 GError *error = NULL;
1057 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1059 if (error != NULL)
1061 if (!done && !ft->priv->custom)
1062 geany_debug("Failed to open %s (%s)", file, error->message);
1064 g_error_free(error);
1065 error = NULL;
1070 /* Load the configuration file for the associated filetype id.
1071 * This should only be called when the filetype is needed, to save loading
1072 * 20+ configuration files all at once. */
1073 void filetypes_load_config(guint ft_id, gboolean reload)
1075 GKeyFile *config, *config_home;
1076 GeanyFiletypePrivate *pft;
1077 GeanyFiletype *ft;
1079 g_return_if_fail(ft_id < filetypes_array->len);
1081 ft = filetypes[ft_id];
1082 pft = ft->priv;
1084 /* when reloading, proceed only if the settings were already loaded */
1085 if (G_UNLIKELY(reload && ! pft->keyfile_loaded))
1086 return;
1088 /* when not reloading, load the settings only once */
1089 if (G_LIKELY(! reload && pft->keyfile_loaded))
1090 return;
1091 pft->keyfile_loaded = TRUE;
1093 config = g_key_file_new();
1094 config_home = g_key_file_new();
1096 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1097 gchar *f;
1099 f = filetypes_get_filename(ft, FALSE);
1100 load_system_keyfile(config, f, G_KEY_FILE_KEEP_COMMENTS, ft);
1102 SETPTR(f, filetypes_get_filename(ft, TRUE));
1103 g_key_file_load_from_file(config_home, f, G_KEY_FILE_KEEP_COMMENTS, NULL);
1104 g_free(f);
1106 /* Copy keys for any groups with [group=C] from system keyfile */
1107 copy_ft_groups(config);
1108 copy_ft_groups(config_home);
1110 load_settings(ft_id, config, config_home);
1111 highlighting_init_styles(ft_id, config, config_home);
1113 g_key_file_free(config);
1114 g_key_file_free(config_home);
1118 static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft)
1120 gchar *result;
1122 if (ft->priv->custom)
1123 return g_strconcat(ft->name, ".conf", NULL);
1125 /* Handle any special extensions different from lowercase filetype->name */
1126 switch (ft->id)
1128 case GEANY_FILETYPES_CPP: result = g_strdup("cpp"); break;
1129 case GEANY_FILETYPES_CS: result = g_strdup("cs"); break;
1130 case GEANY_FILETYPES_MAKE: result = g_strdup("makefile"); break;
1131 case GEANY_FILETYPES_NONE: result = g_strdup("common"); break;
1132 /* name is Matlab/Octave */
1133 case GEANY_FILETYPES_MATLAB: result = g_strdup("matlab"); break;
1134 /* name is Objective-C, and we don't want the hyphen */
1135 case GEANY_FILETYPES_OBJECTIVEC: result = g_strdup("objectivec"); break;
1136 default:
1137 result = g_ascii_strdown(ft->name, -1);
1138 break;
1140 return result;
1144 void filetypes_save_commands(GeanyFiletype *ft)
1146 GKeyFile *config_home;
1147 gchar *fname, *data;
1149 fname = filetypes_get_filename(ft, TRUE);
1150 config_home = g_key_file_new();
1151 g_key_file_load_from_file(config_home, fname, G_KEY_FILE_KEEP_COMMENTS, NULL);
1152 build_save_menu(config_home, ft, GEANY_BCS_HOME_FT);
1153 data = g_key_file_to_data(config_home, NULL, NULL);
1154 utils_write_file(fname, data);
1155 g_free(data);
1156 g_key_file_free(config_home);
1157 g_free(fname);
1161 /* create one file filter which has each file pattern of each filetype */
1162 GtkFileFilter *filetypes_create_file_filter_all_source(void)
1164 GtkFileFilter *new_filter;
1165 guint i, j;
1167 new_filter = gtk_file_filter_new();
1168 gtk_file_filter_set_name(new_filter, _("All Source"));
1170 for (i = 0; i < filetypes_array->len; i++)
1172 if (G_UNLIKELY(i == GEANY_FILETYPES_NONE))
1173 continue;
1175 for (j = 0; filetypes[i]->pattern[j]; j++)
1177 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1180 return new_filter;
1184 GtkFileFilter *filetypes_create_file_filter(const GeanyFiletype *ft)
1186 GtkFileFilter *new_filter;
1187 gint i;
1188 const gchar *title;
1190 g_return_val_if_fail(ft != NULL, NULL);
1192 new_filter = gtk_file_filter_new();
1193 title = ft->id == GEANY_FILETYPES_NONE ? _("All files") : ft->title;
1194 gtk_file_filter_set_name(new_filter, title);
1196 for (i = 0; ft->pattern[i]; i++)
1198 gtk_file_filter_add_pattern(new_filter, ft->pattern[i]);
1201 return new_filter;
1205 /* Indicates whether there is a tag parser for the filetype or not.
1206 * Only works for custom filetypes if the filetype settings have been loaded. */
1207 gboolean filetype_has_tags(GeanyFiletype *ft)
1209 g_return_val_if_fail(ft != NULL, FALSE);
1211 return ft->lang >= 0;
1215 /** Finds a filetype pointer from its @a name field.
1216 * @param name Filetype name.
1217 * @return The filetype found, or @c NULL.
1219 * @since 0.15
1221 GeanyFiletype *filetypes_lookup_by_name(const gchar *name)
1223 GeanyFiletype *ft;
1225 g_return_val_if_fail(!EMPTY(name), NULL);
1227 ft = g_hash_table_lookup(filetypes_hash, name);
1228 if (G_UNLIKELY(ft == NULL))
1229 geany_debug("Could not find filetype '%s'.", name);
1230 return ft;
1234 static void compile_regex(GeanyFiletype *ft, gchar *regstr)
1236 GError *error = NULL;
1237 GRegex *regex = g_regex_new(regstr, 0, 0, &error);
1239 if (!regex)
1241 ui_set_statusbar(TRUE, _("Bad regex for filetype %s: %s"),
1242 filetypes_get_display_name(ft), error->message);
1243 g_error_free(error);
1245 if (ft->priv->error_regex)
1246 g_regex_unref(ft->priv->error_regex);
1247 ft->priv->error_regex = regex;
1251 gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message,
1252 gchar **filename, gint *line)
1254 gchar *regstr;
1255 gchar **tmp;
1256 GeanyDocument *doc;
1257 GMatchInfo *minfo;
1258 gint i, n_match_groups;
1259 gchar *first, *second;
1261 if (ft == NULL)
1263 doc = document_get_current();
1264 if (doc != NULL)
1265 ft = doc->file_type;
1267 tmp = build_get_regex(build_info.grp, ft, NULL);
1268 if (tmp == NULL)
1269 return FALSE;
1270 regstr = *tmp;
1272 *filename = NULL;
1273 *line = -1;
1275 if (G_UNLIKELY(EMPTY(regstr)))
1276 return FALSE;
1278 if (!ft->priv->error_regex || regstr != ft->priv->last_error_pattern)
1280 compile_regex(ft, regstr);
1281 ft->priv->last_error_pattern = regstr;
1283 if (!ft->priv->error_regex)
1284 return FALSE;
1286 if (!g_regex_match(ft->priv->error_regex, message, 0, &minfo))
1288 g_match_info_free(minfo);
1289 return FALSE;
1292 n_match_groups = g_match_info_get_match_count(minfo);
1293 first = second = NULL;
1295 for (i = 1; i < n_match_groups; i++)
1297 gint start_pos;
1299 g_match_info_fetch_pos(minfo, i, &start_pos, NULL);
1300 if (start_pos != -1)
1302 if (first == NULL)
1303 first = g_match_info_fetch(minfo, i);
1304 else
1306 second = g_match_info_fetch(minfo, i);
1307 break;
1312 if (second)
1314 gchar *end;
1315 glong l;
1317 l = strtol(first, &end, 10);
1318 if (*end == '\0') /* first is purely decimals */
1320 *line = l;
1321 g_free(first);
1322 *filename = second;
1324 else
1326 l = strtol(second, &end, 10);
1327 if (*end == '\0')
1329 *line = l;
1330 g_free(second);
1331 *filename = first;
1333 else
1335 g_free(first);
1336 g_free(second);
1340 else
1341 g_free(first);
1343 g_match_info_free(minfo);
1344 return *filename != NULL;
1348 #ifdef G_OS_WIN32
1349 static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1351 guint i;
1352 for (i = 0; i < len; i++)
1354 SETPTR(patterns[i], g_ascii_strdown(patterns[i], -1));
1357 #endif
1360 static void read_extensions(GKeyFile *sysconfig, GKeyFile *userconfig)
1362 guint i;
1363 gsize len = 0;
1365 /* read the keys */
1366 for (i = 0; i < filetypes_array->len; i++)
1368 gboolean userset =
1369 g_key_file_has_key(userconfig, "Extensions", filetypes[i]->name, NULL);
1370 gchar **list = g_key_file_get_string_list(
1371 (userset) ? userconfig : sysconfig, "Extensions", filetypes[i]->name, &len, NULL);
1373 g_strfreev(filetypes[i]->pattern);
1374 /* Note: we allow 'Foo=' to remove all patterns */
1375 if (!list)
1376 list = g_new0(gchar*, 1);
1377 filetypes[i]->pattern = list;
1379 #ifdef G_OS_WIN32
1380 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1381 #endif
1386 static void read_group(GKeyFile *config, const gchar *group_name, GeanyFiletypeGroupID group_id)
1388 gchar **names = g_key_file_get_string_list(config, "Groups", group_name, NULL, NULL);
1389 gchar **name;
1391 foreach_strv(name, names)
1393 GeanyFiletype *ft = filetypes_lookup_by_name(*name);
1395 if (ft)
1397 ft->group = group_id;
1398 if (ft->priv->custom &&
1399 (group_id == GEANY_FILETYPE_GROUP_COMPILED || group_id == GEANY_FILETYPE_GROUP_SCRIPT))
1401 SETPTR(ft->title, filetype_make_title(ft->name, TITLE_SOURCE_FILE));
1404 else
1405 geany_debug("Filetype '%s' not found for group '%s'!", *name, group_name);
1407 g_strfreev(names);
1411 static void read_groups(GKeyFile *config)
1413 read_group(config, "Programming", GEANY_FILETYPE_GROUP_COMPILED);
1414 read_group(config, "Script", GEANY_FILETYPE_GROUP_SCRIPT);
1415 read_group(config, "Markup", GEANY_FILETYPE_GROUP_MARKUP);
1416 read_group(config, "Misc", GEANY_FILETYPE_GROUP_MISC);
1417 read_group(config, "None", GEANY_FILETYPE_GROUP_NONE);
1421 static void read_filetype_config(void)
1423 gchar *sysconfigfile = g_build_filename(app->datadir, "filetype_extensions.conf", NULL);
1424 gchar *userconfigfile = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
1425 GKeyFile *sysconfig = g_key_file_new();
1426 GKeyFile *userconfig = g_key_file_new();
1428 g_key_file_load_from_file(sysconfig, sysconfigfile, G_KEY_FILE_NONE, NULL);
1429 g_key_file_load_from_file(userconfig, userconfigfile, G_KEY_FILE_NONE, NULL);
1431 read_extensions(sysconfig, userconfig);
1432 read_groups(sysconfig);
1433 read_groups(userconfig);
1435 g_free(sysconfigfile);
1436 g_free(userconfigfile);
1437 g_key_file_free(sysconfig);
1438 g_key_file_free(userconfig);
1442 void filetypes_reload_extensions(void)
1444 guint i;
1446 read_filetype_config();
1448 /* Redetect filetype of any documents with none set */
1449 foreach_document(i)
1451 GeanyDocument *doc = documents[i];
1452 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1453 continue;
1454 document_set_filetype(doc, filetypes_detect_from_document(doc));
1459 /** Accessor function for @ref GeanyData::filetypes_array items.
1460 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1461 * @param idx @c filetypes_array index.
1462 * @return The filetype, or @c NULL if @a idx is out of range.
1464 * @since 0.16
1466 GeanyFiletype *filetypes_index(gint idx)
1468 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1472 void filetypes_reload(void)
1474 guint i;
1475 GeanyDocument *current_doc;
1477 /* reload filetype configs */
1478 for (i = 0; i < filetypes_array->len; i++)
1480 /* filetypes_load_config() will skip not loaded filetypes */
1481 filetypes_load_config(i, TRUE);
1484 current_doc = document_get_current();
1485 if (!current_doc)
1486 return;
1488 /* update document styling */
1489 foreach_document(i)
1491 if (current_doc != documents[i])
1492 document_reload_config(documents[i]);
1494 /* process the current document at last */
1495 document_reload_config(current_doc);
1499 /** Gets @c ft->name or a translation for filetype None.
1500 * @param ft .
1501 * @return .
1502 * @since Geany 0.20 */
1503 const gchar *filetypes_get_display_name(GeanyFiletype *ft)
1505 return ft->id == GEANY_FILETYPES_NONE ? _("None") : ft->name;
1509 /* gets comment_open/comment_close/comment_single strings from the filetype
1510 * @param single_first: whether single comment is preferred if both available
1511 * returns true if at least comment_open is set, false otherwise */
1512 gboolean filetype_get_comment_open_close(const GeanyFiletype *ft, gboolean single_first,
1513 const gchar **co, const gchar **cc)
1515 g_return_val_if_fail(ft != NULL, FALSE);
1516 g_return_val_if_fail(co != NULL, FALSE);
1517 g_return_val_if_fail(cc != NULL, FALSE);
1519 if (single_first)
1521 *co = ft->comment_single;
1522 if (!EMPTY(*co))
1523 *cc = NULL;
1524 else
1526 *co = ft->comment_open;
1527 *cc = ft->comment_close;
1530 else
1532 *co = ft->comment_open;
1533 if (!EMPTY(*co))
1534 *cc = ft->comment_close;
1535 else
1537 *co = ft->comment_single;
1538 *cc = NULL;
1542 return !EMPTY(*co);