Add xml_indent_tags filetype setting for documents using the
[geany-mirror.git] / src / filetypes.c
blob76c397e42177b137a3108597ec213df736537b1d
1 /*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 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
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $Id$
24 /**
25 * @file filetypes.h
26 * Filetype detection, file extensions and filetype menu items.
29 /* Note: we use filetype_id for some function arguments, but GeanyFiletype is better; we should
30 * only use GeanyFiletype for API functions. */
32 #include <string.h>
33 #include <glib/gstdio.h>
35 #include "geany.h"
36 #include "filetypes.h"
37 #include "filetypesprivate.h"
38 #include "highlighting.h"
39 #include "support.h"
40 #include "templates.h"
41 #include "document.h"
42 #include "editor.h"
43 #include "msgwindow.h"
44 #include "utils.h"
45 #include "sciwrappers.h"
46 #include "ui_utils.h"
47 #include "symbols.h"
49 #include <stdlib.h>
52 GPtrArray *filetypes_array = NULL; /* Dynamic array of filetype pointers */
54 static GHashTable *filetypes_hash = NULL; /* Hash of filetype pointers based on name keys */
56 /** List of filetype pointers sorted by name, but with @c filetypes_index(GEANY_FILETYPES_NONE)
57 * first, as this is usually treated specially.
58 * The list does not change (after filetypes have been initialized), so you can use
59 * @code g_slist_nth_data(filetypes_by_title, n) @endcode and expect the same result at different times. */
60 GSList *filetypes_by_title = NULL;
63 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype);
66 enum TitleType
68 TITLE_SOURCE_FILE,
69 TITLE_FILE
72 /* Save adding many translation strings if the filetype name doesn't need translating */
73 static void filetype_make_title(GeanyFiletype *ft, enum TitleType type)
75 const gchar *fmt = NULL;
77 switch (type)
79 default:
80 case TITLE_SOURCE_FILE: fmt = _("%s source file"); break;
81 case TITLE_FILE: fmt = _("%s file"); break;
83 g_assert(!ft->title);
84 g_assert(ft->name);
85 ft->title = g_strdup_printf(fmt, ft->name);
89 /* Note: remember to update HACKING if this function is renamed. */
90 static void init_builtin_filetypes(void)
92 GeanyFiletype *ft;
94 #define NONE /* these macros are only to ease navigation */
95 ft = filetypes[GEANY_FILETYPES_NONE];
96 /* ft->name must not be translated as it is used for filetype lookup.
97 * Use filetypes_get_display_name() instead. */
98 ft->name = g_strdup("None");
99 ft->title = g_strdup(_("None"));
100 ft->group = GEANY_FILETYPE_GROUP_NONE;
102 #define C
103 ft = filetypes[GEANY_FILETYPES_C];
104 ft->lang = 0;
105 ft->name = g_strdup("C");
106 filetype_make_title(ft, TITLE_SOURCE_FILE);
107 ft->mime_type = g_strdup("text/x-csrc");
108 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
110 #define CPP
111 ft = filetypes[GEANY_FILETYPES_CPP];
112 ft->lang = 1;
113 ft->name = g_strdup("C++");
114 filetype_make_title(ft, TITLE_SOURCE_FILE);
115 ft->mime_type = g_strdup("text/x-c++src");
116 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
118 #define CS
119 ft = filetypes[GEANY_FILETYPES_CS];
120 ft->lang = 25;
121 ft->name = g_strdup("C#");
122 filetype_make_title(ft, TITLE_SOURCE_FILE);
123 ft->mime_type = g_strdup("text/x-csharp");
124 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
126 #define VALA
127 ft = filetypes[GEANY_FILETYPES_VALA];
128 ft->lang = 33;
129 ft->name = g_strdup("Vala");
130 filetype_make_title(ft, TITLE_SOURCE_FILE);
131 ft->mime_type = g_strdup("text/x-vala");
132 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
134 #define D
135 ft = filetypes[GEANY_FILETYPES_D];
136 ft->lang = 17;
137 ft->name = g_strdup("D");
138 filetype_make_title(ft, TITLE_SOURCE_FILE);
139 ft->mime_type = g_strdup("text/x-dsrc");
140 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
142 #define JAVA
143 ft = filetypes[GEANY_FILETYPES_JAVA];
144 ft->lang = 2;
145 ft->name = g_strdup("Java");
146 filetype_make_title(ft, TITLE_SOURCE_FILE);
147 ft->mime_type = g_strdup("text/x-java");
148 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
150 #define PAS /* to avoid warnings when building under Windows, the symbol PASCAL is there defined */
151 ft = filetypes[GEANY_FILETYPES_PASCAL];
152 ft->lang = 4;
153 ft->name = g_strdup("Pascal");
154 filetype_make_title(ft, TITLE_SOURCE_FILE);
155 ft->mime_type = g_strdup("text/x-pascal");
156 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
158 #define ASM
159 ft = filetypes[GEANY_FILETYPES_ASM];
160 ft->lang = 9;
161 ft->name = g_strdup("ASM");
162 ft->title = g_strdup_printf(_("%s source file"), "Assembler");
163 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
165 #define BASIC
166 ft = filetypes[GEANY_FILETYPES_BASIC];
167 ft->lang = 26;
168 ft->name = g_strdup("FreeBasic");
169 filetype_make_title(ft, TITLE_SOURCE_FILE);
170 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
172 #define FORTRAN
173 ft = filetypes[GEANY_FILETYPES_FORTRAN];
174 ft->lang = 18;
175 ft->name = g_strdup("Fortran");
176 ft->title = g_strdup_printf(_("%s source file"), "Fortran (F90)");
177 ft->mime_type = g_strdup("text/x-fortran");
178 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
180 #define F77
181 ft = filetypes[GEANY_FILETYPES_F77];
182 ft->lang = 30;
183 ft->name = g_strdup("F77");
184 ft->title = g_strdup_printf(_("%s source file"), "Fortran (F77)");
185 ft->mime_type = g_strdup("text/x-fortran");
186 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
188 #define GLSL
189 ft = filetypes[GEANY_FILETYPES_GLSL];
190 ft->lang = 31;
191 ft->name = g_strdup("GLSL");
192 filetype_make_title(ft, TITLE_SOURCE_FILE);
193 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
195 #define CAML
196 ft = filetypes[GEANY_FILETYPES_CAML];
197 ft->name = g_strdup("CAML");
198 ft->title = g_strdup_printf(_("%s source file"), "(O)Caml");
199 ft->mime_type = g_strdup("text/x-ocaml");
200 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
202 #define PERL
203 ft = filetypes[GEANY_FILETYPES_PERL];
204 ft->lang = 5;
205 ft->name = g_strdup("Perl");
206 filetype_make_title(ft, TITLE_SOURCE_FILE);
207 ft->mime_type = g_strdup("application/x-perl");
208 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
210 #define PHP
211 ft = filetypes[GEANY_FILETYPES_PHP];
212 ft->lang = 6;
213 ft->name = g_strdup("PHP");
214 filetype_make_title(ft, TITLE_SOURCE_FILE);
215 ft->mime_type = g_strdup("application/x-php");
216 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
218 #define JAVASCRIPT
219 ft = filetypes[GEANY_FILETYPES_JS];
220 ft->lang = 23;
221 ft->name = g_strdup("Javascript");
222 filetype_make_title(ft, TITLE_SOURCE_FILE);
223 ft->mime_type = g_strdup("application/javascript");
224 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
226 #define PYTHON
227 ft = filetypes[GEANY_FILETYPES_PYTHON];
228 ft->lang = 7;
229 ft->name = g_strdup("Python");
230 filetype_make_title(ft, TITLE_SOURCE_FILE);
231 ft->mime_type = g_strdup("text/x-python");
232 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
234 #define RUBY
235 ft = filetypes[GEANY_FILETYPES_RUBY];
236 ft->lang = 14;
237 ft->name = g_strdup("Ruby");
238 filetype_make_title(ft, TITLE_SOURCE_FILE);
239 ft->mime_type = g_strdup("application/x-ruby");
240 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
242 #define TCL
243 ft = filetypes[GEANY_FILETYPES_TCL];
244 ft->lang = 15;
245 ft->name = g_strdup("Tcl");
246 filetype_make_title(ft, TITLE_SOURCE_FILE);
247 ft->mime_type = g_strdup("text/x-tcl");
248 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
250 #define LUA
251 ft = filetypes[GEANY_FILETYPES_LUA];
252 ft->lang = 22;
253 ft->name = g_strdup("Lua");
254 filetype_make_title(ft, TITLE_SOURCE_FILE);
255 ft->mime_type = g_strdup("text/x-lua");
256 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
258 #define FERITE
259 ft = filetypes[GEANY_FILETYPES_FERITE];
260 ft->lang = 19;
261 ft->name = g_strdup("Ferite");
262 filetype_make_title(ft, TITLE_SOURCE_FILE);
263 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
265 #define HASKELL
266 ft = filetypes[GEANY_FILETYPES_HASKELL];
267 ft->lang = 24;
268 ft->name = g_strdup("Haskell");
269 filetype_make_title(ft, TITLE_SOURCE_FILE);
270 ft->mime_type = g_strdup("text/x-haskell");
271 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
273 #define MARKDOWN
274 ft = filetypes[GEANY_FILETYPES_MARKDOWN];
275 ft->lang = 36;
276 ft->name = g_strdup("Markdown");
277 filetype_make_title(ft, TITLE_SOURCE_FILE);
278 ft->group = GEANY_FILETYPE_GROUP_MISC;
280 #define TXT2TAGS
281 ft = filetypes[GEANY_FILETYPES_TXT2TAGS];
282 ft->lang = 37;
283 ft->name = g_strdup("Txt2tags");
284 filetype_make_title(ft, TITLE_SOURCE_FILE);
285 ft->mime_type = g_strdup("text/x-txt2tags");
286 ft->group = GEANY_FILETYPE_GROUP_MISC;
288 #define ABC
289 ft = filetypes[GEANY_FILETYPES_ABC];
290 ft->lang = 38;
291 ft->name = g_strdup("Abc");
292 filetype_make_title(ft, TITLE_SOURCE_FILE);
293 ft->group = GEANY_FILETYPE_GROUP_MISC;
295 #define SH
296 ft = filetypes[GEANY_FILETYPES_SH];
297 ft->lang = 16;
298 ft->name = g_strdup("Sh");
299 ft->title = g_strdup(_("Shell script file"));
300 ft->mime_type = g_strdup("application/x-shellscript");
301 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
303 #define MAKE
304 ft = filetypes[GEANY_FILETYPES_MAKE];
305 ft->lang = 3;
306 ft->name = g_strdup("Make");
307 ft->title = g_strdup(_("Makefile"));
308 ft->mime_type = g_strdup("text/x-makefile");
309 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
311 #define XML
312 ft = filetypes[GEANY_FILETYPES_XML];
313 ft->name = g_strdup("XML");
314 ft->title = g_strdup(_("XML document"));
315 ft->mime_type = g_strdup("application/xml");
316 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
318 #define DOCBOOK
319 ft = filetypes[GEANY_FILETYPES_DOCBOOK];
320 ft->lang = 12;
321 ft->name = g_strdup("Docbook");
322 filetype_make_title(ft, TITLE_SOURCE_FILE);
323 ft->mime_type = g_strdup("application/docbook+xml");
324 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
326 #define HTML
327 ft = filetypes[GEANY_FILETYPES_HTML];
328 ft->lang = 29;
329 ft->name = g_strdup("HTML");
330 filetype_make_title(ft, TITLE_SOURCE_FILE);
331 ft->mime_type = g_strdup("text/html");
332 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
334 #define CSS
335 ft = filetypes[GEANY_FILETYPES_CSS];
336 ft->lang = 13;
337 ft->name = g_strdup("CSS");
338 ft->title = g_strdup(_("Cascading StyleSheet"));
339 ft->mime_type = g_strdup("text/css");
340 ft->group = GEANY_FILETYPE_GROUP_MARKUP; /* not really markup but fit quite well to HTML */
342 #define SQL
343 ft = filetypes[GEANY_FILETYPES_SQL];
344 ft->lang = 11;
345 ft->name = g_strdup("SQL");
346 ft->title = g_strdup(_("SQL Dump file"));
347 ft->mime_type = g_strdup("text/x-sql");
348 ft->group = GEANY_FILETYPE_GROUP_MISC;
350 #define LATEX
351 ft = filetypes[GEANY_FILETYPES_LATEX];
352 ft->lang = 8;
353 ft->name = g_strdup("LaTeX");
354 filetype_make_title(ft, TITLE_SOURCE_FILE);
355 ft->mime_type = g_strdup("text/x-tex");
356 ft->group = GEANY_FILETYPE_GROUP_MISC;
358 #define VHDL
359 ft = filetypes[GEANY_FILETYPES_VHDL];
360 ft->lang = 21;
361 ft->name = g_strdup("VHDL");
362 filetype_make_title(ft, TITLE_SOURCE_FILE);
363 ft->mime_type = g_strdup("text/x-vhdl");
364 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
366 #define VERILOG
367 ft = filetypes[GEANY_FILETYPES_VERILOG];
368 ft->lang = 39;
369 ft->name = g_strdup("Verilog");
370 filetype_make_title(ft, TITLE_SOURCE_FILE);
371 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
373 #define DIFF
374 ft = filetypes[GEANY_FILETYPES_DIFF];
375 ft->lang = 20;
376 ft->name = g_strdup("Diff");
377 filetype_make_title(ft, TITLE_FILE);
378 ft->mime_type = g_strdup("text/x-patch");
379 ft->group = GEANY_FILETYPE_GROUP_MISC;
381 #define LISP
382 ft = filetypes[GEANY_FILETYPES_LISP];
383 ft->name = g_strdup("Lisp");
384 filetype_make_title(ft, TITLE_SOURCE_FILE);
385 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
387 #define ERLANG
388 ft = filetypes[GEANY_FILETYPES_ERLANG];
389 ft->name = g_strdup("Erlang");
390 filetype_make_title(ft, TITLE_SOURCE_FILE);
391 ft->mime_type = g_strdup("text/x-erlang");
392 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
394 #define CONF
395 ft = filetypes[GEANY_FILETYPES_CONF];
396 ft->lang = 10;
397 ft->name = g_strdup("Conf");
398 ft->title = g_strdup(_("Config file"));
399 ft->group = GEANY_FILETYPE_GROUP_MISC;
401 #define PO
402 ft = filetypes[GEANY_FILETYPES_PO];
403 ft->name = g_strdup("Po");
404 ft->title = g_strdup(_("Gettext translation file"));
405 ft->mime_type = g_strdup("text/x-gettext-translation");
406 ft->group = GEANY_FILETYPE_GROUP_MISC;
408 #define HAXE
409 ft = filetypes[GEANY_FILETYPES_HAXE];
410 ft->lang = 27;
411 ft->name = g_strdup("Haxe");
412 filetype_make_title(ft, TITLE_SOURCE_FILE);
413 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
415 #define ACTIONSCRIPT
416 ft = filetypes[GEANY_FILETYPES_AS];
417 ft->lang = 34;
418 ft->name = g_strdup("ActionScript");
419 filetype_make_title(ft, TITLE_SOURCE_FILE);
420 ft->mime_type = g_strdup("application/ecmascript");
421 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
423 #define R
424 ft = filetypes[GEANY_FILETYPES_R];
425 ft->lang = 40;
426 ft->name = g_strdup("R");
427 ft->title = g_strdup_printf(_("%s script file"), "R");
428 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
430 #define REST
431 ft = filetypes[GEANY_FILETYPES_REST];
432 ft->lang = 28;
433 ft->name = g_strdup("reStructuredText");
434 filetype_make_title(ft, TITLE_FILE);
435 ft->group = GEANY_FILETYPE_GROUP_MISC;
437 #define MATLAB
438 ft = filetypes[GEANY_FILETYPES_MATLAB];
439 ft->lang = 32;
440 ft->name = g_strdup("Matlab/Octave");
441 filetype_make_title(ft, TITLE_SOURCE_FILE);
442 ft->mime_type = g_strdup("text/x-matlab");
443 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
445 #define YAML
446 ft = filetypes[GEANY_FILETYPES_YAML];
447 ft->name = g_strdup("YAML");
448 filetype_make_title(ft, TITLE_SOURCE_FILE);
449 ft->group = GEANY_FILETYPE_GROUP_MISC;
451 #define CMAKE
452 ft = filetypes[GEANY_FILETYPES_CMAKE];
453 ft->name = g_strdup("CMake");
454 filetype_make_title(ft, TITLE_SOURCE_FILE);
455 ft->mime_type = g_strdup("text/x-cmake");
456 ft->group = GEANY_FILETYPE_GROUP_MISC;
458 #define NSIS
459 ft = filetypes[GEANY_FILETYPES_NSIS];
460 ft->lang = 35;
461 ft->name = g_strdup("NSIS");
462 filetype_make_title(ft, TITLE_SOURCE_FILE);
463 ft->group = GEANY_FILETYPE_GROUP_MISC;
465 #define ADA
466 ft = filetypes[GEANY_FILETYPES_ADA];
467 ft->name = g_strdup("Ada");
468 filetype_make_title(ft, TITLE_SOURCE_FILE);
469 ft->mime_type = g_strdup("text/x-adasrc");
470 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
472 #define FORTH
473 ft = filetypes[GEANY_FILETYPES_FORTH];
474 ft->name = g_strdup("Forth");
475 filetype_make_title(ft, TITLE_SOURCE_FILE);
476 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
480 /* initialize fields. */
481 static GeanyFiletype *filetype_new(void)
483 GeanyFiletype *ft = g_new0(GeanyFiletype, 1);
485 ft->lang = -2; /* assume no tagmanager parser */
486 /* pattern must not be null */
487 ft->pattern = g_new0(gchar*, 1);
488 ft->project_list_entry = -1; /* no entry */
490 ft->priv = g_new0(GeanyFiletypePrivate, 1);
491 return ft;
495 static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2)
497 const GeanyFiletype *ft1 = pft1, *ft2 = pft2;
499 if (G_UNLIKELY(ft1->id == GEANY_FILETYPES_NONE))
500 return -1;
501 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
502 return 1;
504 return utils_str_casecmp(ft1->title, ft2->title);
508 /* Add a filetype pointer to the lists of available filetypes,
509 * and set the filetype::id field. */
510 static void filetype_add(GeanyFiletype *ft)
512 g_return_if_fail(ft);
513 g_return_if_fail(ft->name);
515 ft->id = filetypes_array->len; /* len will be the index for filetype_array */
516 g_ptr_array_add(filetypes_array, ft);
517 g_hash_table_insert(filetypes_hash, ft->name, ft);
519 /* list will be sorted later */
520 filetypes_by_title = g_slist_prepend(filetypes_by_title, ft);
522 if (!ft->mime_type)
523 ft->mime_type = g_strdup("text/plain");
525 ft->icon = ui_get_mime_icon(ft->mime_type, GTK_ICON_SIZE_MENU);
529 static void add_custom_filetype(const gchar *filename)
531 gchar *fn = utils_strdupa(strstr(filename, ".") + 1);
532 gchar *dot = g_strrstr(fn, ".conf");
533 GeanyFiletype *ft;
535 g_return_if_fail(dot);
537 *dot = 0x0;
539 if (g_hash_table_lookup(filetypes_hash, fn))
540 return;
542 ft = filetype_new();
543 ft->name = g_strdup(fn);
544 filetype_make_title(ft, TITLE_FILE);
545 ft->group = GEANY_FILETYPE_GROUP_CUSTOM;
546 ft->priv->custom = TRUE;
547 filetype_add(ft);
548 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
552 static void init_custom_filetypes(const gchar *path)
554 GDir *dir;
555 const gchar *filename;
557 g_return_if_fail(path);
559 dir = g_dir_open(path, 0, NULL);
560 if (dir == NULL)
561 return;
563 foreach_dir(filename, dir)
565 const gchar prefix[] = "filetypes.";
567 if (g_str_has_prefix(filename, prefix) &&
568 g_str_has_suffix(filename + strlen(prefix), ".conf"))
570 add_custom_filetype(filename);
573 g_dir_close(dir);
577 /* Create the filetypes array and fill it with the known filetypes. */
578 void filetypes_init_types()
580 filetype_id ft_id;
582 g_return_if_fail(filetypes_array == NULL);
583 g_return_if_fail(filetypes_hash == NULL);
585 filetypes_array = g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES);
586 filetypes_hash = g_hash_table_new(g_str_hash, g_str_equal);
588 /* Create built-in filetypes */
589 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
591 filetypes[ft_id] = filetype_new();
593 init_builtin_filetypes();
595 /* Add built-in filetypes to the hash now the name fields are set */
596 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
598 filetype_add(filetypes[ft_id]);
600 init_custom_filetypes(app->datadir);
601 init_custom_filetypes(utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, NULL));
603 /* sort last instead of on insertion to prevent exponential time */
604 filetypes_by_title = g_slist_sort(filetypes_by_title, cmp_filetype);
608 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
610 g_return_if_fail(NZV(doc->real_path));
612 if (utils_str_equal(doc->real_path,
613 utils_build_path(app->configdir, "filetype_extensions.conf", NULL)))
614 filetypes_read_extensions();
615 else if (utils_str_equal(doc->real_path,
616 utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL)))
618 guint i;
620 /* Note: we don't reload other filetypes, even though the named styles may have changed.
621 * The user can do this manually with 'Tools->Reload Configuration' */
622 filetypes_load_config(GEANY_FILETYPES_NONE, TRUE);
624 foreach_document(i)
625 document_reload_config(documents[i]);
630 static void setup_config_file_menus(void)
632 ui_add_config_file_menu_item(
633 utils_build_path(app->configdir, "filetype_extensions.conf", NULL), NULL, NULL);
634 ui_add_config_file_menu_item(
635 utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL), NULL, NULL);
637 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
641 static GtkWidget *group_menus[GEANY_FILETYPE_GROUP_COUNT] = {NULL};
643 static void create_sub_menu(GtkWidget *parent, gsize group_id, const gchar *title)
645 GtkWidget *menu, *item;
647 menu = gtk_menu_new();
648 item = gtk_menu_item_new_with_mnemonic((title));
649 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
650 gtk_container_add(GTK_CONTAINER(parent), item);
651 gtk_widget_show(item);
652 group_menus[group_id] = menu;
656 static void create_set_filetype_menu(void)
658 GSList *node;
659 GtkWidget *filetype_menu = ui_lookup_widget(main_widgets.window, "set_filetype1_menu");
661 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_COMPILED, _("_Programming Languages"));
662 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_SCRIPT, _("_Scripting Languages"));
663 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MARKUP, _("_Markup Languages"));
664 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MISC, _("M_iscellaneous Languages"));
665 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_CUSTOM, _("_Custom Filetypes"));
667 /* Append all filetypes to the filetype menu */
668 foreach_slist(node, filetypes_by_title)
670 GeanyFiletype *ft = node->data;
672 if (ft->group != GEANY_FILETYPE_GROUP_NONE)
673 create_radio_menu_item(group_menus[ft->group], ft);
674 else
675 create_radio_menu_item(filetype_menu, ft);
680 void filetypes_init()
682 filetypes_init_types();
684 create_set_filetype_menu();
685 setup_config_file_menus();
689 /* Find a filetype that predicate returns TRUE for, otherwise return NULL. */
690 GeanyFiletype *filetypes_find(GCompareFunc predicate, gpointer user_data)
692 guint i;
694 for (i = 0; i < filetypes_array->len; i++)
696 GeanyFiletype *ft = filetypes[i];
698 if (predicate(ft, user_data))
699 return ft;
701 return NULL;
705 static gboolean match_basename(gconstpointer pft, gconstpointer user_data)
707 const GeanyFiletype *ft = pft;
708 const gchar *base_filename = user_data;
709 gint j;
710 gboolean ret = FALSE;
712 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
713 return FALSE;
715 for (j = 0; ft->pattern[j] != NULL; j++)
717 GPatternSpec *pattern = g_pattern_spec_new(ft->pattern[j]);
719 if (g_pattern_match_string(pattern, base_filename))
721 ret = TRUE;
722 g_pattern_spec_free(pattern);
723 break;
725 g_pattern_spec_free(pattern);
727 return ret;
731 static GeanyFiletype *check_builtin_filenames(const gchar *utf8_filename)
733 gchar *lfn = NULL;
734 const gchar *path;
735 gboolean found = FALSE;
737 #ifdef G_OS_WIN32
738 /* use lower case basename */
739 lfn = g_utf8_strdown(utf8_filename, -1);
740 #else
741 lfn = g_strdup(utf8_filename);
742 #endif
743 setptr(lfn, utils_get_locale_from_utf8(lfn));
745 path = utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL);
746 if (g_str_has_prefix(lfn, path))
747 found = TRUE;
749 path = utils_build_path(app->datadir, "filetypes.", NULL);
750 if (g_str_has_prefix(lfn, path))
751 found = TRUE;
753 g_free(lfn);
754 return found ? filetypes[GEANY_FILETYPES_CONF] : NULL;
758 /* Detect filetype only based on the filename extension.
759 * utf8_filename can include the full path. */
760 GeanyFiletype *filetypes_detect_from_extension(const gchar *utf8_filename)
762 gchar *base_filename;
763 GeanyFiletype *ft;
765 ft = check_builtin_filenames(utf8_filename);
766 if (ft)
767 return ft;
769 /* to match against the basename of the file (because of Makefile*) */
770 base_filename = g_path_get_basename(utf8_filename);
771 #ifdef G_OS_WIN32
772 /* use lower case basename */
773 setptr(base_filename, g_utf8_strdown(base_filename, -1));
774 #endif
776 ft = filetypes_find(match_basename, base_filename);
777 if (ft == NULL)
778 ft = filetypes[GEANY_FILETYPES_NONE];
780 g_free(base_filename);
781 return ft;
785 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
786 * terminated by -1.
787 * The detected filetype of the file is checked against every id in the passed list and if
788 * there is a match, TRUE is returned. */
789 static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first, ...)
791 GeanyFiletype *ft = NULL;
792 gint test;
793 gboolean result = FALSE;
794 va_list args;
796 ft = filetypes_detect_from_extension(utf8_filename);
797 if (ft == NULL || ft->id >= filetypes_array->len)
798 return FALSE;
800 va_start(args, first);
801 test = first;
802 while (1)
804 if (test == -1)
805 break;
807 if (ft->id == (guint) test)
809 result = TRUE;
810 break;
812 test = va_arg(args, gint);
814 va_end(args);
816 return result;
820 static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line)
822 GeanyFiletype *ft = NULL;
824 if (strlen(line) > 2 && line[0] == '#' && line[1] == '!')
826 gchar *tmp = g_path_get_basename(line + 2);
827 gchar *basename_interpreter = tmp;
829 if (strncmp(tmp, "env ", 4) == 0 && strlen(tmp) > 4)
830 { /* skip "env" and read the following interpreter */
831 basename_interpreter += 4;
834 if (strncmp(basename_interpreter, "sh", 2) == 0)
835 ft = filetypes[GEANY_FILETYPES_SH];
836 else if (strncmp(basename_interpreter, "bash", 4) == 0)
837 ft = filetypes[GEANY_FILETYPES_SH];
838 else if (strncmp(basename_interpreter, "perl", 4) == 0)
839 ft = filetypes[GEANY_FILETYPES_PERL];
840 else if (strncmp(basename_interpreter, "python", 6) == 0)
841 ft = filetypes[GEANY_FILETYPES_PYTHON];
842 else if (strncmp(basename_interpreter, "php", 3) == 0)
843 ft = filetypes[GEANY_FILETYPES_PHP];
844 else if (strncmp(basename_interpreter, "ruby", 4) == 0)
845 ft = filetypes[GEANY_FILETYPES_RUBY];
846 else if (strncmp(basename_interpreter, "tcl", 3) == 0)
847 ft = filetypes[GEANY_FILETYPES_TCL];
848 else if (strncmp(basename_interpreter, "make", 4) == 0)
849 ft = filetypes[GEANY_FILETYPES_MAKE];
850 else if (strncmp(basename_interpreter, "zsh", 3) == 0)
851 ft = filetypes[GEANY_FILETYPES_SH];
852 else if (strncmp(basename_interpreter, "ksh", 3) == 0)
853 ft = filetypes[GEANY_FILETYPES_SH];
854 else if (strncmp(basename_interpreter, "csh", 3) == 0)
855 ft = filetypes[GEANY_FILETYPES_SH];
856 else if (strncmp(basename_interpreter, "ash", 3) == 0)
857 ft = filetypes[GEANY_FILETYPES_SH];
858 else if (strncmp(basename_interpreter, "dmd", 3) == 0)
859 ft = filetypes[GEANY_FILETYPES_D];
860 else if (strncmp(basename_interpreter, "wish", 4) == 0)
861 ft = filetypes[GEANY_FILETYPES_TCL];
863 g_free(tmp);
865 /* detect HTML files */
866 if (strncmp(line, "<!DOCTYPE html", 14) == 0 || strncmp(line, "<html", 5) == 0)
868 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
869 * extension and use the detected filetype, else assume HTML */
870 if (! shebang_find_and_match_filetype(utf8_filename,
871 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
873 ft = filetypes[GEANY_FILETYPES_HTML];
876 /* detect XML files */
877 else if (utf8_filename && strncmp(line, "<?xml", 5) == 0)
879 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
880 * extension and use the detected filetype, else assume XML */
881 if (! shebang_find_and_match_filetype(utf8_filename,
882 GEANY_FILETYPES_HTML, GEANY_FILETYPES_DOCBOOK,
883 /* Perl, Python and PHP only to be safe */
884 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
886 ft = filetypes[GEANY_FILETYPES_XML];
889 else if (strncmp(line, "<?php", 5) == 0)
891 ft = filetypes[GEANY_FILETYPES_PHP];
894 return ft;
898 /* Detect the filetype checking for a shebang, then filename extension. */
899 static GeanyFiletype *filetypes_detect_from_file_internal(const gchar *utf8_filename,
900 const gchar *line)
902 GeanyFiletype *ft;
904 /* try to find a shebang and if found use it prior to the filename extension
905 * also checks for <?xml */
906 ft = find_shebang(utf8_filename, line);
907 if (ft != NULL)
908 return ft;
910 if (utf8_filename == NULL)
911 return filetypes[GEANY_FILETYPES_NONE];
913 return filetypes_detect_from_extension(utf8_filename);
917 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
918 GeanyFiletype *filetypes_detect_from_document(GeanyDocument *doc)
920 GeanyFiletype *ft;
921 gchar *line;
923 if (doc == NULL)
924 return filetypes[GEANY_FILETYPES_NONE];
926 line = sci_get_line(doc->editor->sci, 0);
927 ft = filetypes_detect_from_file_internal(doc->file_name, line);
928 g_free(line);
929 return ft;
933 #ifdef HAVE_PLUGINS
934 /* Currently only used by external plugins (e.g. geanyprj). */
936 * Detects filetype based on a shebang line in the file or the filename extension.
938 * @param utf8_filename The filename in UTF-8 encoding.
940 * @return The detected filetype for @a utf8_filename or @c filetypes[GEANY_FILETYPES_NONE]
941 * if it could not be detected.
943 GeanyFiletype *filetypes_detect_from_file(const gchar *utf8_filename)
945 gchar line[1024];
946 FILE *f;
947 gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
949 f = g_fopen(locale_name, "r");
950 g_free(locale_name);
951 if (f != NULL)
953 if (fgets(line, sizeof(line), f) != NULL)
955 fclose(f);
956 return filetypes_detect_from_file_internal(utf8_filename, line);
958 fclose(f);
960 return filetypes_detect_from_extension(utf8_filename);
962 #endif
965 void filetypes_select_radio_item(const GeanyFiletype *ft)
967 /* ignore_callback has to be set by the caller */
968 g_return_if_fail(ignore_callback);
970 if (ft == NULL)
971 ft = filetypes[GEANY_FILETYPES_NONE];
973 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
977 static void
978 on_filetype_change (GtkCheckMenuItem *menuitem,
979 gpointer user_data)
981 GeanyDocument *doc = document_get_current();
982 if (ignore_callback || doc == NULL || ! gtk_check_menu_item_get_active(menuitem))
983 return;
985 document_set_filetype(doc, (GeanyFiletype*)user_data);
989 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
991 static GSList *group = NULL;
992 GtkWidget *tmp;
994 tmp = gtk_radio_menu_item_new_with_label(group, ftype->title);
995 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp));
996 ftype->priv->menu_item = tmp;
997 gtk_widget_show(tmp);
998 gtk_container_add(GTK_CONTAINER(menu), tmp);
999 g_signal_connect(tmp, "activate", G_CALLBACK(on_filetype_change), (gpointer) ftype);
1003 #if 0
1004 /* Remove a filetype pointer from the list of available filetypes. */
1005 static void filetype_remove(GeanyFiletype *ft)
1007 g_return_if_fail(ft);
1009 g_ptr_array_remove(filetypes_array, ft);
1011 if (!g_hash_table_remove(filetypes_hash, ft))
1012 g_warning("Could not remove filetype %p!", ft);
1014 #endif
1017 static void set_error_regex(GeanyFiletype *ft, gchar *string)
1019 setptr(ft->error_regex_string, string);
1021 if (ft->priv->error_regex_compiled)
1022 regfree(&ft->priv->error_regex);
1024 ft->priv->error_regex_compiled = FALSE;
1025 /* regex will be compiled when needed */
1029 static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
1031 GeanyFiletype *ft = data;
1033 g_return_if_fail(ft != NULL);
1035 g_free(ft->name);
1036 g_free(ft->title);
1037 g_free(ft->extension);
1038 g_free(ft->mime_type);
1039 g_free(ft->comment_open);
1040 g_free(ft->comment_close);
1041 g_free(ft->context_action_cmd);
1042 g_free(ft->filecmds);
1043 g_free(ft->ftdefcmds);
1044 g_free(ft->execcmds);
1045 set_error_regex(ft, NULL);
1046 if (ft->icon)
1047 g_object_unref(ft->icon);
1049 g_strfreev(ft->pattern);
1050 g_free(ft->priv);
1051 g_free(ft);
1055 /* frees the array and all related pointers */
1056 void filetypes_free_types(void)
1058 g_return_if_fail(filetypes_array != NULL);
1059 g_return_if_fail(filetypes_hash != NULL);
1061 g_ptr_array_foreach(filetypes_array, filetype_free, NULL);
1062 g_ptr_array_free(filetypes_array, TRUE);
1063 g_hash_table_destroy(filetypes_hash);
1067 static void load_settings(gint ft_id, GKeyFile *config, GKeyFile *configh)
1069 GeanyFiletype *ft = filetypes[ft_id];
1070 gchar *result;
1071 GError *error = NULL;
1072 gboolean tmp;
1074 /* default extension */
1075 result = g_key_file_get_string(configh, "settings", "extension", NULL);
1076 if (result == NULL) result = g_key_file_get_string(config, "settings", "extension", NULL);
1077 if (G_LIKELY(result != NULL))
1079 setptr(filetypes[ft_id]->extension, result);
1082 /* read comment notes */
1083 result = g_key_file_get_string(configh, "settings", "comment_open", NULL);
1084 if (result == NULL) result = g_key_file_get_string(config, "settings", "comment_open", NULL);
1085 if (G_LIKELY(result != NULL))
1087 g_free(filetypes[ft_id]->comment_open);
1088 filetypes[ft_id]->comment_open = result;
1091 result = g_key_file_get_string(configh, "settings", "comment_close", NULL);
1092 if (result == NULL) result = g_key_file_get_string(config, "settings", "comment_close", NULL);
1093 if (G_LIKELY(result != NULL))
1095 g_free(filetypes[ft_id]->comment_close);
1096 filetypes[ft_id]->comment_close = result;
1099 tmp = g_key_file_get_boolean(configh, "settings", "comment_use_indent", &error);
1100 if (error)
1102 g_error_free(error);
1103 error = NULL;
1104 tmp = g_key_file_get_boolean(config, "settings", "comment_use_indent", &error);
1105 if (error) g_error_free(error);
1106 else filetypes[ft_id]->comment_use_indent = tmp;
1108 else filetypes[ft_id]->comment_use_indent = tmp;
1110 /* read context action */
1111 result = g_key_file_get_string(configh, "settings", "context_action_cmd", NULL);
1112 if (result == NULL) result = g_key_file_get_string(config, "settings", "context_action_cmd", NULL);
1113 if (G_LIKELY(result != NULL))
1115 filetypes[ft_id]->context_action_cmd = result;
1118 result = utils_get_setting_string(configh, "settings", "tag_parser", NULL);
1119 if (!result)
1120 result = utils_get_setting_string(config, "settings", "tag_parser", NULL);
1121 if (result)
1123 ft->lang = tm_source_file_get_named_lang(result);
1124 if (ft->lang < 0)
1125 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result, ft->name);
1126 g_free(result);
1129 result = utils_get_setting_string(configh, "settings", "lexer_filetype", NULL);
1130 if (!result)
1131 result = utils_get_setting_string(config, "settings", "lexer_filetype", NULL);
1132 if (result)
1134 ft->lexer_filetype = filetypes_lookup_by_name(result);
1135 if (!ft->lexer_filetype)
1136 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result, ft->name);
1137 g_free(result);
1140 ft->priv->symbol_list_sort_mode = utils_get_setting(integer, configh, config, "settings",
1141 "symbol_list_sort_mode", SYMBOLS_SORT_BY_NAME);
1142 ft->priv->xml_indent_tags = utils_get_setting(boolean, configh, config, "settings",
1143 "xml_indent_tags", FALSE);
1145 /* read build settings */
1146 build_load_menu(config, GEANY_BCS_FT, (gpointer)ft);
1147 build_load_menu(configh, GEANY_BCS_HOME_FT, (gpointer)ft);
1151 /* simple wrapper function to print file errors in DEBUG mode */
1152 static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags,
1153 GeanyFiletype *ft)
1155 GError *error = NULL;
1156 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1158 if (error != NULL)
1160 if (!done && !ft->priv->custom)
1161 geany_debug("Failed to open %s (%s)", file, error->message);
1163 g_error_free(error);
1164 error = NULL;
1169 /* Load the configuration file for the associated filetype id.
1170 * This should only be called when the filetype is needed, to save loading
1171 * 20+ configuration files all at once. */
1172 void filetypes_load_config(gint ft_id, gboolean reload)
1174 GKeyFile *config, *config_home;
1175 GeanyFiletypePrivate *pft;
1176 GeanyFiletype *ft;
1178 g_return_if_fail(ft_id >= 0 && ft_id < (gint) filetypes_array->len);
1180 ft = filetypes[ft_id];
1181 pft = ft->priv;
1183 /* when reloading, proceed only if the settings were already loaded */
1184 if (reload && G_UNLIKELY(! pft->keyfile_loaded))
1185 return;
1187 /* when not reloading, load the settings only once */
1188 if (! reload && G_LIKELY(pft->keyfile_loaded))
1189 return;
1190 pft->keyfile_loaded = TRUE;
1192 config = g_key_file_new();
1193 config_home = g_key_file_new();
1195 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1196 gchar *ext = filetypes_get_conf_extension(ft_id);
1197 gchar *f0 = g_strconcat(app->datadir, G_DIR_SEPARATOR_S "filetypes.", ext, NULL);
1198 gchar *f = g_strconcat(app->configdir,
1199 G_DIR_SEPARATOR_S GEANY_FILEDEFS_SUBDIR G_DIR_SEPARATOR_S "filetypes.", ext, NULL);
1201 load_system_keyfile(config, f0, G_KEY_FILE_KEEP_COMMENTS, ft);
1202 g_key_file_load_from_file(config_home, f, G_KEY_FILE_KEEP_COMMENTS, NULL);
1204 g_free(ext);
1205 g_free(f);
1206 g_free(f0);
1209 load_settings(ft_id, config, config_home);
1210 highlighting_init_styles(ft_id, config, config_home);
1212 g_key_file_free(config);
1213 g_key_file_free(config_home);
1217 gchar *filetypes_get_conf_extension(gint filetype_idx)
1219 gchar *result, *ptr;
1220 GeanyFiletype *ft = filetypes[filetype_idx];
1222 if (ft->priv->custom)
1223 return g_strconcat(ft->name, ".conf", NULL);
1225 /* Handle any special extensions different from lowercase filetype->name */
1226 switch (filetype_idx)
1228 case GEANY_FILETYPES_CPP: result = g_strdup("cpp"); break;
1229 case GEANY_FILETYPES_CS: result = g_strdup("cs"); break;
1230 case GEANY_FILETYPES_MAKE: result = g_strdup("makefile"); break;
1231 case GEANY_FILETYPES_NONE: result = g_strdup("common"); break;
1232 default:
1233 result = g_ascii_strdown(ft->name, -1);
1234 /* truncate at slash (e.g. for Matlab/Octave) */
1235 ptr = strstr(result, "/");
1236 if (ptr)
1237 *ptr = 0x0;
1238 break;
1240 return result;
1244 void filetypes_save_commands(void)
1246 gchar *conf_prefix = g_strconcat(app->configdir,
1247 G_DIR_SEPARATOR_S GEANY_FILEDEFS_SUBDIR G_DIR_SEPARATOR_S "filetypes.", NULL);
1248 guint i;
1250 for (i = 0; i < filetypes_array->len; i++)
1252 GKeyFile *config_home;
1253 gchar *fname, *ext, *data;
1255 if (filetypes[i]->home_save_needed)
1257 ext = filetypes_get_conf_extension(i);
1258 fname = g_strconcat(conf_prefix, ext, NULL);
1259 g_free(ext);
1260 config_home = g_key_file_new();
1261 g_key_file_load_from_file(config_home, fname, G_KEY_FILE_KEEP_COMMENTS, NULL);
1262 build_save_menu(config_home, (gpointer)(filetypes[i]), GEANY_BCS_HOME_FT);
1263 data = g_key_file_to_data(config_home, NULL, NULL);
1264 utils_write_file(fname, data);
1265 g_free(data);
1266 g_key_file_free(config_home);
1267 g_free(fname);
1270 g_free(conf_prefix);
1274 /* create one file filter which has each file pattern of each filetype */
1275 GtkFileFilter *filetypes_create_file_filter_all_source(void)
1277 GtkFileFilter *new_filter;
1278 guint i, j;
1280 new_filter = gtk_file_filter_new();
1281 gtk_file_filter_set_name(new_filter, _("All Source"));
1283 for (i = 0; i < filetypes_array->len; i++)
1285 if (G_UNLIKELY(i == GEANY_FILETYPES_NONE))
1286 continue;
1288 for (j = 0; filetypes[i]->pattern[j]; j++)
1290 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1293 return new_filter;
1297 GtkFileFilter *filetypes_create_file_filter(const GeanyFiletype *ft)
1299 GtkFileFilter *new_filter;
1300 gint i;
1301 const gchar *title;
1303 g_return_val_if_fail(ft != NULL, NULL);
1305 new_filter = gtk_file_filter_new();
1306 title = ft->id == GEANY_FILETYPES_NONE ? _("All files") : ft->title;
1307 gtk_file_filter_set_name(new_filter, title);
1309 for (i = 0; ft->pattern[i]; i++)
1311 gtk_file_filter_add_pattern(new_filter, ft->pattern[i]);
1314 return new_filter;
1318 /* Indicates whether there is a tag parser for the filetype or not.
1319 * Only works for custom filetypes if the filetype settings have been loaded. */
1320 gboolean filetype_has_tags(GeanyFiletype *ft)
1322 g_return_val_if_fail(ft != NULL, FALSE);
1324 return ft->lang >= 0;
1328 /** Finds a filetype pointer from its @a name field.
1329 * @param name Filetype name.
1330 * @return The filetype found, or @c NULL.
1332 * @since 0.15
1334 GeanyFiletype *filetypes_lookup_by_name(const gchar *name)
1336 GeanyFiletype *ft;
1338 g_return_val_if_fail(NZV(name), NULL);
1340 ft = g_hash_table_lookup(filetypes_hash, name);
1341 if (G_UNLIKELY(ft == NULL))
1342 geany_debug("Could not find filetype '%s'.", name);
1343 return ft;
1347 static gchar *get_regex_match_string(const gchar *message, regmatch_t *pmatch, gint match_idx)
1349 return g_strndup(&message[pmatch[match_idx].rm_so],
1350 pmatch[match_idx].rm_eo - pmatch[match_idx].rm_so);
1354 static void compile_regex(GeanyFiletype *ft, regex_t *regex, gchar *regstr)
1356 gint retval = regcomp(regex, regstr, REG_EXTENDED);
1358 ft->priv->error_regex_compiled = (retval == 0); /* prevent recompilation */
1360 if (G_UNLIKELY(retval != 0))
1362 gchar buf[256];
1363 regerror(retval, regex, buf, sizeof buf);
1364 ui_set_statusbar(TRUE, _("Bad regex for filetype %s: %s"),
1365 filetypes_get_display_name(ft), buf);
1367 /* regex will be freed in set_error_regex(). */
1371 gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message,
1372 gchar **filename, gint *line)
1374 gchar *regstr;
1375 gchar **tmp;
1376 GeanyDocument *doc;
1377 regex_t *regex;
1378 regmatch_t pmatch[3];
1380 if (ft == NULL)
1382 doc = document_get_current();
1383 if (doc != NULL)
1384 ft = doc->file_type;
1386 tmp = build_get_regex(build_info.grp, ft, NULL);
1387 if (tmp == NULL)
1388 return FALSE;
1389 regstr = *tmp;
1390 regex = &ft->priv->error_regex;
1392 *filename = NULL;
1393 *line = -1;
1395 if (!NZV(regstr))
1396 return FALSE;
1398 if (!ft->priv->error_regex_compiled || regstr != ft->priv->last_string)
1400 compile_regex(ft, regex, regstr);
1401 ft->priv->last_string = regstr;
1403 if (!ft->priv->error_regex_compiled) /* regex error */
1404 return FALSE;
1406 if (regexec(regex, message, G_N_ELEMENTS(pmatch), pmatch, 0) != 0)
1407 return FALSE;
1409 if (pmatch[0].rm_so != -1 && pmatch[1].rm_so != -1 && pmatch[2].rm_so != -1)
1411 gchar *first, *second, *end;
1412 glong l;
1414 first = get_regex_match_string(message, pmatch, 1);
1415 second = get_regex_match_string(message, pmatch, 2);
1416 l = strtol(first, &end, 10);
1417 if (*end == '\0') /* first is purely decimals */
1419 *line = l;
1420 g_free(first);
1421 *filename = second;
1423 else
1425 l = strtol(second, &end, 10);
1426 if (*end == '\0')
1428 *line = l;
1429 g_free(second);
1430 *filename = first;
1432 else
1434 g_free(first);
1435 g_free(second);
1439 return *filename != NULL;
1443 #ifdef G_OS_WIN32
1444 static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1446 guint i;
1447 for (i = 0; i < len; i++)
1449 setptr(patterns[i], g_ascii_strdown(patterns[i], -1));
1452 #endif
1455 void filetypes_read_extensions(void)
1457 guint i;
1458 gsize len = 0;
1459 gchar *sysconfigfile = g_strconcat(app->datadir, G_DIR_SEPARATOR_S,
1460 "filetype_extensions.conf", NULL);
1461 gchar *userconfigfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
1462 "filetype_extensions.conf", NULL);
1463 GKeyFile *sysconfig = g_key_file_new();
1464 GKeyFile *userconfig = g_key_file_new();
1466 g_key_file_load_from_file(sysconfig, sysconfigfile, G_KEY_FILE_NONE, NULL);
1467 g_key_file_load_from_file(userconfig, userconfigfile, G_KEY_FILE_NONE, NULL);
1469 /* read the keys */
1470 for (i = 0; i < filetypes_array->len; i++)
1472 gboolean userset =
1473 g_key_file_has_key(userconfig, "Extensions", filetypes[i]->name, NULL);
1474 gchar **list = g_key_file_get_string_list(
1475 (userset) ? userconfig : sysconfig, "Extensions", filetypes[i]->name, &len, NULL);
1477 g_strfreev(filetypes[i]->pattern);
1478 /* Note: we allow 'Foo=' to remove all patterns */
1479 if (!list)
1480 list = g_new0(gchar*, 1);
1481 filetypes[i]->pattern = list;
1483 #ifdef G_OS_WIN32
1484 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1485 #endif
1488 g_free(sysconfigfile);
1489 g_free(userconfigfile);
1490 g_key_file_free(sysconfig);
1491 g_key_file_free(userconfig);
1493 /* Redetect filetype of any documents with none set */
1494 foreach_document(i)
1496 GeanyDocument *doc = documents[i];
1497 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1498 continue;
1499 document_set_filetype(doc, filetypes_detect_from_document(doc));
1504 /** Accessor function for @ref GeanyData::filetypes_array items.
1505 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1506 * @param idx @c filetypes_array index.
1507 * @return The filetype, or @c NULL if @a idx is out of range.
1509 * @since 0.16
1511 GeanyFiletype *filetypes_index(gint idx)
1513 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1517 void filetypes_reload(void)
1519 guint i;
1520 GeanyDocument *current_doc;
1522 /* save possibly changed commands before re-reading them */
1523 filetypes_save_commands();
1525 /* reload filetype configs */
1526 for (i = 0; i < filetypes_array->len; i++)
1528 /* filetypes_load_config() will skip not loaded filetypes */
1529 filetypes_load_config(i, TRUE);
1532 current_doc = document_get_current();
1533 if (!current_doc)
1534 return;
1536 /* update document styling */
1537 foreach_document(i)
1539 if (current_doc != documents[i])
1540 document_reload_config(documents[i]);
1542 /* process the current document at last */
1543 document_reload_config(current_doc);
1547 /** Gets @c ft->name or a translation for filetype None.
1548 * @param ft .
1549 * @return .
1550 * @since Geany 0.20 */
1551 const gchar *filetypes_get_display_name(GeanyFiletype *ft)
1553 return ft->id == GEANY_FILETYPES_NONE ? _("None") : ft->name;