Add PowerShell filetype
[geany-mirror.git] / src / filetypes.c
blobbc676def20986fe9572abc362108c1bd14391275
1 /*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /**
23 * @file filetypes.h
24 * Filetype detection, file extensions and filetype menu items.
27 /* Note: we use filetype_id for some function arguments, but GeanyFiletype is better; we should
28 * only use GeanyFiletype for API functions. */
30 #include <string.h>
31 #include <glib/gstdio.h>
33 #include "geany.h"
34 #include "filetypes.h"
35 #include "filetypesprivate.h"
36 #include "highlighting.h"
37 #include "support.h"
38 #include "templates.h"
39 #include "document.h"
40 #include "editor.h"
41 #include "msgwindow.h"
42 #include "utils.h"
43 #include "sciwrappers.h"
44 #include "ui_utils.h"
45 #include "symbols.h"
47 #include <stdlib.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);
69 enum TitleType
71 TITLE_SOURCE_FILE,
72 TITLE_FILE
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;
80 switch (type)
82 default:
83 case TITLE_SOURCE_FILE: fmt = _("%s source file"); break;
84 case TITLE_FILE: fmt = _("%s file"); break;
86 g_assert(!ft->title);
87 g_assert(ft->name);
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)
95 GeanyFiletype *ft;
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;
105 #define C
106 ft = filetypes[GEANY_FILETYPES_C];
107 ft->lang = 0;
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;
113 #define CPP
114 ft = filetypes[GEANY_FILETYPES_CPP];
115 ft->lang = 1;
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;
121 #define OBJECTIVEC
122 ft = filetypes[GEANY_FILETYPES_OBJECTIVEC];
123 ft->lang = 42;
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;
129 #define CS
130 ft = filetypes[GEANY_FILETYPES_CS];
131 ft->lang = 25;
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;
137 #define VALA
138 ft = filetypes[GEANY_FILETYPES_VALA];
139 ft->lang = 33;
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;
145 #define D
146 ft = filetypes[GEANY_FILETYPES_D];
147 ft->lang = 17;
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;
153 #define JAVA
154 ft = filetypes[GEANY_FILETYPES_JAVA];
155 ft->lang = 2;
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];
163 ft->lang = 4;
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;
169 #define ASM
170 ft = filetypes[GEANY_FILETYPES_ASM];
171 ft->lang = 9;
172 ft->name = g_strdup("ASM");
173 ft->title = g_strdup_printf(_("%s source file"), "Assembler");
174 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
176 #define BASIC
177 ft = filetypes[GEANY_FILETYPES_BASIC];
178 ft->lang = 26;
179 ft->name = g_strdup("FreeBasic");
180 filetype_make_title(ft, TITLE_SOURCE_FILE);
181 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
183 #define FORTRAN
184 ft = filetypes[GEANY_FILETYPES_FORTRAN];
185 ft->lang = 18;
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;
191 #define F77
192 ft = filetypes[GEANY_FILETYPES_F77];
193 ft->lang = 30;
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;
199 #define GLSL
200 ft = filetypes[GEANY_FILETYPES_GLSL];
201 ft->lang = 31;
202 ft->name = g_strdup("GLSL");
203 filetype_make_title(ft, TITLE_SOURCE_FILE);
204 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
206 #define CAML
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;
213 #define PERL
214 ft = filetypes[GEANY_FILETYPES_PERL];
215 ft->lang = 5;
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;
221 #define PHP
222 ft = filetypes[GEANY_FILETYPES_PHP];
223 ft->lang = 6;
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;
229 #define JAVASCRIPT
230 ft = filetypes[GEANY_FILETYPES_JS];
231 ft->lang = 23;
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;
237 #define PYTHON
238 ft = filetypes[GEANY_FILETYPES_PYTHON];
239 ft->lang = 7;
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;
245 #define RUBY
246 ft = filetypes[GEANY_FILETYPES_RUBY];
247 ft->lang = 14;
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;
253 #define TCL
254 ft = filetypes[GEANY_FILETYPES_TCL];
255 ft->lang = 15;
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;
261 #define LUA
262 ft = filetypes[GEANY_FILETYPES_LUA];
263 ft->lang = 22;
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;
269 #define FERITE
270 ft = filetypes[GEANY_FILETYPES_FERITE];
271 ft->lang = 19;
272 ft->name = g_strdup("Ferite");
273 filetype_make_title(ft, TITLE_SOURCE_FILE);
274 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
276 #define HASKELL
277 ft = filetypes[GEANY_FILETYPES_HASKELL];
278 ft->lang = 24;
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;
284 #define MARKDOWN
285 ft = filetypes[GEANY_FILETYPES_MARKDOWN];
286 ft->lang = 36;
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;
292 #define TXT2TAGS
293 ft = filetypes[GEANY_FILETYPES_TXT2TAGS];
294 ft->lang = 37;
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;
300 #define ABC
301 ft = filetypes[GEANY_FILETYPES_ABC];
302 ft->lang = 38;
303 ft->name = g_strdup("Abc");
304 filetype_make_title(ft, TITLE_FILE);
305 ft->group = GEANY_FILETYPE_GROUP_MISC;
307 #define SH
308 ft = filetypes[GEANY_FILETYPES_SH];
309 ft->lang = 16;
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;
315 #define MAKE
316 ft = filetypes[GEANY_FILETYPES_MAKE];
317 ft->lang = 3;
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;
323 #define XML
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;
330 #define DOCBOOK
331 ft = filetypes[GEANY_FILETYPES_DOCBOOK];
332 ft->lang = 12;
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;
338 #define HTML
339 ft = filetypes[GEANY_FILETYPES_HTML];
340 ft->lang = 29;
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;
346 #define CSS
347 ft = filetypes[GEANY_FILETYPES_CSS];
348 ft->lang = 13;
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 */
354 #define SQL
355 ft = filetypes[GEANY_FILETYPES_SQL];
356 ft->lang = 11;
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;
362 #define COBOL
363 ft = filetypes[GEANY_FILETYPES_COBOL];
364 ft->lang = 41;
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;
370 #define LATEX
371 ft = filetypes[GEANY_FILETYPES_LATEX];
372 ft->lang = 8;
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;
378 #define VHDL
379 ft = filetypes[GEANY_FILETYPES_VHDL];
380 ft->lang = 21;
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;
386 #define VERILOG
387 ft = filetypes[GEANY_FILETYPES_VERILOG];
388 ft->lang = 39;
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;
394 #define DIFF
395 ft = filetypes[GEANY_FILETYPES_DIFF];
396 ft->lang = 20;
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;
402 #define LISP
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;
408 #define ERLANG
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;
415 #define CONF
416 ft = filetypes[GEANY_FILETYPES_CONF];
417 ft->lang = 10;
418 ft->name = g_strdup("Conf");
419 ft->title = g_strdup(_("Config file"));
420 ft->group = GEANY_FILETYPE_GROUP_MISC;
422 #define PO
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;
429 #define HAXE
430 ft = filetypes[GEANY_FILETYPES_HAXE];
431 ft->lang = 27;
432 ft->name = g_strdup("Haxe");
433 filetype_make_title(ft, TITLE_SOURCE_FILE);
434 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
436 #define ACTIONSCRIPT
437 ft = filetypes[GEANY_FILETYPES_AS];
438 ft->lang = 34;
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;
444 #define R
445 ft = filetypes[GEANY_FILETYPES_R];
446 ft->lang = 40;
447 ft->name = g_strdup("R");
448 filetype_make_title(ft, TITLE_SOURCE_FILE);
449 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
451 #define REST
452 ft = filetypes[GEANY_FILETYPES_REST];
453 ft->lang = 28;
454 ft->name = g_strdup("reStructuredText");
455 filetype_make_title(ft, TITLE_SOURCE_FILE);
456 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
458 #define MATLAB
459 ft = filetypes[GEANY_FILETYPES_MATLAB];
460 ft->lang = 32;
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;
466 #define YAML
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;
473 #define CMAKE
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;
480 #define NSIS
481 ft = filetypes[GEANY_FILETYPES_NSIS];
482 ft->lang = 35;
483 ft->name = g_strdup("NSIS");
484 filetype_make_title(ft, TITLE_SOURCE_FILE);
485 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
487 #define ADA
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;
494 #define FORTH
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;
500 #define ASCIIDOC
501 ft = filetypes[GEANY_FILETYPES_ASCIIDOC];
502 ft->lang = 43;
503 ft->name = g_strdup("Asciidoc");
504 filetype_make_title(ft, TITLE_SOURCE_FILE);
505 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
507 #define ABAQUS
508 ft = filetypes[GEANY_FILETYPES_ABAQUS];
509 ft->lang = 44;
510 ft->name = g_strdup("Abaqus");
511 filetype_make_title(ft, TITLE_SOURCE_FILE);
512 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
514 #define BATCH
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;
520 #define POWERSHELL
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);
542 return ft;
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))
552 return -1;
553 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
554 return 1;
556 return by_name ?
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);
572 if (!list)
574 list = g_slist_copy(filetypes_by_title);
575 list = g_slist_sort_with_data(list, cmp_filetype, GINT_TO_POINTER(TRUE));
577 return list;
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);
595 if (!ft->mime_type)
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");
604 GeanyFiletype *ft;
606 g_return_if_fail(dot);
608 *dot = 0x0;
610 if (g_hash_table_lookup(filetypes_hash, fn))
611 return;
613 ft = filetype_new();
614 ft->name = g_strdup(fn);
615 filetype_make_title(ft, TITLE_FILE);
616 ft->priv->custom = TRUE;
617 filetype_add(ft);
618 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
622 static void init_custom_filetypes(const gchar *path)
624 GDir *dir;
625 const gchar *filename;
627 g_return_if_fail(path);
629 dir = g_dir_open(path, 0, NULL);
630 if (dir == NULL)
631 return;
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);
643 g_dir_close(dir);
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()
651 filetype_id ft_id;
652 gchar *f;
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);
675 g_free(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)
687 gchar *f;
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();
695 g_free(f);
696 f = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL);
697 if (utils_str_equal(doc->real_path, f))
699 guint i;
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);
705 foreach_document(i)
706 document_reload_config(documents[i]);
708 g_free(f);
712 static void setup_config_file_menus(void)
714 gchar *f;
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);
720 g_free(f);
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)
743 GSList *node;
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);
758 else
759 create_radio_menu_item(filetype_menu, ft);
764 void filetypes_init()
766 GSList *node;
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)
784 guint i;
786 for (i = 0; i < filetypes_array->len; i++)
788 GeanyFiletype *ft = filetypes[i];
790 if (predicate(ft, user_data))
791 return ft;
793 return NULL;
797 static gboolean match_basename(gconstpointer pft, gconstpointer user_data)
799 const GeanyFiletype *ft = pft;
800 const gchar *base_filename = user_data;
801 gint j;
802 gboolean ret = FALSE;
804 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
805 return FALSE;
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))
813 ret = TRUE;
814 g_pattern_spec_free(pattern);
815 break;
817 g_pattern_spec_free(pattern);
819 return ret;
823 static GeanyFiletype *check_builtin_filenames(const gchar *utf8_filename)
825 gchar *lfn = NULL;
826 gchar *path;
827 gboolean found = FALSE;
829 #ifdef G_OS_WIN32
830 /* use lower case basename */
831 lfn = g_utf8_strdown(utf8_filename, -1);
832 #else
833 lfn = g_strdup(utf8_filename);
834 #endif
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))
839 found = TRUE;
841 SETPTR(path, g_build_filename(app->datadir, "filetypes.", NULL));
842 if (g_str_has_prefix(lfn, path))
843 found = TRUE;
845 g_free(path);
846 g_free(lfn);
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;
856 GeanyFiletype *ft;
858 ft = check_builtin_filenames(utf8_filename);
859 if (ft)
860 return ft;
862 /* to match against the basename of the file (because of Makefile*) */
863 base_filename = g_path_get_basename(utf8_filename);
864 #ifdef G_OS_WIN32
865 /* use lower case basename */
866 SETPTR(base_filename, g_utf8_strdown(base_filename, -1));
867 #endif
869 ft = filetypes_find(match_basename, base_filename);
870 if (ft == NULL)
871 ft = filetypes[GEANY_FILETYPES_NONE];
873 g_free(base_filename);
874 return ft;
878 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
879 * terminated by -1.
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;
885 gint test;
886 gboolean result = FALSE;
887 va_list args;
889 ft = filetypes_detect_from_extension(utf8_filename);
890 if (ft == NULL || ft->id >= filetypes_array->len)
891 return FALSE;
893 va_start(args, first);
894 test = first;
895 while (1)
897 if (test == -1)
898 break;
900 if (ft->id == (guint) test)
902 result = TRUE;
903 break;
905 test = va_arg(args, gint);
907 va_end(args);
909 return result;
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 {
920 const gchar *name;
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;
944 guint i;
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];
956 g_free(tmp);
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];
986 return ft;
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,
993 gchar **lines)
995 GeanyFiletype *ft;
996 gint i;
997 GRegex *ft_regex;
998 GMatchInfo *match;
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]);
1004 if (ft != NULL)
1005 return ft;
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, &regex_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);
1020 g_free(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);
1032 if (ft != NULL)
1033 return ft;
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)
1045 GeanyFiletype *ft;
1046 gchar *lines[GEANY_FILETYPE_SEARCH_LINES + 1];
1047 gint i;
1049 if (doc == NULL)
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);
1056 lines[i] = NULL;
1057 ft = filetypes_detect_from_file_internal(doc->file_name, lines);
1058 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
1060 g_free(lines[i]);
1062 return ft;
1066 #ifdef HAVE_PLUGINS
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)
1078 gchar line[1024];
1079 gchar *lines[2];
1080 FILE *f;
1081 gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
1083 f = g_fopen(locale_name, "r");
1084 g_free(locale_name);
1085 if (f != NULL)
1087 if (fgets(line, sizeof(line), f) != NULL)
1089 fclose(f);
1090 lines[0] = line;
1091 lines[1] = NULL;
1092 return filetypes_detect_from_file_internal(utf8_filename, lines);
1094 fclose(f);
1096 return filetypes_detect_from_extension(utf8_filename);
1098 #endif
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);
1106 if (ft == NULL)
1107 ft = filetypes[GEANY_FILETYPES_NONE];
1109 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
1113 static void
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))
1118 return;
1120 document_set_filetype(doc, (GeanyFiletype*)user_data);
1124 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
1126 static GSList *group = NULL;
1127 GtkWidget *tmp;
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);
1144 g_free(ft->name);
1145 g_free(ft->title);
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);
1156 if (ft->icon)
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);
1165 g_free(ft->priv);
1166 g_free(ft);
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:
1192 case -1:
1193 break;
1195 default:
1196 g_warning("Invalid indent type %d in file type %s", ft->indent_type, ft->name);
1197 ft->indent_type = -1;
1198 break;
1203 static void load_settings(guint ft_id, GKeyFile *config, GKeyFile *configh)
1205 GeanyFiletype *ft = filetypes[ft_id];
1206 gchar *result;
1208 /* default extension */
1209 result = utils_get_setting(string, configh, config, "settings", "extension", NULL);
1210 if (result != NULL)
1212 SETPTR(filetypes[ft_id]->extension, result);
1215 /* read comment notes */
1216 result = utils_get_setting(string, configh, config, "settings", "comment_open", NULL);
1217 if (result != NULL)
1219 SETPTR(filetypes[ft_id]->comment_open, result);
1222 result = utils_get_setting(string, configh, config, "settings", "comment_close", NULL);
1223 if (result != NULL)
1225 SETPTR(filetypes[ft_id]->comment_close, result);
1228 result = utils_get_setting(string, configh, config, "settings", "comment_single", NULL);
1229 if (result != 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);
1245 if (result != NULL)
1247 SETPTR(filetypes[ft_id]->context_action_cmd, result);
1250 result = utils_get_setting(string, configh, config, "settings", "tag_parser", NULL);
1251 if (result != NULL)
1253 ft->lang = tm_source_file_get_named_lang(result);
1254 if (ft->lang < 0)
1255 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result, ft->name);
1256 g_free(result);
1259 result = utils_get_setting(string, configh, config, "settings", "lexer_filetype", NULL);
1260 if (result != 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);
1265 g_free(result);
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);
1285 gchar **ptr;
1287 foreach_strv(ptr, keys)
1289 gchar *key = *ptr;
1290 gchar *value = g_key_file_get_value(src, group, key, NULL);
1292 g_key_file_set_value(dest, group, key, value);
1293 g_free(value);
1295 g_strfreev(keys);
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);
1303 gchar *file_name;
1305 if (user)
1306 file_name = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, base_name, NULL);
1307 else
1308 file_name = g_build_filename(app->datadir, base_name, NULL);
1310 g_free(ext);
1311 g_free(base_name);
1313 return file_name;
1317 static void add_group_keys(GKeyFile *kf, const gchar *group, GeanyFiletype *ft)
1319 gchar *files[2];
1320 gboolean loaded = FALSE;
1321 guint i;
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);
1333 loaded = TRUE;
1335 g_key_file_free(src);
1338 if (!loaded)
1339 geany_debug("Could not read config file %s for [%s=%s]!", files[0], group, ft->name);
1341 g_free(files[0]);
1342 g_free(files[1]);
1346 static void copy_ft_groups(GKeyFile *kf)
1348 gchar **groups = g_key_file_get_groups(kf, NULL);
1349 gchar **ptr;
1351 foreach_strv(ptr, groups)
1353 gchar *group = *ptr;
1354 gchar *name = strstr(*ptr, "=");
1355 GeanyFiletype *ft;
1357 if (!name)
1358 continue;
1360 /* terminate group at '=' */
1361 *name = 0;
1362 name++;
1363 if (!name[0])
1364 continue;
1366 ft = filetypes_lookup_by_name(name);
1367 if (ft)
1368 add_group_keys(kf, group, ft);
1370 g_strfreev(groups);
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,
1376 GeanyFiletype *ft)
1378 GError *error = NULL;
1379 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1381 if (error != NULL)
1383 if (!done && !ft->priv->custom)
1384 geany_debug("Failed to open %s (%s)", file, error->message);
1386 g_error_free(error);
1387 error = NULL;
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;
1399 GeanyFiletype *ft;
1401 g_return_if_fail(ft_id < filetypes_array->len);
1403 ft = filetypes[ft_id];
1404 pft = ft->priv;
1406 /* when reloading, proceed only if the settings were already loaded */
1407 if (G_UNLIKELY(reload && ! pft->keyfile_loaded))
1408 return;
1410 /* when not reloading, load the settings only once */
1411 if (G_LIKELY(! reload && pft->keyfile_loaded))
1412 return;
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 */
1419 gchar *f;
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);
1426 g_free(f);
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)
1442 gchar *result;
1444 if (ft->priv->custom)
1445 return g_strconcat(ft->name, ".conf", NULL);
1447 /* Handle any special extensions different from lowercase filetype->name */
1448 switch (ft->id)
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;
1458 default:
1459 result = g_ascii_strdown(ft->name, -1);
1460 break;
1462 return result;
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);
1477 g_free(data);
1478 g_key_file_free(config_home);
1479 g_free(fname);
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;
1487 guint i, j;
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))
1495 continue;
1497 for (j = 0; filetypes[i]->pattern[j]; j++)
1499 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1502 return new_filter;
1506 GtkFileFilter *filetypes_create_file_filter(const GeanyFiletype *ft)
1508 GtkFileFilter *new_filter;
1509 gint i;
1510 const gchar *title;
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]);
1523 return new_filter;
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.
1541 * @since 0.15
1543 GeanyFiletype *filetypes_lookup_by_name(const gchar *name)
1545 GeanyFiletype *ft;
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);
1552 return ft;
1556 static void compile_regex(GeanyFiletype *ft, gchar *regstr)
1558 GError *error = NULL;
1559 GRegex *regex = g_regex_new(regstr, 0, 0, &error);
1561 if (!regex)
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)
1576 gchar *regstr;
1577 gchar **tmp;
1578 GeanyDocument *doc;
1579 GMatchInfo *minfo;
1581 if (ft == NULL)
1583 doc = document_get_current();
1584 if (doc != NULL)
1585 ft = doc->file_type;
1587 tmp = build_get_regex(build_info.grp, ft, NULL);
1588 if (tmp == NULL)
1589 return FALSE;
1590 regstr = *tmp;
1592 *filename = NULL;
1593 *line = -1;
1595 if (G_UNLIKELY(EMPTY(regstr)))
1596 return FALSE;
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)
1604 return FALSE;
1606 if (!g_regex_match(ft->priv->error_regex, message, 0, &minfo))
1608 g_match_info_free(minfo);
1609 return FALSE;
1611 if (g_match_info_get_match_count(minfo) >= 3)
1613 gchar *first, *second, *end;
1614 glong l;
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 */
1621 *line = l;
1622 g_free(first);
1623 *filename = second;
1625 else
1627 l = strtol(second, &end, 10);
1628 if (*end == '\0')
1630 *line = l;
1631 g_free(second);
1632 *filename = first;
1634 else
1636 g_free(first);
1637 g_free(second);
1641 g_match_info_free(minfo);
1642 return *filename != NULL;
1646 #ifdef G_OS_WIN32
1647 static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1649 guint i;
1650 for (i = 0; i < len; i++)
1652 SETPTR(patterns[i], g_ascii_strdown(patterns[i], -1));
1655 #endif
1658 static void read_extensions(GKeyFile *sysconfig, GKeyFile *userconfig)
1660 guint i;
1661 gsize len = 0;
1663 /* read the keys */
1664 for (i = 0; i < filetypes_array->len; i++)
1666 gboolean userset =
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 */
1673 if (!list)
1674 list = g_new0(gchar*, 1);
1675 filetypes[i]->pattern = list;
1677 #ifdef G_OS_WIN32
1678 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1679 #endif
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);
1687 gchar **name;
1689 foreach_strv(name, names)
1691 GeanyFiletype *ft = filetypes_lookup_by_name(*name);
1693 if (ft)
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);
1703 else
1704 geany_debug("Filetype '%s' not found for group '%s'!", *name, group_name);
1706 g_strfreev(names);
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)
1743 guint i;
1745 read_filetype_config();
1747 /* Redetect filetype of any documents with none set */
1748 foreach_document(i)
1750 GeanyDocument *doc = documents[i];
1751 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1752 continue;
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.
1763 * @since 0.16
1765 GeanyFiletype *filetypes_index(gint idx)
1767 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1771 void filetypes_reload(void)
1773 guint i;
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();
1784 if (!current_doc)
1785 return;
1787 /* update document styling */
1788 foreach_document(i)
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.
1799 * @param ft .
1800 * @return .
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);
1818 if (single_first)
1820 *co = ft->comment_single;
1821 if (!EMPTY(*co))
1822 *cc = NULL;
1823 else
1825 *co = ft->comment_open;
1826 *cc = ft->comment_close;
1829 else
1831 *co = ft->comment_open;
1832 if (!EMPTY(*co))
1833 *cc = ft->comment_close;
1834 else
1836 *co = ft->comment_single;
1837 *cc = NULL;
1841 return !EMPTY(*co);