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.
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. */
31 #include <glib/gstdio.h>
34 #include "filetypes.h"
35 #include "filetypesprivate.h"
36 #include "highlighting.h"
38 #include "templates.h"
41 #include "msgwindow.h"
43 #include "sciwrappers.h"
49 #define GEANY_FILETYPE_SEARCH_LINES 2 /* lines of file to search for filetype */
51 GPtrArray
*filetypes_array
= NULL
; /* Dynamic array of filetype pointers */
53 static GHashTable
*filetypes_hash
= NULL
; /* Hash of filetype pointers based on name keys */
55 /** List of filetype pointers sorted by name, but with @c filetypes_index(GEANY_FILETYPES_NONE)
56 * first, as this is usually treated specially.
57 * The list does not change (after filetypes have been initialized), so you can use
58 * @code g_slist_nth_data(filetypes_by_title, n) @endcode and expect the same result at different times.
59 * @see filetypes_get_sorted_by_name(). */
60 GSList
*filetypes_by_title
= NULL
;
63 static void create_radio_menu_item(GtkWidget
*menu
, GeanyFiletype
*ftype
);
65 static gchar
*filetypes_get_conf_extension(const GeanyFiletype
*ft
);
66 static void read_filetype_config(void);
75 /* Save adding many translation strings if the filetype name doesn't need translating */
76 static void filetype_make_title(GeanyFiletype
*ft
, enum TitleType type
)
78 const gchar
*fmt
= NULL
;
83 case TITLE_SOURCE_FILE
: fmt
= _("%s source file"); break;
84 case TITLE_FILE
: fmt
= _("%s file"); break;
88 ft
->title
= g_strdup_printf(fmt
, ft
->name
);
92 /* Note: remember to update HACKING if this function is renamed. */
93 static void init_builtin_filetypes(void)
97 #define NONE /* these macros are only to ease navigation */
98 ft
= filetypes
[GEANY_FILETYPES_NONE
];
99 /* ft->name must not be translated as it is used for filetype lookup.
100 * Use filetypes_get_display_name() instead. */
101 ft
->name
= g_strdup("None");
102 ft
->title
= g_strdup(_("None"));
103 ft
->group
= GEANY_FILETYPE_GROUP_NONE
;
106 ft
= filetypes
[GEANY_FILETYPES_C
];
108 ft
->name
= g_strdup("C");
109 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
110 ft
->mime_type
= g_strdup("text/x-csrc");
111 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
114 ft
= filetypes
[GEANY_FILETYPES_CPP
];
116 ft
->name
= g_strdup("C++");
117 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
118 ft
->mime_type
= g_strdup("text/x-c++src");
119 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
122 ft
= filetypes
[GEANY_FILETYPES_OBJECTIVEC
];
124 ft
->name
= g_strdup("Objective-C");
125 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
126 ft
->mime_type
= g_strdup("text/x-objc");
127 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
130 ft
= filetypes
[GEANY_FILETYPES_CS
];
132 ft
->name
= g_strdup("C#");
133 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
134 ft
->mime_type
= g_strdup("text/x-csharp");
135 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
138 ft
= filetypes
[GEANY_FILETYPES_VALA
];
140 ft
->name
= g_strdup("Vala");
141 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
142 ft
->mime_type
= g_strdup("text/x-vala");
143 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
146 ft
= filetypes
[GEANY_FILETYPES_D
];
148 ft
->name
= g_strdup("D");
149 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
150 ft
->mime_type
= g_strdup("text/x-dsrc");
151 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
154 ft
= filetypes
[GEANY_FILETYPES_JAVA
];
156 ft
->name
= g_strdup("Java");
157 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
158 ft
->mime_type
= g_strdup("text/x-java");
159 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
161 #define PAS /* to avoid warnings when building under Windows, the symbol PASCAL is there defined */
162 ft
= filetypes
[GEANY_FILETYPES_PASCAL
];
164 ft
->name
= g_strdup("Pascal");
165 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
166 ft
->mime_type
= g_strdup("text/x-pascal");
167 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
170 ft
= filetypes
[GEANY_FILETYPES_ASM
];
172 ft
->name
= g_strdup("ASM");
173 ft
->title
= g_strdup_printf(_("%s source file"), "Assembler");
174 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
177 ft
= filetypes
[GEANY_FILETYPES_BASIC
];
179 ft
->name
= g_strdup("FreeBasic");
180 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
181 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
184 ft
= filetypes
[GEANY_FILETYPES_FORTRAN
];
186 ft
->name
= g_strdup("Fortran");
187 ft
->title
= g_strdup_printf(_("%s source file"), "Fortran (F90)");
188 ft
->mime_type
= g_strdup("text/x-fortran");
189 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
192 ft
= filetypes
[GEANY_FILETYPES_F77
];
194 ft
->name
= g_strdup("F77");
195 ft
->title
= g_strdup_printf(_("%s source file"), "Fortran (F77)");
196 ft
->mime_type
= g_strdup("text/x-fortran");
197 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
200 ft
= filetypes
[GEANY_FILETYPES_GLSL
];
202 ft
->name
= g_strdup("GLSL");
203 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
204 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
207 ft
= filetypes
[GEANY_FILETYPES_CAML
];
208 ft
->name
= g_strdup("CAML");
209 ft
->title
= g_strdup_printf(_("%s source file"), "(O)Caml");
210 ft
->mime_type
= g_strdup("text/x-ocaml");
211 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
214 ft
= filetypes
[GEANY_FILETYPES_PERL
];
216 ft
->name
= g_strdup("Perl");
217 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
218 ft
->mime_type
= g_strdup("application/x-perl");
219 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
222 ft
= filetypes
[GEANY_FILETYPES_PHP
];
224 ft
->name
= g_strdup("PHP");
225 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
226 ft
->mime_type
= g_strdup("application/x-php");
227 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
230 ft
= filetypes
[GEANY_FILETYPES_JS
];
232 ft
->name
= g_strdup("Javascript");
233 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
234 ft
->mime_type
= g_strdup("application/javascript");
235 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
238 ft
= filetypes
[GEANY_FILETYPES_PYTHON
];
240 ft
->name
= g_strdup("Python");
241 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
242 ft
->mime_type
= g_strdup("text/x-python");
243 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
246 ft
= filetypes
[GEANY_FILETYPES_RUBY
];
248 ft
->name
= g_strdup("Ruby");
249 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
250 ft
->mime_type
= g_strdup("application/x-ruby");
251 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
254 ft
= filetypes
[GEANY_FILETYPES_TCL
];
256 ft
->name
= g_strdup("Tcl");
257 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
258 ft
->mime_type
= g_strdup("text/x-tcl");
259 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
262 ft
= filetypes
[GEANY_FILETYPES_LUA
];
264 ft
->name
= g_strdup("Lua");
265 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
266 ft
->mime_type
= g_strdup("text/x-lua");
267 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
270 ft
= filetypes
[GEANY_FILETYPES_FERITE
];
272 ft
->name
= g_strdup("Ferite");
273 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
274 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
277 ft
= filetypes
[GEANY_FILETYPES_HASKELL
];
279 ft
->name
= g_strdup("Haskell");
280 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
281 ft
->mime_type
= g_strdup("text/x-haskell");
282 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
285 ft
= filetypes
[GEANY_FILETYPES_MARKDOWN
];
287 ft
->name
= g_strdup("Markdown");
288 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
289 ft
->mime_type
= g_strdup("text/x-markdown");
290 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
293 ft
= filetypes
[GEANY_FILETYPES_TXT2TAGS
];
295 ft
->name
= g_strdup("Txt2tags");
296 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
297 ft
->mime_type
= g_strdup("text/x-txt2tags");
298 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
301 ft
= filetypes
[GEANY_FILETYPES_ABC
];
303 ft
->name
= g_strdup("Abc");
304 filetype_make_title(ft
, TITLE_FILE
);
305 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
308 ft
= filetypes
[GEANY_FILETYPES_SH
];
310 ft
->name
= g_strdup("Sh");
311 ft
->title
= g_strdup(_("Shell script"));
312 ft
->mime_type
= g_strdup("application/x-shellscript");
313 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
316 ft
= filetypes
[GEANY_FILETYPES_MAKE
];
318 ft
->name
= g_strdup("Make");
319 ft
->title
= g_strdup(_("Makefile"));
320 ft
->mime_type
= g_strdup("text/x-makefile");
321 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
324 ft
= filetypes
[GEANY_FILETYPES_XML
];
325 ft
->name
= g_strdup("XML");
326 ft
->title
= g_strdup(_("XML document"));
327 ft
->mime_type
= g_strdup("application/xml");
328 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
331 ft
= filetypes
[GEANY_FILETYPES_DOCBOOK
];
333 ft
->name
= g_strdup("Docbook");
334 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
335 ft
->mime_type
= g_strdup("application/docbook+xml");
336 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
339 ft
= filetypes
[GEANY_FILETYPES_HTML
];
341 ft
->name
= g_strdup("HTML");
342 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
343 ft
->mime_type
= g_strdup("text/html");
344 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
347 ft
= filetypes
[GEANY_FILETYPES_CSS
];
349 ft
->name
= g_strdup("CSS");
350 ft
->title
= g_strdup(_("Cascading StyleSheet"));
351 ft
->mime_type
= g_strdup("text/css");
352 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
; /* not really markup but fit quite well to HTML */
355 ft
= filetypes
[GEANY_FILETYPES_SQL
];
357 ft
->name
= g_strdup("SQL");
358 filetype_make_title(ft
, TITLE_FILE
);
359 ft
->mime_type
= g_strdup("text/x-sql");
360 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
363 ft
= filetypes
[GEANY_FILETYPES_COBOL
];
365 ft
->name
= g_strdup("COBOL");
366 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
367 ft
->mime_type
= g_strdup("text/x-cobol");
368 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
371 ft
= filetypes
[GEANY_FILETYPES_LATEX
];
373 ft
->name
= g_strdup("LaTeX");
374 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
375 ft
->mime_type
= g_strdup("text/x-tex");
376 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
379 ft
= filetypes
[GEANY_FILETYPES_VHDL
];
381 ft
->name
= g_strdup("VHDL");
382 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
383 ft
->mime_type
= g_strdup("text/x-vhdl");
384 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
387 ft
= filetypes
[GEANY_FILETYPES_VERILOG
];
389 ft
->name
= g_strdup("Verilog");
390 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
391 ft
->mime_type
= g_strdup("text/x-verilog");
392 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
395 ft
= filetypes
[GEANY_FILETYPES_DIFF
];
397 ft
->name
= g_strdup("Diff");
398 filetype_make_title(ft
, TITLE_FILE
);
399 ft
->mime_type
= g_strdup("text/x-patch");
400 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
403 ft
= filetypes
[GEANY_FILETYPES_LISP
];
404 ft
->name
= g_strdup("Lisp");
405 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
406 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
409 ft
= filetypes
[GEANY_FILETYPES_ERLANG
];
410 ft
->name
= g_strdup("Erlang");
411 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
412 ft
->mime_type
= g_strdup("text/x-erlang");
413 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
416 ft
= filetypes
[GEANY_FILETYPES_CONF
];
418 ft
->name
= g_strdup("Conf");
419 ft
->title
= g_strdup(_("Config file"));
420 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
423 ft
= filetypes
[GEANY_FILETYPES_PO
];
424 ft
->name
= g_strdup("Po");
425 ft
->title
= g_strdup(_("Gettext translation file"));
426 ft
->mime_type
= g_strdup("text/x-gettext-translation");
427 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
430 ft
= filetypes
[GEANY_FILETYPES_HAXE
];
432 ft
->name
= g_strdup("Haxe");
433 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
434 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
437 ft
= filetypes
[GEANY_FILETYPES_AS
];
439 ft
->name
= g_strdup("ActionScript");
440 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
441 ft
->mime_type
= g_strdup("application/ecmascript");
442 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
445 ft
= filetypes
[GEANY_FILETYPES_R
];
447 ft
->name
= g_strdup("R");
448 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
449 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
452 ft
= filetypes
[GEANY_FILETYPES_REST
];
454 ft
->name
= g_strdup("reStructuredText");
455 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
456 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
459 ft
= filetypes
[GEANY_FILETYPES_MATLAB
];
461 ft
->name
= g_strdup("Matlab/Octave");
462 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
463 ft
->mime_type
= g_strdup("text/x-matlab");
464 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
467 ft
= filetypes
[GEANY_FILETYPES_YAML
];
468 ft
->name
= g_strdup("YAML");
469 filetype_make_title(ft
, TITLE_FILE
);
470 ft
->mime_type
= g_strdup("application/x-yaml");
471 ft
->group
= GEANY_FILETYPE_GROUP_MISC
;
474 ft
= filetypes
[GEANY_FILETYPES_CMAKE
];
475 ft
->name
= g_strdup("CMake");
476 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
477 ft
->mime_type
= g_strdup("text/x-cmake");
478 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
481 ft
= filetypes
[GEANY_FILETYPES_NSIS
];
483 ft
->name
= g_strdup("NSIS");
484 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
485 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
488 ft
= filetypes
[GEANY_FILETYPES_ADA
];
489 ft
->name
= g_strdup("Ada");
490 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
491 ft
->mime_type
= g_strdup("text/x-adasrc");
492 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
495 ft
= filetypes
[GEANY_FILETYPES_FORTH
];
496 ft
->name
= g_strdup("Forth");
497 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
498 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
501 ft
= filetypes
[GEANY_FILETYPES_ASCIIDOC
];
503 ft
->name
= g_strdup("Asciidoc");
504 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
505 ft
->group
= GEANY_FILETYPE_GROUP_MARKUP
;
508 ft
= filetypes
[GEANY_FILETYPES_ABAQUS
];
510 ft
->name
= g_strdup("Abaqus");
511 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
512 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
515 ft
= filetypes
[GEANY_FILETYPES_BATCH
];
516 ft
->name
= g_strdup("Batch");
517 filetype_make_title(ft
, TITLE_FILE
);
518 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
521 ft
= filetypes
[GEANY_FILETYPES_POWERSHELL
];
522 ft
->name
= g_strdup("PowerShell");
523 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
524 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
528 /* initialize fields. */
529 static GeanyFiletype
*filetype_new(void)
531 GeanyFiletype
*ft
= g_new0(GeanyFiletype
, 1);
533 ft
->group
= GEANY_FILETYPE_GROUP_NONE
;
534 ft
->lang
= -2; /* assume no tagmanager parser */
535 /* pattern must not be null */
536 ft
->pattern
= g_new0(gchar
*, 1);
537 ft
->project_list_entry
= -1; /* no entry */
538 ft
->indent_width
= -1;
539 ft
->indent_type
= -1;
541 ft
->priv
= g_new0(GeanyFiletypePrivate
, 1);
546 static gint
cmp_filetype(gconstpointer pft1
, gconstpointer pft2
, gpointer data
)
548 gboolean by_name
= GPOINTER_TO_INT(data
);
549 const GeanyFiletype
*ft1
= pft1
, *ft2
= pft2
;
551 if (G_UNLIKELY(ft1
->id
== GEANY_FILETYPES_NONE
))
553 if (G_UNLIKELY(ft2
->id
== GEANY_FILETYPES_NONE
))
557 utils_str_casecmp(ft1
->name
, ft2
->name
) :
558 utils_str_casecmp(ft1
->title
, ft2
->title
);
562 /** Gets a list of filetype pointers sorted by name.
563 * The list does not change on subsequent calls.
564 * @return The list - do not free.
565 * @see filetypes_by_title. */
566 const GSList
*filetypes_get_sorted_by_name(void)
568 static GSList
*list
= NULL
;
570 g_return_val_if_fail(filetypes_by_title
, NULL
);
574 list
= g_slist_copy(filetypes_by_title
);
575 list
= g_slist_sort_with_data(list
, cmp_filetype
, GINT_TO_POINTER(TRUE
));
581 /* Add a filetype pointer to the lists of available filetypes,
582 * and set the filetype::id field. */
583 static void filetype_add(GeanyFiletype
*ft
)
585 g_return_if_fail(ft
);
586 g_return_if_fail(ft
->name
);
588 ft
->id
= filetypes_array
->len
; /* len will be the index for filetype_array */
589 g_ptr_array_add(filetypes_array
, ft
);
590 g_hash_table_insert(filetypes_hash
, ft
->name
, ft
);
592 /* list will be sorted later */
593 filetypes_by_title
= g_slist_prepend(filetypes_by_title
, ft
);
596 ft
->mime_type
= g_strdup("text/plain");
600 static void add_custom_filetype(const gchar
*filename
)
602 gchar
*fn
= utils_strdupa(strstr(filename
, ".") + 1);
603 gchar
*dot
= g_strrstr(fn
, ".conf");
606 g_return_if_fail(dot
);
610 if (g_hash_table_lookup(filetypes_hash
, fn
))
614 ft
->name
= g_strdup(fn
);
615 filetype_make_title(ft
, TITLE_FILE
);
616 ft
->priv
->custom
= TRUE
;
618 geany_debug("Added filetype %s (%d).", ft
->name
, ft
->id
);
622 static void init_custom_filetypes(const gchar
*path
)
625 const gchar
*filename
;
627 g_return_if_fail(path
);
629 dir
= g_dir_open(path
, 0, NULL
);
633 foreach_dir(filename
, dir
)
635 const gchar prefix
[] = "filetypes.";
637 if (g_str_has_prefix(filename
, prefix
) &&
638 g_str_has_suffix(filename
+ strlen(prefix
), ".conf"))
640 add_custom_filetype(filename
);
647 /* Create the filetypes array and fill it with the known filetypes.
648 * Warning: GTK isn't necessarily initialized yet. */
649 void filetypes_init_types()
654 g_return_if_fail(filetypes_array
== NULL
);
655 g_return_if_fail(filetypes_hash
== NULL
);
657 filetypes_array
= g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES
);
658 filetypes_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
660 /* Create built-in filetypes */
661 for (ft_id
= 0; ft_id
< GEANY_MAX_BUILT_IN_FILETYPES
; ft_id
++)
663 filetypes
[ft_id
] = filetype_new();
665 init_builtin_filetypes();
667 /* Add built-in filetypes to the hash now the name fields are set */
668 for (ft_id
= 0; ft_id
< GEANY_MAX_BUILT_IN_FILETYPES
; ft_id
++)
670 filetype_add(filetypes
[ft_id
]);
672 init_custom_filetypes(app
->datadir
);
673 f
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, NULL
);
674 init_custom_filetypes(f
);
677 /* sort last instead of on insertion to prevent exponential time */
678 filetypes_by_title
= g_slist_sort_with_data(filetypes_by_title
,
679 cmp_filetype
, GINT_TO_POINTER(FALSE
));
681 read_filetype_config();
685 static void on_document_save(G_GNUC_UNUSED GObject
*object
, GeanyDocument
*doc
)
689 g_return_if_fail(!EMPTY(doc
->real_path
));
691 f
= g_build_filename(app
->configdir
, "filetype_extensions.conf", NULL
);
692 if (utils_str_equal(doc
->real_path
, f
))
693 filetypes_reload_extensions();
696 f
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, "filetypes.common", NULL
);
697 if (utils_str_equal(doc
->real_path
, f
))
701 /* Note: we don't reload other filetypes, even though the named styles may have changed.
702 * The user can do this manually with 'Tools->Reload Configuration' */
703 filetypes_load_config(GEANY_FILETYPES_NONE
, TRUE
);
706 document_reload_config(documents
[i
]);
712 static void setup_config_file_menus(void)
716 f
= g_build_filename(app
->configdir
, "filetype_extensions.conf", NULL
);
717 ui_add_config_file_menu_item(f
, NULL
, NULL
);
718 SETPTR(f
, g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, "filetypes.common", NULL
));
719 ui_add_config_file_menu_item(f
, NULL
, NULL
);
722 g_signal_connect(geany_object
, "document-save", G_CALLBACK(on_document_save
), NULL
);
726 static GtkWidget
*group_menus
[GEANY_FILETYPE_GROUP_COUNT
] = {NULL
};
728 static void create_sub_menu(GtkWidget
*parent
, gsize group_id
, const gchar
*title
)
730 GtkWidget
*menu
, *item
;
732 menu
= gtk_menu_new();
733 item
= gtk_menu_item_new_with_mnemonic((title
));
734 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), menu
);
735 gtk_container_add(GTK_CONTAINER(parent
), item
);
736 gtk_widget_show(item
);
737 group_menus
[group_id
] = menu
;
741 static void create_set_filetype_menu(void)
744 GtkWidget
*filetype_menu
= ui_lookup_widget(main_widgets
.window
, "set_filetype1_menu");
746 create_sub_menu(filetype_menu
, GEANY_FILETYPE_GROUP_COMPILED
, _("_Programming Languages"));
747 create_sub_menu(filetype_menu
, GEANY_FILETYPE_GROUP_SCRIPT
, _("_Scripting Languages"));
748 create_sub_menu(filetype_menu
, GEANY_FILETYPE_GROUP_MARKUP
, _("_Markup Languages"));
749 create_sub_menu(filetype_menu
, GEANY_FILETYPE_GROUP_MISC
, _("M_iscellaneous"));
751 /* Append all filetypes to the filetype menu */
752 foreach_slist(node
, filetypes_by_title
)
754 GeanyFiletype
*ft
= node
->data
;
756 if (ft
->group
!= GEANY_FILETYPE_GROUP_NONE
)
757 create_radio_menu_item(group_menus
[ft
->group
], ft
);
759 create_radio_menu_item(filetype_menu
, ft
);
764 void filetypes_init()
768 filetypes_init_types();
770 /* this has to be here as GTK isn't initialized in filetypes_init_types(). */
771 foreach_slist(node
, filetypes_by_title
)
773 GeanyFiletype
*ft
= node
->data
;
774 ft
->icon
= ui_get_mime_icon(ft
->mime_type
, GTK_ICON_SIZE_MENU
);
776 create_set_filetype_menu();
777 setup_config_file_menus();
781 /* Find a filetype that predicate returns TRUE for, otherwise return NULL. */
782 GeanyFiletype
*filetypes_find(GCompareFunc predicate
, gpointer user_data
)
786 for (i
= 0; i
< filetypes_array
->len
; i
++)
788 GeanyFiletype
*ft
= filetypes
[i
];
790 if (predicate(ft
, user_data
))
797 static gboolean
match_basename(gconstpointer pft
, gconstpointer user_data
)
799 const GeanyFiletype
*ft
= pft
;
800 const gchar
*base_filename
= user_data
;
802 gboolean ret
= FALSE
;
804 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
807 for (j
= 0; ft
->pattern
[j
] != NULL
; j
++)
809 GPatternSpec
*pattern
= g_pattern_spec_new(ft
->pattern
[j
]);
811 if (g_pattern_match_string(pattern
, base_filename
))
814 g_pattern_spec_free(pattern
);
817 g_pattern_spec_free(pattern
);
823 static GeanyFiletype
*check_builtin_filenames(const gchar
*utf8_filename
)
827 gboolean found
= FALSE
;
830 /* use lower case basename */
831 lfn
= g_utf8_strdown(utf8_filename
, -1);
833 lfn
= g_strdup(utf8_filename
);
835 SETPTR(lfn
, utils_get_locale_from_utf8(lfn
));
837 path
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, "filetypes.", NULL
);
838 if (g_str_has_prefix(lfn
, path
))
841 SETPTR(path
, g_build_filename(app
->datadir
, "filetypes.", NULL
));
842 if (g_str_has_prefix(lfn
, path
))
847 return found
? filetypes
[GEANY_FILETYPES_CONF
] : NULL
;
851 /* Detect filetype only based on the filename extension.
852 * utf8_filename can include the full path. */
853 GeanyFiletype
*filetypes_detect_from_extension(const gchar
*utf8_filename
)
855 gchar
*base_filename
;
858 ft
= check_builtin_filenames(utf8_filename
);
862 /* to match against the basename of the file (because of Makefile*) */
863 base_filename
= g_path_get_basename(utf8_filename
);
865 /* use lower case basename */
866 SETPTR(base_filename
, g_utf8_strdown(base_filename
, -1));
869 ft
= filetypes_find(match_basename
, base_filename
);
871 ft
= filetypes
[GEANY_FILETYPES_NONE
];
873 g_free(base_filename
);
878 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
880 * The detected filetype of the file is checked against every id in the passed list and if
881 * there is a match, TRUE is returned. */
882 static gboolean
shebang_find_and_match_filetype(const gchar
*utf8_filename
, gint first
, ...)
884 GeanyFiletype
*ft
= NULL
;
886 gboolean result
= FALSE
;
889 ft
= filetypes_detect_from_extension(utf8_filename
);
890 if (ft
== NULL
|| ft
->id
>= filetypes_array
->len
)
893 va_start(args
, first
);
900 if (ft
->id
== (guint
) test
)
905 test
= va_arg(args
, gint
);
913 static GeanyFiletype
*find_shebang(const gchar
*utf8_filename
, const gchar
*line
)
915 GeanyFiletype
*ft
= NULL
;
917 if (strlen(line
) > 2 && line
[0] == '#' && line
[1] == '!')
919 static const struct {
921 filetype_id filetype
;
922 } intepreter_map
[] = {
923 { "sh", GEANY_FILETYPES_SH
},
924 { "bash", GEANY_FILETYPES_SH
},
925 { "dash", GEANY_FILETYPES_SH
},
926 { "perl", GEANY_FILETYPES_PERL
},
927 { "python", GEANY_FILETYPES_PYTHON
},
928 { "php", GEANY_FILETYPES_PHP
},
929 { "ruby", GEANY_FILETYPES_RUBY
},
930 { "tcl", GEANY_FILETYPES_TCL
},
931 { "make", GEANY_FILETYPES_MAKE
},
932 { "zsh", GEANY_FILETYPES_SH
},
933 { "ksh", GEANY_FILETYPES_SH
},
934 { "mksh", GEANY_FILETYPES_SH
},
935 { "csh", GEANY_FILETYPES_SH
},
936 { "tcsh", GEANY_FILETYPES_SH
},
937 { "ash", GEANY_FILETYPES_SH
},
938 { "dmd", GEANY_FILETYPES_D
},
939 { "wish", GEANY_FILETYPES_TCL
},
940 { "node", GEANY_FILETYPES_JS
}
942 gchar
*tmp
= g_path_get_basename(line
+ 2);
943 gchar
*basename_interpreter
= tmp
;
946 if (g_str_has_prefix(tmp
, "env "))
947 { /* skip "env" and read the following interpreter */
948 basename_interpreter
+= 4;
951 for (i
= 0; ! ft
&& i
< G_N_ELEMENTS(intepreter_map
); i
++)
953 if (g_str_has_prefix(basename_interpreter
, intepreter_map
[i
].name
))
954 ft
= filetypes
[intepreter_map
[i
].filetype
];
958 /* detect HTML files */
959 if (g_str_has_prefix(line
, "<!DOCTYPE html") || g_str_has_prefix(line
, "<html"))
961 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
962 * extension and use the detected filetype, else assume HTML */
963 if (! shebang_find_and_match_filetype(utf8_filename
,
964 GEANY_FILETYPES_PERL
, GEANY_FILETYPES_PHP
, GEANY_FILETYPES_PYTHON
, -1))
966 ft
= filetypes
[GEANY_FILETYPES_HTML
];
969 /* detect XML files */
970 else if (utf8_filename
&& g_str_has_prefix(line
, "<?xml"))
972 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
973 * extension and use the detected filetype, else assume XML */
974 if (! shebang_find_and_match_filetype(utf8_filename
,
975 GEANY_FILETYPES_HTML
, GEANY_FILETYPES_DOCBOOK
,
976 /* Perl, Python and PHP only to be safe */
977 GEANY_FILETYPES_PERL
, GEANY_FILETYPES_PHP
, GEANY_FILETYPES_PYTHON
, -1))
979 ft
= filetypes
[GEANY_FILETYPES_XML
];
982 else if (g_str_has_prefix(line
, "<?php"))
984 ft
= filetypes
[GEANY_FILETYPES_PHP
];
990 /* Detect the filetype checking for a shebang, then filename extension.
991 * @lines: an strv of the lines to scan (must containing at least one line) */
992 static GeanyFiletype
*filetypes_detect_from_file_internal(const gchar
*utf8_filename
,
999 GError
*regex_error
= NULL
;
1001 /* try to find a shebang and if found use it prior to the filename extension
1002 * also checks for <?xml */
1003 ft
= find_shebang(utf8_filename
, lines
[0]);
1007 /* try to extract the filetype using a regex capture */
1008 ft_regex
= g_regex_new(file_prefs
.extract_filetype_regex
,
1009 G_REGEX_RAW
| G_REGEX_MULTILINE
, 0, ®ex_error
);
1010 if (ft_regex
!= NULL
)
1012 for (i
= 0; ft
== NULL
&& lines
[i
] != NULL
; i
++)
1014 if (g_regex_match(ft_regex
, lines
[i
], 0, &match
))
1016 gchar
*capture
= g_match_info_fetch(match
, 1);
1017 if (capture
!= NULL
)
1019 ft
= filetypes_lookup_by_name(capture
);
1023 g_match_info_free(match
);
1025 g_regex_unref(ft_regex
);
1027 else if (regex_error
!= NULL
)
1029 geany_debug("Filetype extract regex ignored: %s", regex_error
->message
);
1030 g_error_free(regex_error
);
1035 if (utf8_filename
== NULL
)
1036 return filetypes
[GEANY_FILETYPES_NONE
];
1038 return filetypes_detect_from_extension(utf8_filename
);
1042 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
1043 GeanyFiletype
*filetypes_detect_from_document(GeanyDocument
*doc
)
1046 gchar
*lines
[GEANY_FILETYPE_SEARCH_LINES
+ 1];
1050 return filetypes
[GEANY_FILETYPES_NONE
];
1052 for (i
= 0; i
< GEANY_FILETYPE_SEARCH_LINES
; ++i
)
1054 lines
[i
] = sci_get_line(doc
->editor
->sci
, i
);
1057 ft
= filetypes_detect_from_file_internal(doc
->file_name
, lines
);
1058 for (i
= 0; i
< GEANY_FILETYPE_SEARCH_LINES
; ++i
)
1067 /* Currently only used by external plugins (e.g. geanyprj). */
1069 * Detects filetype based on a shebang line in the file or the filename extension.
1071 * @param utf8_filename The filename in UTF-8 encoding.
1073 * @return The detected filetype for @a utf8_filename or @c filetypes[GEANY_FILETYPES_NONE]
1074 * if it could not be detected.
1076 GeanyFiletype
*filetypes_detect_from_file(const gchar
*utf8_filename
)
1081 gchar
*locale_name
= utils_get_locale_from_utf8(utf8_filename
);
1083 f
= g_fopen(locale_name
, "r");
1084 g_free(locale_name
);
1087 if (fgets(line
, sizeof(line
), f
) != NULL
)
1092 return filetypes_detect_from_file_internal(utf8_filename
, lines
);
1096 return filetypes_detect_from_extension(utf8_filename
);
1101 void filetypes_select_radio_item(const GeanyFiletype
*ft
)
1103 /* ignore_callback has to be set by the caller */
1104 g_return_if_fail(ignore_callback
);
1107 ft
= filetypes
[GEANY_FILETYPES_NONE
];
1109 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft
->priv
->menu_item
), TRUE
);
1114 on_filetype_change(GtkCheckMenuItem
*menuitem
, gpointer user_data
)
1116 GeanyDocument
*doc
= document_get_current();
1117 if (ignore_callback
|| doc
== NULL
|| ! gtk_check_menu_item_get_active(menuitem
))
1120 document_set_filetype(doc
, (GeanyFiletype
*)user_data
);
1124 static void create_radio_menu_item(GtkWidget
*menu
, GeanyFiletype
*ftype
)
1126 static GSList
*group
= NULL
;
1129 tmp
= gtk_radio_menu_item_new_with_label(group
, ftype
->title
);
1130 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp
));
1131 ftype
->priv
->menu_item
= tmp
;
1132 gtk_widget_show(tmp
);
1133 gtk_container_add(GTK_CONTAINER(menu
), tmp
);
1134 g_signal_connect(tmp
, "activate", G_CALLBACK(on_filetype_change
), (gpointer
) ftype
);
1138 static void filetype_free(gpointer data
, G_GNUC_UNUSED gpointer user_data
)
1140 GeanyFiletype
*ft
= data
;
1142 g_return_if_fail(ft
!= NULL
);
1146 g_free(ft
->extension
);
1147 g_free(ft
->mime_type
);
1148 g_free(ft
->comment_open
);
1149 g_free(ft
->comment_close
);
1150 g_free(ft
->comment_single
);
1151 g_free(ft
->context_action_cmd
);
1152 g_free(ft
->filecmds
);
1153 g_free(ft
->ftdefcmds
);
1154 g_free(ft
->execcmds
);
1155 g_free(ft
->error_regex_string
);
1157 g_object_unref(ft
->icon
);
1158 g_strfreev(ft
->pattern
);
1160 if (ft
->priv
->error_regex
)
1161 g_regex_unref(ft
->priv
->error_regex
);
1162 g_slist_foreach(ft
->priv
->tag_files
, (GFunc
) g_free
, NULL
);
1163 g_slist_free(ft
->priv
->tag_files
);
1170 /* frees the array and all related pointers */
1171 void filetypes_free_types(void)
1173 g_return_if_fail(filetypes_array
!= NULL
);
1174 g_return_if_fail(filetypes_hash
!= NULL
);
1176 g_ptr_array_foreach(filetypes_array
, filetype_free
, NULL
);
1177 g_ptr_array_free(filetypes_array
, TRUE
);
1178 g_hash_table_destroy(filetypes_hash
);
1182 static void load_indent_settings(GeanyFiletype
*ft
, GKeyFile
*config
, GKeyFile
*configh
)
1184 ft
->indent_width
= utils_get_setting(integer
, configh
, config
, "indentation", "width", -1);
1185 ft
->indent_type
= utils_get_setting(integer
, configh
, config
, "indentation", "type", -1);
1186 /* check whether the indent type is OK */
1187 switch (ft
->indent_type
)
1189 case GEANY_INDENT_TYPE_TABS
:
1190 case GEANY_INDENT_TYPE_SPACES
:
1191 case GEANY_INDENT_TYPE_BOTH
:
1196 g_warning("Invalid indent type %d in file type %s", ft
->indent_type
, ft
->name
);
1197 ft
->indent_type
= -1;
1203 static void load_settings(guint ft_id
, GKeyFile
*config
, GKeyFile
*configh
)
1205 GeanyFiletype
*ft
= filetypes
[ft_id
];
1208 /* default extension */
1209 result
= utils_get_setting(string
, configh
, config
, "settings", "extension", NULL
);
1212 SETPTR(filetypes
[ft_id
]->extension
, result
);
1215 /* read comment notes */
1216 result
= utils_get_setting(string
, configh
, config
, "settings", "comment_open", NULL
);
1219 SETPTR(filetypes
[ft_id
]->comment_open
, result
);
1222 result
= utils_get_setting(string
, configh
, config
, "settings", "comment_close", NULL
);
1225 SETPTR(filetypes
[ft_id
]->comment_close
, result
);
1228 result
= utils_get_setting(string
, configh
, config
, "settings", "comment_single", NULL
);
1231 SETPTR(filetypes
[ft_id
]->comment_single
, result
);
1233 /* import correctly filetypes that use old-style single comments */
1234 else if (EMPTY(filetypes
[ft_id
]->comment_close
))
1236 SETPTR(filetypes
[ft_id
]->comment_single
, filetypes
[ft_id
]->comment_open
);
1237 filetypes
[ft_id
]->comment_open
= NULL
;
1240 filetypes
[ft_id
]->comment_use_indent
= utils_get_setting(boolean
, configh
, config
,
1241 "settings", "comment_use_indent", FALSE
);
1243 /* read context action */
1244 result
= utils_get_setting(string
, configh
, config
, "settings", "context_action_cmd", NULL
);
1247 SETPTR(filetypes
[ft_id
]->context_action_cmd
, result
);
1250 result
= utils_get_setting(string
, configh
, config
, "settings", "tag_parser", NULL
);
1253 ft
->lang
= tm_source_file_get_named_lang(result
);
1255 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result
, ft
->name
);
1259 result
= utils_get_setting(string
, configh
, config
, "settings", "lexer_filetype", NULL
);
1262 ft
->lexer_filetype
= filetypes_lookup_by_name(result
);
1263 if (!ft
->lexer_filetype
)
1264 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result
, ft
->name
);
1268 ft
->priv
->symbol_list_sort_mode
= utils_get_setting(integer
, configh
, config
, "settings",
1269 "symbol_list_sort_mode", SYMBOLS_SORT_BY_NAME
);
1270 ft
->priv
->xml_indent_tags
= utils_get_setting(boolean
, configh
, config
, "settings",
1271 "xml_indent_tags", FALSE
);
1273 /* read indent settings */
1274 load_indent_settings(ft
, config
, configh
);
1276 /* read build settings */
1277 build_load_menu(config
, GEANY_BCS_FT
, (gpointer
)ft
);
1278 build_load_menu(configh
, GEANY_BCS_HOME_FT
, (gpointer
)ft
);
1282 static void add_keys(GKeyFile
*dest
, const gchar
*group
, GKeyFile
*src
)
1284 gchar
**keys
= g_key_file_get_keys(src
, group
, NULL
, NULL
);
1287 foreach_strv(ptr
, keys
)
1290 gchar
*value
= g_key_file_get_value(src
, group
, key
, NULL
);
1292 g_key_file_set_value(dest
, group
, key
, value
);
1299 static gchar
*filetypes_get_filename(GeanyFiletype
*ft
, gboolean user
)
1301 gchar
*ext
= filetypes_get_conf_extension(ft
);
1302 gchar
*base_name
= g_strconcat("filetypes.", ext
, NULL
);
1306 file_name
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, base_name
, NULL
);
1308 file_name
= g_build_filename(app
->datadir
, base_name
, NULL
);
1317 static void add_group_keys(GKeyFile
*kf
, const gchar
*group
, GeanyFiletype
*ft
)
1320 gboolean loaded
= FALSE
;
1323 files
[0] = filetypes_get_filename(ft
, FALSE
);
1324 files
[1] = filetypes_get_filename(ft
, TRUE
);
1326 for (i
= 0; i
< G_N_ELEMENTS(files
); i
++)
1328 GKeyFile
*src
= g_key_file_new();
1330 if (g_key_file_load_from_file(src
, files
[i
], G_KEY_FILE_NONE
, NULL
))
1332 add_keys(kf
, group
, src
);
1335 g_key_file_free(src
);
1339 geany_debug("Could not read config file %s for [%s=%s]!", files
[0], group
, ft
->name
);
1346 static void copy_ft_groups(GKeyFile
*kf
)
1348 gchar
**groups
= g_key_file_get_groups(kf
, NULL
);
1351 foreach_strv(ptr
, groups
)
1353 gchar
*group
= *ptr
;
1354 gchar
*name
= strstr(*ptr
, "=");
1360 /* terminate group at '=' */
1366 ft
= filetypes_lookup_by_name(name
);
1368 add_group_keys(kf
, group
, ft
);
1374 /* simple wrapper function to print file errors in DEBUG mode */
1375 static void load_system_keyfile(GKeyFile
*key_file
, const gchar
*file
, GKeyFileFlags flags
,
1378 GError
*error
= NULL
;
1379 gboolean done
= g_key_file_load_from_file(key_file
, file
, flags
, &error
);
1383 if (!done
&& !ft
->priv
->custom
)
1384 geany_debug("Failed to open %s (%s)", file
, error
->message
);
1386 g_error_free(error
);
1392 /* Load the configuration file for the associated filetype id.
1393 * This should only be called when the filetype is needed, to save loading
1394 * 20+ configuration files all at once. */
1395 void filetypes_load_config(guint ft_id
, gboolean reload
)
1397 GKeyFile
*config
, *config_home
;
1398 GeanyFiletypePrivate
*pft
;
1401 g_return_if_fail(ft_id
< filetypes_array
->len
);
1403 ft
= filetypes
[ft_id
];
1406 /* when reloading, proceed only if the settings were already loaded */
1407 if (G_UNLIKELY(reload
&& ! pft
->keyfile_loaded
))
1410 /* when not reloading, load the settings only once */
1411 if (G_LIKELY(! reload
&& pft
->keyfile_loaded
))
1413 pft
->keyfile_loaded
= TRUE
;
1415 config
= g_key_file_new();
1416 config_home
= g_key_file_new();
1418 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1421 f
= filetypes_get_filename(ft
, FALSE
);
1422 load_system_keyfile(config
, f
, G_KEY_FILE_KEEP_COMMENTS
, ft
);
1424 SETPTR(f
, filetypes_get_filename(ft
, TRUE
));
1425 g_key_file_load_from_file(config_home
, f
, G_KEY_FILE_KEEP_COMMENTS
, NULL
);
1428 /* Copy keys for any groups with [group=C] from system keyfile */
1429 copy_ft_groups(config
);
1430 copy_ft_groups(config_home
);
1432 load_settings(ft_id
, config
, config_home
);
1433 highlighting_init_styles(ft_id
, config
, config_home
);
1435 g_key_file_free(config
);
1436 g_key_file_free(config_home
);
1440 static gchar
*filetypes_get_conf_extension(const GeanyFiletype
*ft
)
1444 if (ft
->priv
->custom
)
1445 return g_strconcat(ft
->name
, ".conf", NULL
);
1447 /* Handle any special extensions different from lowercase filetype->name */
1450 case GEANY_FILETYPES_CPP
: result
= g_strdup("cpp"); break;
1451 case GEANY_FILETYPES_CS
: result
= g_strdup("cs"); break;
1452 case GEANY_FILETYPES_MAKE
: result
= g_strdup("makefile"); break;
1453 case GEANY_FILETYPES_NONE
: result
= g_strdup("common"); break;
1454 /* name is Matlab/Octave */
1455 case GEANY_FILETYPES_MATLAB
: result
= g_strdup("matlab"); break;
1456 /* name is Objective-C, and we don't want the hyphen */
1457 case GEANY_FILETYPES_OBJECTIVEC
: result
= g_strdup("objectivec"); break;
1459 result
= g_ascii_strdown(ft
->name
, -1);
1466 void filetypes_save_commands(GeanyFiletype
*ft
)
1468 GKeyFile
*config_home
;
1469 gchar
*fname
, *data
;
1471 fname
= filetypes_get_filename(ft
, TRUE
);
1472 config_home
= g_key_file_new();
1473 g_key_file_load_from_file(config_home
, fname
, G_KEY_FILE_KEEP_COMMENTS
, NULL
);
1474 build_save_menu(config_home
, ft
, GEANY_BCS_HOME_FT
);
1475 data
= g_key_file_to_data(config_home
, NULL
, NULL
);
1476 utils_write_file(fname
, data
);
1478 g_key_file_free(config_home
);
1483 /* create one file filter which has each file pattern of each filetype */
1484 GtkFileFilter
*filetypes_create_file_filter_all_source(void)
1486 GtkFileFilter
*new_filter
;
1489 new_filter
= gtk_file_filter_new();
1490 gtk_file_filter_set_name(new_filter
, _("All Source"));
1492 for (i
= 0; i
< filetypes_array
->len
; i
++)
1494 if (G_UNLIKELY(i
== GEANY_FILETYPES_NONE
))
1497 for (j
= 0; filetypes
[i
]->pattern
[j
]; j
++)
1499 gtk_file_filter_add_pattern(new_filter
, filetypes
[i
]->pattern
[j
]);
1506 GtkFileFilter
*filetypes_create_file_filter(const GeanyFiletype
*ft
)
1508 GtkFileFilter
*new_filter
;
1512 g_return_val_if_fail(ft
!= NULL
, NULL
);
1514 new_filter
= gtk_file_filter_new();
1515 title
= ft
->id
== GEANY_FILETYPES_NONE
? _("All files") : ft
->title
;
1516 gtk_file_filter_set_name(new_filter
, title
);
1518 for (i
= 0; ft
->pattern
[i
]; i
++)
1520 gtk_file_filter_add_pattern(new_filter
, ft
->pattern
[i
]);
1527 /* Indicates whether there is a tag parser for the filetype or not.
1528 * Only works for custom filetypes if the filetype settings have been loaded. */
1529 gboolean
filetype_has_tags(GeanyFiletype
*ft
)
1531 g_return_val_if_fail(ft
!= NULL
, FALSE
);
1533 return ft
->lang
>= 0;
1537 /** Finds a filetype pointer from its @a name field.
1538 * @param name Filetype name.
1539 * @return The filetype found, or @c NULL.
1543 GeanyFiletype
*filetypes_lookup_by_name(const gchar
*name
)
1547 g_return_val_if_fail(!EMPTY(name
), NULL
);
1549 ft
= g_hash_table_lookup(filetypes_hash
, name
);
1550 if (G_UNLIKELY(ft
== NULL
))
1551 geany_debug("Could not find filetype '%s'.", name
);
1556 static void compile_regex(GeanyFiletype
*ft
, gchar
*regstr
)
1558 GError
*error
= NULL
;
1559 GRegex
*regex
= g_regex_new(regstr
, 0, 0, &error
);
1563 ui_set_statusbar(TRUE
, _("Bad regex for filetype %s: %s"),
1564 filetypes_get_display_name(ft
), error
->message
);
1565 g_error_free(error
);
1567 if (ft
->priv
->error_regex
)
1568 g_regex_unref(ft
->priv
->error_regex
);
1569 ft
->priv
->error_regex
= regex
;
1573 gboolean
filetypes_parse_error_message(GeanyFiletype
*ft
, const gchar
*message
,
1574 gchar
**filename
, gint
*line
)
1583 doc
= document_get_current();
1585 ft
= doc
->file_type
;
1587 tmp
= build_get_regex(build_info
.grp
, ft
, NULL
);
1595 if (G_UNLIKELY(EMPTY(regstr
)))
1598 if (!ft
->priv
->error_regex
|| regstr
!= ft
->priv
->last_error_pattern
)
1600 compile_regex(ft
, regstr
);
1601 ft
->priv
->last_error_pattern
= regstr
;
1603 if (!ft
->priv
->error_regex
)
1606 if (!g_regex_match(ft
->priv
->error_regex
, message
, 0, &minfo
))
1608 g_match_info_free(minfo
);
1611 if (g_match_info_get_match_count(minfo
) >= 3)
1613 gchar
*first
, *second
, *end
;
1616 first
= g_match_info_fetch(minfo
, 1);
1617 second
= g_match_info_fetch(minfo
, 2);
1618 l
= strtol(first
, &end
, 10);
1619 if (*end
== '\0') /* first is purely decimals */
1627 l
= strtol(second
, &end
, 10);
1641 g_match_info_free(minfo
);
1642 return *filename
!= NULL
;
1647 static void convert_filetype_extensions_to_lower_case(gchar
**patterns
, gsize len
)
1650 for (i
= 0; i
< len
; i
++)
1652 SETPTR(patterns
[i
], g_ascii_strdown(patterns
[i
], -1));
1658 static void read_extensions(GKeyFile
*sysconfig
, GKeyFile
*userconfig
)
1664 for (i
= 0; i
< filetypes_array
->len
; i
++)
1667 g_key_file_has_key(userconfig
, "Extensions", filetypes
[i
]->name
, NULL
);
1668 gchar
**list
= g_key_file_get_string_list(
1669 (userset
) ? userconfig
: sysconfig
, "Extensions", filetypes
[i
]->name
, &len
, NULL
);
1671 g_strfreev(filetypes
[i
]->pattern
);
1672 /* Note: we allow 'Foo=' to remove all patterns */
1674 list
= g_new0(gchar
*, 1);
1675 filetypes
[i
]->pattern
= list
;
1678 convert_filetype_extensions_to_lower_case(filetypes
[i
]->pattern
, len
);
1684 static void read_group(GKeyFile
*config
, const gchar
*group_name
, gint group_id
)
1686 gchar
**names
= g_key_file_get_string_list(config
, "Groups", group_name
, NULL
, NULL
);
1689 foreach_strv(name
, names
)
1691 GeanyFiletype
*ft
= filetypes_lookup_by_name(*name
);
1695 ft
->group
= group_id
;
1696 if (ft
->priv
->custom
&&
1697 (group_id
== GEANY_FILETYPE_GROUP_COMPILED
|| group_id
== GEANY_FILETYPE_GROUP_SCRIPT
))
1699 SETPTR(ft
->title
, NULL
);
1700 filetype_make_title(ft
, TITLE_SOURCE_FILE
);
1704 geany_debug("Filetype '%s' not found for group '%s'!", *name
, group_name
);
1710 static void read_groups(GKeyFile
*config
)
1712 read_group(config
, "Programming", GEANY_FILETYPE_GROUP_COMPILED
);
1713 read_group(config
, "Script", GEANY_FILETYPE_GROUP_SCRIPT
);
1714 read_group(config
, "Markup", GEANY_FILETYPE_GROUP_MARKUP
);
1715 read_group(config
, "Misc", GEANY_FILETYPE_GROUP_MISC
);
1716 read_group(config
, "None", GEANY_FILETYPE_GROUP_NONE
);
1720 static void read_filetype_config(void)
1722 gchar
*sysconfigfile
= g_build_filename(app
->datadir
, "filetype_extensions.conf", NULL
);
1723 gchar
*userconfigfile
= g_build_filename(app
->configdir
, "filetype_extensions.conf", NULL
);
1724 GKeyFile
*sysconfig
= g_key_file_new();
1725 GKeyFile
*userconfig
= g_key_file_new();
1727 g_key_file_load_from_file(sysconfig
, sysconfigfile
, G_KEY_FILE_NONE
, NULL
);
1728 g_key_file_load_from_file(userconfig
, userconfigfile
, G_KEY_FILE_NONE
, NULL
);
1730 read_extensions(sysconfig
, userconfig
);
1731 read_groups(sysconfig
);
1732 read_groups(userconfig
);
1734 g_free(sysconfigfile
);
1735 g_free(userconfigfile
);
1736 g_key_file_free(sysconfig
);
1737 g_key_file_free(userconfig
);
1741 void filetypes_reload_extensions(void)
1745 read_filetype_config();
1747 /* Redetect filetype of any documents with none set */
1750 GeanyDocument
*doc
= documents
[i
];
1751 if (doc
->file_type
->id
!= GEANY_FILETYPES_NONE
)
1753 document_set_filetype(doc
, filetypes_detect_from_document(doc
));
1758 /** Accessor function for @ref GeanyData::filetypes_array items.
1759 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1760 * @param idx @c filetypes_array index.
1761 * @return The filetype, or @c NULL if @a idx is out of range.
1765 GeanyFiletype
*filetypes_index(gint idx
)
1767 return (idx
>= 0 && idx
< (gint
) filetypes_array
->len
) ? filetypes
[idx
] : NULL
;
1771 void filetypes_reload(void)
1774 GeanyDocument
*current_doc
;
1776 /* reload filetype configs */
1777 for (i
= 0; i
< filetypes_array
->len
; i
++)
1779 /* filetypes_load_config() will skip not loaded filetypes */
1780 filetypes_load_config(i
, TRUE
);
1783 current_doc
= document_get_current();
1787 /* update document styling */
1790 if (current_doc
!= documents
[i
])
1791 document_reload_config(documents
[i
]);
1793 /* process the current document at last */
1794 document_reload_config(current_doc
);
1798 /** Gets @c ft->name or a translation for filetype None.
1801 * @since Geany 0.20 */
1802 const gchar
*filetypes_get_display_name(GeanyFiletype
*ft
)
1804 return ft
->id
== GEANY_FILETYPES_NONE
? _("None") : ft
->name
;
1808 /* gets comment_open/comment_close/comment_single strings from the filetype
1809 * @param single_first: whether single comment is preferred if both available
1810 * returns true if at least comment_open is set, false otherwise */
1811 gboolean
filetype_get_comment_open_close(const GeanyFiletype
*ft
, gboolean single_first
,
1812 const gchar
**co
, const gchar
**cc
)
1814 g_return_val_if_fail(ft
!= NULL
, FALSE
);
1815 g_return_val_if_fail(co
!= NULL
, FALSE
);
1816 g_return_val_if_fail(cc
!= NULL
, FALSE
);
1820 *co
= ft
->comment_single
;
1825 *co
= ft
->comment_open
;
1826 *cc
= ft
->comment_close
;
1831 *co
= ft
->comment_open
;
1833 *cc
= ft
->comment_close
;
1836 *co
= ft
->comment_single
;