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.
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. */
33 #include <glib/gstdio.h>
36 #include "filetypes.h"
37 #include "filetypesprivate.h"
38 #include "highlighting.h"
40 #include "templates.h"
43 #include "msgwindow.h"
45 #include "sciwrappers.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
);
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
;
80 case TITLE_SOURCE_FILE
: fmt
= _("%s source file"); break;
81 case TITLE_FILE
: fmt
= _("%s file"); break;
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)
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
;
103 ft
= filetypes
[GEANY_FILETYPES_C
];
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
;
111 ft
= filetypes
[GEANY_FILETYPES_CPP
];
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
;
119 ft
= filetypes
[GEANY_FILETYPES_CS
];
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
;
127 ft
= filetypes
[GEANY_FILETYPES_VALA
];
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
;
135 ft
= filetypes
[GEANY_FILETYPES_D
];
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
;
143 ft
= filetypes
[GEANY_FILETYPES_JAVA
];
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
];
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
;
159 ft
= filetypes
[GEANY_FILETYPES_ASM
];
161 ft
->name
= g_strdup("ASM");
162 ft
->title
= g_strdup_printf(_("%s source file"), "Assembler");
163 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
166 ft
= filetypes
[GEANY_FILETYPES_BASIC
];
168 ft
->name
= g_strdup("FreeBasic");
169 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
170 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
173 ft
= filetypes
[GEANY_FILETYPES_FORTRAN
];
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
;
181 ft
= filetypes
[GEANY_FILETYPES_F77
];
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
;
189 ft
= filetypes
[GEANY_FILETYPES_GLSL
];
191 ft
->name
= g_strdup("GLSL");
192 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
193 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
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
;
203 ft
= filetypes
[GEANY_FILETYPES_PERL
];
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
;
211 ft
= filetypes
[GEANY_FILETYPES_PHP
];
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
;
219 ft
= filetypes
[GEANY_FILETYPES_JS
];
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
;
227 ft
= filetypes
[GEANY_FILETYPES_PYTHON
];
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
;
235 ft
= filetypes
[GEANY_FILETYPES_RUBY
];
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
;
243 ft
= filetypes
[GEANY_FILETYPES_TCL
];
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
;
251 ft
= filetypes
[GEANY_FILETYPES_LUA
];
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
;
259 ft
= filetypes
[GEANY_FILETYPES_FERITE
];
261 ft
->name
= g_strdup("Ferite");
262 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
263 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
266 ft
= filetypes
[GEANY_FILETYPES_HASKELL
];
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
;
274 ft
= filetypes
[GEANY_FILETYPES_MARKDOWN
];
276 ft
->name
= g_strdup("Markdown");
277 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
278 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
281 ft
= filetypes
[GEANY_FILETYPES_TXT2TAGS
];
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
;
289 ft
= filetypes
[GEANY_FILETYPES_ABC
];
291 ft
->name
= g_strdup("Abc");
292 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
293 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
296 ft
= filetypes
[GEANY_FILETYPES_SH
];
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
;
304 ft
= filetypes
[GEANY_FILETYPES_MAKE
];
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
;
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
;
319 ft
= filetypes
[GEANY_FILETYPES_DOCBOOK
];
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
;
327 ft
= filetypes
[GEANY_FILETYPES_HTML
];
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
;
335 ft
= filetypes
[GEANY_FILETYPES_CSS
];
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 */
343 ft
= filetypes
[GEANY_FILETYPES_SQL
];
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
;
351 ft
= filetypes
[GEANY_FILETYPES_LATEX
];
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
;
359 ft
= filetypes
[GEANY_FILETYPES_VHDL
];
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
;
367 ft
= filetypes
[GEANY_FILETYPES_VERILOG
];
369 ft
->name
= g_strdup("Verilog");
370 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
371 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
374 ft
= filetypes
[GEANY_FILETYPES_DIFF
];
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
;
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
;
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
;
395 ft
= filetypes
[GEANY_FILETYPES_CONF
];
397 ft
->name
= g_strdup("Conf");
398 ft
->title
= g_strdup(_("Config file"));
399 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
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
;
409 ft
= filetypes
[GEANY_FILETYPES_HAXE
];
411 ft
->name
= g_strdup("Haxe");
412 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
413 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
416 ft
= filetypes
[GEANY_FILETYPES_AS
];
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
;
424 ft
= filetypes
[GEANY_FILETYPES_R
];
426 ft
->name
= g_strdup("R");
427 ft
->title
= g_strdup_printf(_("%s script file"), "R");
428 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
431 ft
= filetypes
[GEANY_FILETYPES_REST
];
433 ft
->name
= g_strdup("reStructuredText");
434 filetype_make_title(ft
, TITLE_FILE
);
435 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
438 ft
= filetypes
[GEANY_FILETYPES_MATLAB
];
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
;
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
;
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
;
459 ft
= filetypes
[GEANY_FILETYPES_NSIS
];
461 ft
->name
= g_strdup("NSIS");
462 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
463 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
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
;
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);
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
))
501 if (G_UNLIKELY(ft2
->id
== GEANY_FILETYPES_NONE
))
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
);
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");
535 g_return_if_fail(dot
);
539 if (g_hash_table_lookup(filetypes_hash
, fn
))
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
;
548 geany_debug("Added filetype %s (%d).", ft
->name
, ft
->id
);
552 static void init_custom_filetypes(const gchar
*path
)
555 const gchar
*filename
;
557 g_return_if_fail(path
);
559 dir
= g_dir_open(path
, 0, NULL
);
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
);
577 /* Create the filetypes array and fill it with the known filetypes. */
578 void filetypes_init_types()
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
)))
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
);
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)
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
);
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
)
694 for (i
= 0; i
< filetypes_array
->len
; i
++)
696 GeanyFiletype
*ft
= filetypes
[i
];
698 if (predicate(ft
, user_data
))
705 static gboolean
match_basename(gconstpointer pft
, gconstpointer user_data
)
707 const GeanyFiletype
*ft
= pft
;
708 const gchar
*base_filename
= user_data
;
710 gboolean ret
= FALSE
;
712 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
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
))
722 g_pattern_spec_free(pattern
);
725 g_pattern_spec_free(pattern
);
731 static GeanyFiletype
*check_builtin_filenames(const gchar
*utf8_filename
)
735 gboolean found
= FALSE
;
738 /* use lower case basename */
739 lfn
= g_utf8_strdown(utf8_filename
, -1);
741 lfn
= g_strdup(utf8_filename
);
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
))
749 path
= utils_build_path(app
->datadir
, "filetypes.", NULL
);
750 if (g_str_has_prefix(lfn
, path
))
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
;
765 ft
= check_builtin_filenames(utf8_filename
);
769 /* to match against the basename of the file (because of Makefile*) */
770 base_filename
= g_path_get_basename(utf8_filename
);
772 /* use lower case basename */
773 setptr(base_filename
, g_utf8_strdown(base_filename
, -1));
776 ft
= filetypes_find(match_basename
, base_filename
);
778 ft
= filetypes
[GEANY_FILETYPES_NONE
];
780 g_free(base_filename
);
785 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
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
;
793 gboolean result
= FALSE
;
796 ft
= filetypes_detect_from_extension(utf8_filename
);
797 if (ft
== NULL
|| ft
->id
>= filetypes_array
->len
)
800 va_start(args
, first
);
807 if (ft
->id
== (guint
) test
)
812 test
= va_arg(args
, gint
);
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
];
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
];
898 /* Detect the filetype checking for a shebang, then filename extension. */
899 static GeanyFiletype
*filetypes_detect_from_file_internal(const gchar
*utf8_filename
,
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
);
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
)
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
);
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
)
947 gchar
*locale_name
= utils_get_locale_from_utf8(utf8_filename
);
949 f
= g_fopen(locale_name
, "r");
953 if (fgets(line
, sizeof(line
), f
) != NULL
)
956 return filetypes_detect_from_file_internal(utf8_filename
, line
);
960 return filetypes_detect_from_extension(utf8_filename
);
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
);
971 ft
= filetypes
[GEANY_FILETYPES_NONE
];
973 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft
->priv
->menu_item
), TRUE
);
978 on_filetype_change (GtkCheckMenuItem
*menuitem
,
981 GeanyDocument
*doc
= document_get_current();
982 if (ignore_callback
|| doc
== NULL
|| ! gtk_check_menu_item_get_active(menuitem
))
985 document_set_filetype(doc
, (GeanyFiletype
*)user_data
);
989 static void create_radio_menu_item(GtkWidget
*menu
, GeanyFiletype
*ftype
)
991 static GSList
*group
= NULL
;
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
);
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
);
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
);
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
);
1047 g_object_unref(ft
->icon
);
1049 g_strfreev(ft
->pattern
);
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
];
1071 GError
*error
= NULL
;
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
);
1102 g_error_free(error
);
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 setptr(filetypes
[ft_id
]->context_action_cmd
, result
);
1118 result
= utils_get_setting_string(configh
, "settings", "tag_parser", NULL
);
1120 result
= utils_get_setting_string(config
, "settings", "tag_parser", NULL
);
1123 ft
->lang
= tm_source_file_get_named_lang(result
);
1125 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result
, ft
->name
);
1129 result
= utils_get_setting_string(configh
, "settings", "lexer_filetype", NULL
);
1131 result
= utils_get_setting_string(config
, "settings", "lexer_filetype", NULL
);
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
);
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
,
1155 GError
*error
= NULL
;
1156 gboolean done
= g_key_file_load_from_file(key_file
, file
, flags
, &error
);
1160 if (!done
&& !ft
->priv
->custom
)
1161 geany_debug("Failed to open %s (%s)", file
, error
->message
);
1163 g_error_free(error
);
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
;
1178 g_return_if_fail(ft_id
>= 0 && ft_id
< (gint
) filetypes_array
->len
);
1180 ft
= filetypes
[ft_id
];
1183 /* when reloading, proceed only if the settings were already loaded */
1184 if (reload
&& G_UNLIKELY(! pft
->keyfile_loaded
))
1187 /* when not reloading, load the settings only once */
1188 if (! reload
&& G_LIKELY(pft
->keyfile_loaded
))
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
);
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;
1233 result
= g_ascii_strdown(ft
->name
, -1);
1234 /* truncate at slash (e.g. for Matlab/Octave) */
1235 ptr
= strstr(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
);
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
);
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
);
1266 g_key_file_free(config_home
);
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
;
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
))
1288 for (j
= 0; filetypes
[i
]->pattern
[j
]; j
++)
1290 gtk_file_filter_add_pattern(new_filter
, filetypes
[i
]->pattern
[j
]);
1297 GtkFileFilter
*filetypes_create_file_filter(const GeanyFiletype
*ft
)
1299 GtkFileFilter
*new_filter
;
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
]);
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.
1334 GeanyFiletype
*filetypes_lookup_by_name(const gchar
*name
)
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
);
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))
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
)
1378 regmatch_t pmatch
[3];
1382 doc
= document_get_current();
1384 ft
= doc
->file_type
;
1386 tmp
= build_get_regex(build_info
.grp
, ft
, NULL
);
1390 regex
= &ft
->priv
->error_regex
;
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 */
1406 if (regexec(regex
, message
, G_N_ELEMENTS(pmatch
), pmatch
, 0) != 0)
1409 if (pmatch
[0].rm_so
!= -1 && pmatch
[1].rm_so
!= -1 && pmatch
[2].rm_so
!= -1)
1411 gchar
*first
, *second
, *end
;
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 */
1425 l
= strtol(second
, &end
, 10);
1439 return *filename
!= NULL
;
1444 static void convert_filetype_extensions_to_lower_case(gchar
**patterns
, gsize len
)
1447 for (i
= 0; i
< len
; i
++)
1449 setptr(patterns
[i
], g_ascii_strdown(patterns
[i
], -1));
1455 void filetypes_read_extensions(void)
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
);
1470 for (i
= 0; i
< filetypes_array
->len
; i
++)
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 */
1480 list
= g_new0(gchar
*, 1);
1481 filetypes
[i
]->pattern
= list
;
1484 convert_filetype_extensions_to_lower_case(filetypes
[i
]->pattern
, len
);
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 */
1496 GeanyDocument
*doc
= documents
[i
];
1497 if (doc
->file_type
->id
!= GEANY_FILETYPES_NONE
)
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.
1511 GeanyFiletype
*filetypes_index(gint idx
)
1513 return (idx
>= 0 && idx
< (gint
) filetypes_array
->len
) ? filetypes
[idx
] : NULL
;
1517 void filetypes_reload(void)
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();
1536 /* update document styling */
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.
1550 * @since Geany 0.20 */
1551 const gchar
*filetypes_get_display_name(GeanyFiletype
*ft
)
1553 return ft
->id
== GEANY_FILETYPES_NONE
? _("None") : ft
->name
;