Version bump.
[geany-mirror.git] / src / filetypes.c
blob94d504a0f7af2a126604cc152cf9e92a54fb18b7
1 /*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $Id$
24 /**
25 * @file filetypes.h
26 * Filetype detection, file extensions and filetype menu items.
29 /* Note: we use filetype_id for some function arguments, but GeanyFiletype is better; we should
30 * only use GeanyFiletype for API functions. */
32 #include <string.h>
33 #include <glib/gstdio.h>
35 #include "geany.h"
36 #include "filetypes.h"
37 #include "highlighting.h"
38 #include "support.h"
39 #include "templates.h"
40 #include "document.h"
41 #include "editor.h"
42 #include "msgwindow.h"
43 #include "utils.h"
44 #include "sciwrappers.h"
45 #include "ui_utils.h"
47 #include <stdlib.h>
49 #ifdef HAVE_REGCOMP
50 # ifdef HAVE_REGEX_H
51 # include <regex.h>
52 # else
53 # include "gnuregex.h"
54 # endif
55 #endif
58 /* Private GeanyFiletype fields */
59 typedef struct GeanyFiletypePrivate
61 GtkWidget *menu_item; /* holds a pointer to the menu item for this filetype */
62 gboolean keyfile_loaded;
63 #ifdef HAVE_REGCOMP
64 regex_t error_regex;
65 gboolean error_regex_compiled;
66 gchar *last_string; /* last one compiled */
67 #endif
68 gboolean custom;
70 GeanyFiletypePrivate;
73 GPtrArray *filetypes_array = NULL; /* Dynamic array of filetype pointers */
75 static GHashTable *filetypes_hash = NULL; /* Hash of filetype pointers based on name keys */
77 /** List of filetype pointers sorted by name, but with @c filetypes_index(GEANY_FILETYPES_NONE)
78 * first, as this is usually treated specially.
79 * The list does not change (after filetypes have been initialized), so you can use
80 * @code g_slist_nth_data(filetypes_by_title, n) @endcode and expect the same result at different times. */
81 GSList *filetypes_by_title = NULL;
84 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype);
87 enum TitleType
89 TITLE_SOURCE_FILE,
90 TITLE_FILE
93 /* Save adding many translation strings if the filetype name doesn't need translating */
94 static void filetype_make_title(GeanyFiletype *ft, enum TitleType type)
96 const gchar *fmt = NULL;
98 switch (type)
100 default:
101 case TITLE_SOURCE_FILE: fmt = _("%s source file"); break;
102 case TITLE_FILE: fmt = _("%s file"); break;
104 g_assert(!ft->title);
105 g_assert(ft->name);
106 ft->title = g_strdup_printf(fmt, ft->name);
110 /* Note: remember to update HACKING if this function is renamed. */
111 static void init_builtin_filetypes(void)
113 GeanyFiletype *ft;
115 #define NONE /* these macros are only to ease navigation */
116 ft = filetypes[GEANY_FILETYPES_NONE];
117 ft->lang = -2;
118 ft->name = g_strdup(_("None"));
119 ft->title = g_strdup(_("None"));
120 ft->extension = NULL;
121 ft->pattern = utils_strv_new("*", NULL);
122 ft->comment_open = NULL;
123 ft->comment_close = NULL;
124 ft->group = GEANY_FILETYPE_GROUP_NONE;
126 #define C
127 ft = filetypes[GEANY_FILETYPES_C];
128 ft->lang = 0;
129 ft->name = g_strdup("C");
130 filetype_make_title(ft, TITLE_SOURCE_FILE);
131 ft->extension = g_strdup("c");
132 ft->pattern = utils_strv_new("*.c", "*.h", NULL);
133 ft->comment_open = g_strdup("/*");
134 ft->comment_close = g_strdup("*/");
135 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
137 #define CPP
138 ft = filetypes[GEANY_FILETYPES_CPP];
139 ft->lang = 1;
140 ft->name = g_strdup("C++");
141 filetype_make_title(ft, TITLE_FILE);
142 ft->extension = g_strdup("cpp");
143 ft->pattern = utils_strv_new("*.cpp", "*.cxx", "*.c++", "*.cc",
144 "*.h", "*.hpp", "*.hxx", "*.h++", "*.hh", "*.C", NULL);
145 ft->comment_open = g_strdup("//");
146 ft->comment_close = NULL;
147 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
149 #define CS
150 ft = filetypes[GEANY_FILETYPES_CS];
151 ft->lang = 25;
152 ft->name = g_strdup("C#");
153 filetype_make_title(ft, TITLE_FILE);
154 ft->extension = g_strdup("cs");
155 ft->pattern = utils_strv_new("*.cs", NULL);
156 ft->comment_open = g_strdup("//");
157 ft->comment_close = NULL;
158 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
160 #define VALA
161 ft = filetypes[GEANY_FILETYPES_VALA];
162 ft->lang = 33;
163 ft->name = g_strdup("Vala");
164 filetype_make_title(ft, TITLE_SOURCE_FILE);
165 ft->extension = g_strdup("vala");
166 ft->pattern = utils_strv_new("*.vala", "*.vapi", NULL);
167 ft->comment_open = g_strdup("//");
168 ft->comment_close = NULL;
169 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
171 #define D
172 ft = filetypes[GEANY_FILETYPES_D];
173 ft->lang = 17;
174 ft->name = g_strdup("D");
175 filetype_make_title(ft, TITLE_SOURCE_FILE);
176 ft->extension = g_strdup("d");
177 ft->pattern = utils_strv_new("*.d", "*.di", NULL);
178 ft->comment_open = g_strdup("//");
179 ft->comment_close = NULL;
180 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
182 #define JAVA
183 ft = filetypes[GEANY_FILETYPES_JAVA];
184 ft->lang = 2;
185 ft->name = g_strdup("Java");
186 filetype_make_title(ft, TITLE_SOURCE_FILE);
187 ft->extension = g_strdup("java");
188 ft->pattern = utils_strv_new("*.java", "*.jsp", NULL);
189 ft->comment_open = g_strdup("/*");
190 ft->comment_close = g_strdup("*/");
191 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
193 #define PAS /* to avoid warnings when building under Windows, the symbol PASCAL is there defined */
194 ft = filetypes[GEANY_FILETYPES_PASCAL];
195 ft->lang = 4;
196 ft->name = g_strdup("Pascal");
197 filetype_make_title(ft, TITLE_SOURCE_FILE);
198 ft->extension = g_strdup("pas");
199 ft->pattern = utils_strv_new("*.pas", "*.pp", "*.inc", "*.dpr",
200 "*.dpk", NULL);
201 ft->comment_open = g_strdup("{");
202 ft->comment_close = g_strdup("}");
203 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
205 #define ASM
206 ft = filetypes[GEANY_FILETYPES_ASM];
207 ft->lang = 9;
208 ft->name = g_strdup("ASM");
209 ft->title = g_strdup_printf(_("%s source file"), "Assembler");
210 ft->extension = g_strdup("asm");
211 ft->pattern = utils_strv_new("*.asm", NULL);
212 ft->comment_open = g_strdup(";");
213 ft->comment_close = NULL;
214 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
216 #define BASIC
217 ft = filetypes[GEANY_FILETYPES_BASIC];
218 ft->lang = 26;
219 ft->name = g_strdup("FreeBasic");
220 filetype_make_title(ft, TITLE_SOURCE_FILE);
221 ft->extension = g_strdup("bas");
222 ft->pattern = utils_strv_new("*.bas", "*.bi", NULL);
223 ft->comment_open = g_strdup("'");
224 ft->comment_close = NULL;
225 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
227 #define FORTRAN
228 ft = filetypes[GEANY_FILETYPES_FORTRAN];
229 ft->lang = 18;
230 ft->name = g_strdup("Fortran");
231 ft->title = g_strdup_printf(_("%s source file"), "Fortran (F90)");
232 ft->extension = g_strdup("f90");
233 ft->pattern = utils_strv_new("*.f90", "*.f95", "*.f03", NULL);
234 ft->comment_open = g_strdup("!");
235 ft->comment_close = NULL;
236 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
238 #define F77
239 ft = filetypes[GEANY_FILETYPES_F77];
240 ft->lang = 30;
241 ft->name = g_strdup("F77");
242 ft->title = g_strdup_printf(_("%s source file"), "Fortran (F77)");
243 ft->extension = g_strdup("f");
244 ft->pattern = utils_strv_new("*.f", "*.for", "*.ftn", "*.f77", NULL);
245 ft->comment_open = g_strdup("c");
246 ft->comment_close = NULL;
247 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
249 #define GLSL
250 ft = filetypes[GEANY_FILETYPES_GLSL];
251 ft->lang = 31;
252 ft->name = g_strdup("GLSL");
253 filetype_make_title(ft, TITLE_SOURCE_FILE);
254 ft->extension = g_strdup("glsl");
255 ft->pattern = utils_strv_new("*.glsl", "*.frag", "*.vert", NULL);
256 ft->comment_open = g_strdup("/*");
257 ft->comment_close = g_strdup("*/");
258 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
260 #define CAML
261 ft = filetypes[GEANY_FILETYPES_CAML];
262 ft->lang = -2;
263 ft->name = g_strdup("CAML");
264 ft->title = g_strdup_printf(_("%s source file"), "(O)Caml");
265 ft->extension = g_strdup("ml");
266 ft->pattern = utils_strv_new("*.ml", "*.mli", NULL);
267 ft->comment_open = g_strdup("(*");
268 ft->comment_close = g_strdup("*)");
269 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
271 #define PERL
272 ft = filetypes[GEANY_FILETYPES_PERL];
273 ft->lang = 5;
274 ft->name = g_strdup("Perl");
275 filetype_make_title(ft, TITLE_SOURCE_FILE);
276 ft->extension = g_strdup("pl");
277 ft->pattern = utils_strv_new("*.pl", "*.perl", "*.pm", "*.agi",
278 "*.pod", NULL);
279 ft->comment_open = g_strdup("#");
280 ft->comment_close = NULL;
281 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
283 #define PHP
284 ft = filetypes[GEANY_FILETYPES_PHP];
285 ft->lang = 6;
286 ft->name = g_strdup("PHP");
287 filetype_make_title(ft, TITLE_SOURCE_FILE);
288 ft->extension = g_strdup("php");
289 ft->pattern = utils_strv_new("*.php", "*.php3", "*.php4", "*.php5",
290 "*.phtml", NULL);
291 ft->comment_open = g_strdup("//");
292 ft->comment_close = NULL;
293 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
295 #define JAVASCRIPT
296 ft = filetypes[GEANY_FILETYPES_JS];
297 ft->lang = 23;
298 ft->name = g_strdup("Javascript");
299 filetype_make_title(ft, TITLE_SOURCE_FILE);
300 ft->extension = g_strdup("js");
301 ft->pattern = utils_strv_new("*.js", NULL);
302 ft->comment_open = g_strdup("//");
303 ft->comment_close = NULL;
304 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
306 #define PYTHON
307 ft = filetypes[GEANY_FILETYPES_PYTHON];
308 ft->lang = 7;
309 ft->name = g_strdup("Python");
310 filetype_make_title(ft, TITLE_SOURCE_FILE);
311 ft->extension = g_strdup("py");
312 ft->pattern = utils_strv_new("*.py", "*.pyw", NULL);
313 ft->comment_open = g_strdup("#");
314 ft->comment_close = NULL;
315 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
317 #define RUBY
318 ft = filetypes[GEANY_FILETYPES_RUBY];
319 ft->lang = 14;
320 ft->name = g_strdup("Ruby");
321 filetype_make_title(ft, TITLE_SOURCE_FILE);
322 ft->extension = g_strdup("rb");
323 ft->pattern = utils_strv_new("*.rb", "*.rhtml", "*.ruby", NULL);
324 ft->comment_open = g_strdup("#");
325 ft->comment_close = NULL;
326 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
328 #define TCL
329 ft = filetypes[GEANY_FILETYPES_TCL];
330 ft->lang = 15;
331 ft->name = g_strdup("Tcl");
332 filetype_make_title(ft, TITLE_SOURCE_FILE);
333 ft->extension = g_strdup("tcl");
334 ft->pattern = utils_strv_new("*.tcl", "*.tk", "*.wish", NULL);
335 ft->comment_open = g_strdup("#");
336 ft->comment_close = NULL;
337 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
339 #define LUA
340 ft = filetypes[GEANY_FILETYPES_LUA];
341 ft->lang = 22;
342 ft->name = g_strdup("Lua");
343 filetype_make_title(ft, TITLE_SOURCE_FILE);
344 ft->extension = g_strdup("lua");
345 ft->pattern = utils_strv_new("*.lua", NULL);
346 ft->comment_open = g_strdup("--");
347 ft->comment_close = NULL;
348 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
350 #define FERITE
351 ft = filetypes[GEANY_FILETYPES_FERITE];
352 ft->lang = 19;
353 ft->name = g_strdup("Ferite");
354 filetype_make_title(ft, TITLE_SOURCE_FILE);
355 ft->extension = g_strdup("fe");
356 ft->pattern = utils_strv_new("*.fe", NULL);
357 ft->comment_open = g_strdup("/*");
358 ft->comment_close = g_strdup("*/");
359 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
361 #define HASKELL
362 ft = filetypes[GEANY_FILETYPES_HASKELL];
363 ft->lang = 24;
364 ft->name = g_strdup("Haskell");
365 filetype_make_title(ft, TITLE_SOURCE_FILE);
366 ft->extension = g_strdup("hs");
367 ft->pattern = utils_strv_new("*.hs", "*.lhs", NULL);
368 ft->comment_open = g_strdup("--");
369 ft->comment_close = NULL;
370 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
372 #define MARKDOWN
373 ft = filetypes[GEANY_FILETYPES_MARKDOWN];
374 ft->lang = 36;
375 ft->name = g_strdup("Markdown");
376 filetype_make_title(ft, TITLE_SOURCE_FILE);
377 ft->extension = g_strdup("md");
378 ft->pattern = utils_strv_new("*.mdml", "*.mdwn", "*.markdown", "*.md", NULL);
379 ft->comment_open = NULL;
380 ft->comment_close = NULL;
381 ft->group = GEANY_FILETYPE_GROUP_MISC;
383 #define TXT2TAGS
384 ft = filetypes[GEANY_FILETYPES_TXT2TAGS];
385 ft->lang = 37;
386 ft->name = g_strdup("Txt2tags");
387 filetype_make_title(ft, TITLE_SOURCE_FILE);
388 ft->extension = g_strdup("t2t");
389 ft->pattern = utils_strv_new("*.t2t", "*.txt2tags", NULL);
390 ft->comment_open = NULL;
391 ft->comment_close = NULL;
392 ft->group = GEANY_FILETYPE_GROUP_MISC;
394 #define ABC
395 ft = filetypes[GEANY_FILETYPES_ABC];
396 ft->lang = 38;
397 ft->name = g_strdup("Abc");
398 filetype_make_title(ft, TITLE_SOURCE_FILE);
399 ft->extension = g_strdup("abc");
400 ft->pattern = utils_strv_new("*.abc", "*.abp", NULL);
401 ft->comment_open = NULL;
402 ft->comment_close = NULL;
403 ft->group = GEANY_FILETYPE_GROUP_MISC;
405 #define SH
406 ft = filetypes[GEANY_FILETYPES_SH];
407 ft->lang = 16;
408 ft->name = g_strdup("Sh");
409 ft->title = g_strdup(_("Shell script file"));
410 ft->extension = g_strdup("sh");
411 ft->pattern = utils_strv_new("*.sh", "configure", "configure.in",
412 "configure.in.in", "configure.ac", "*.ksh", "*.zsh", "*.ash", "*.bash", NULL);
413 ft->comment_open = g_strdup("#");
414 ft->comment_close = NULL;
415 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
417 #define MAKE
418 ft = filetypes[GEANY_FILETYPES_MAKE];
419 ft->lang = 3;
420 ft->name = g_strdup("Make");
421 ft->title = g_strdup(_("Makefile"));
422 ft->extension = g_strdup("mak");
423 ft->pattern = utils_strv_new(
424 "*.mak", "*.mk", "GNUmakefile", "makefile", "Makefile", "makefile.*", "Makefile.*", NULL);
425 ft->comment_open = g_strdup("#");
426 ft->comment_close = NULL;
427 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
429 #define XML
430 ft = filetypes[GEANY_FILETYPES_XML];
431 ft->lang = -2;
432 ft->name = g_strdup("XML");
433 ft->title = g_strdup(_("XML document"));
434 ft->extension = g_strdup("xml");
435 ft->pattern = utils_strv_new(
436 "*.xml", "*.sgml", "*.xsl", "*.xslt", "*.xsd", "*.xhtml", NULL);
437 ft->comment_open = g_strdup("<!--");
438 ft->comment_close = g_strdup("-->");
439 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
441 #define DOCBOOK
442 ft = filetypes[GEANY_FILETYPES_DOCBOOK];
443 ft->lang = 12;
444 ft->name = g_strdup("Docbook");
445 filetype_make_title(ft, TITLE_SOURCE_FILE);
446 ft->extension = g_strdup("docbook");
447 ft->pattern = utils_strv_new("*.docbook", NULL);
448 ft->comment_open = g_strdup("<!--");
449 ft->comment_close = g_strdup("-->");
450 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
452 #define HTML
453 ft = filetypes[GEANY_FILETYPES_HTML];
454 ft->lang = 29;
455 ft->name = g_strdup("HTML");
456 filetype_make_title(ft, TITLE_SOURCE_FILE);
457 ft->extension = g_strdup("html");
458 ft->pattern = utils_strv_new(
459 "*.htm", "*.html", "*.shtml", "*.hta", "*.htd", "*.htt", "*.cfm", NULL);
460 ft->comment_open = g_strdup("<!--");
461 ft->comment_close = g_strdup("-->");
462 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
464 #define CSS
465 ft = filetypes[GEANY_FILETYPES_CSS];
466 ft->lang = 13;
467 ft->name = g_strdup("CSS");
468 ft->title = g_strdup(_("Cascading StyleSheet"));
469 ft->extension = g_strdup("css");
470 ft->pattern = utils_strv_new("*.css", NULL);
471 ft->comment_open = g_strdup("/*");
472 ft->comment_close = g_strdup("*/");
473 ft->group = GEANY_FILETYPE_GROUP_MARKUP; /* not really markup but fit quite well to HTML */
475 #define SQL
476 ft = filetypes[GEANY_FILETYPES_SQL];
477 ft->lang = 11;
478 ft->name = g_strdup("SQL");
479 ft->title = g_strdup(_("SQL Dump file"));
480 ft->extension = g_strdup("sql");
481 ft->pattern = utils_strv_new("*.sql", NULL);
482 ft->comment_open = g_strdup("/*");
483 ft->comment_close = g_strdup("*/");
484 ft->group = GEANY_FILETYPE_GROUP_MISC;
486 #define LATEX
487 ft = filetypes[GEANY_FILETYPES_LATEX];
488 ft->lang = 8;
489 ft->name = g_strdup("LaTeX");
490 filetype_make_title(ft, TITLE_SOURCE_FILE);
491 ft->extension = g_strdup("tex");
492 ft->pattern = utils_strv_new("*.tex", "*.sty", "*.idx", "*.ltx", NULL);
493 ft->comment_open = g_strdup("%");
494 ft->comment_close = NULL;
495 ft->group = GEANY_FILETYPE_GROUP_MISC;
497 #define VHDL
498 ft = filetypes[GEANY_FILETYPES_VHDL];
499 ft->lang = 21;
500 ft->name = g_strdup("VHDL");
501 filetype_make_title(ft, TITLE_SOURCE_FILE);
502 ft->extension = g_strdup("vhd");
503 ft->pattern = utils_strv_new("*.vhd", "*.vhdl", NULL);
504 ft->comment_open = g_strdup("--");
505 ft->comment_close = NULL;
506 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
508 #define VERILOG
509 ft = filetypes[GEANY_FILETYPES_VERILOG];
510 ft->lang = 39;
511 ft->name = g_strdup("Verilog");
512 filetype_make_title(ft, TITLE_SOURCE_FILE);
513 ft->extension = g_strdup("v");
514 ft->pattern = utils_strv_new("*.v", "*.verilog", NULL);
515 ft->comment_open = g_strdup("/*");
516 ft->comment_close = g_strdup("*/");
517 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
519 #define DIFF
520 ft = filetypes[GEANY_FILETYPES_DIFF];
521 ft->lang = 20;
522 ft->name = g_strdup("Diff");
523 filetype_make_title(ft, TITLE_FILE);
524 ft->extension = g_strdup("diff");
525 ft->pattern = utils_strv_new("*.diff", "*.patch", "*.rej", NULL);
526 ft->comment_open = g_strdup("#");
527 ft->comment_close = NULL;
528 ft->group = GEANY_FILETYPE_GROUP_MISC;
530 #define CONF
531 ft = filetypes[GEANY_FILETYPES_CONF];
532 ft->lang = 10;
533 ft->name = g_strdup("Conf");
534 ft->title = g_strdup(_("Config file"));
535 ft->extension = g_strdup("conf");
536 ft->pattern = utils_strv_new("*.conf", "*.ini", "config", "*rc",
537 "*.cfg", "*.desktop", NULL);
538 ft->comment_open = g_strdup("#");
539 ft->comment_close = NULL;
540 ft->group = GEANY_FILETYPE_GROUP_MISC;
542 #define PO
543 ft = filetypes[GEANY_FILETYPES_PO];
544 ft->lang = -2;
545 ft->name = g_strdup("Po");
546 ft->title = g_strdup(_("Gettext translation file"));
547 ft->extension = g_strdup("po");
548 ft->pattern = utils_strv_new("*.po", "*.pot", NULL);
549 ft->comment_open = g_strdup("#");
550 ft->comment_close = NULL;
551 ft->group = GEANY_FILETYPE_GROUP_MISC;
553 #define HAXE
554 ft = filetypes[GEANY_FILETYPES_HAXE];
555 ft->lang = 27;
556 ft->name = g_strdup("Haxe");
557 filetype_make_title(ft, TITLE_SOURCE_FILE);
558 ft->extension = g_strdup("hx");
559 ft->pattern = utils_strv_new("*.hx", NULL);
560 ft->comment_open = g_strdup("//");
561 ft->comment_close = NULL;
562 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
564 #define ACTIONSCRIPT
565 ft = filetypes[GEANY_FILETYPES_AS];
566 ft->lang = 34;
567 ft->name = g_strdup("ActionScript");
568 filetype_make_title(ft, TITLE_SOURCE_FILE);
569 ft->extension = g_strdup("as");
570 ft->pattern = utils_strv_new("*.as", NULL);
571 ft->comment_open = g_strdup("//");
572 ft->comment_close = NULL;
573 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
575 #define R
576 ft = filetypes[GEANY_FILETYPES_R];
577 ft->lang = 40;
578 ft->name = g_strdup("R");
579 ft->title = g_strdup_printf(_("%s script file"), "R");
580 ft->extension = g_strdup("R");
581 ft->pattern = utils_strv_new("*.R", "*.r", NULL);
582 ft->comment_open = g_strdup("#");
583 ft->comment_close = NULL;
584 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
586 #define REST
587 ft = filetypes[GEANY_FILETYPES_REST];
588 ft->lang = 28;
589 ft->name = g_strdup("reStructuredText");
590 filetype_make_title(ft, TITLE_FILE);
591 ft->extension = g_strdup("rst");
592 ft->pattern = utils_strv_new(
593 "*.rest", "*.reST", "*.rst", NULL);
594 ft->comment_open = NULL;
595 ft->comment_close = NULL;
596 ft->group = GEANY_FILETYPE_GROUP_MISC;
598 #define MATLAB
599 ft = filetypes[GEANY_FILETYPES_MATLAB];
600 ft->lang = 32;
601 ft->name = g_strdup("Matlab");
602 filetype_make_title(ft, TITLE_SOURCE_FILE);
603 ft->extension = g_strdup("m");
604 ft->pattern = utils_strv_new("*.m", NULL);
605 ft->comment_open = g_strdup("%");
606 ft->comment_close = NULL;
607 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
609 #define YAML
610 ft = filetypes[GEANY_FILETYPES_YAML];
611 ft->lang = -2;
612 ft->name = g_strdup("YAML");
613 filetype_make_title(ft, TITLE_SOURCE_FILE);
614 ft->extension = g_strdup("yaml");
615 ft->pattern = utils_strv_new("*.yaml", "*.yml", NULL);
616 ft->comment_open = g_strdup("#");
617 ft->comment_close = NULL;
618 ft->group = GEANY_FILETYPE_GROUP_MISC;
620 #define CMAKE
621 ft = filetypes[GEANY_FILETYPES_CMAKE];
622 ft->lang = -2;
623 ft->name = g_strdup("CMake");
624 filetype_make_title(ft, TITLE_SOURCE_FILE);
625 ft->extension = g_strdup("cmake");
626 ft->pattern = utils_strv_new("CMakeLists.txt", "*.cmake", "*.ctest", NULL);
627 ft->comment_open = g_strdup("#");
628 ft->comment_close = NULL;
629 ft->group = GEANY_FILETYPE_GROUP_MISC;
631 #define NSIS
632 ft = filetypes[GEANY_FILETYPES_NSIS];
633 ft->lang = 35;
634 ft->name = g_strdup("NSIS");
635 filetype_make_title(ft, TITLE_SOURCE_FILE);
636 ft->extension = g_strdup("nsis");
637 ft->pattern = utils_strv_new("*.nsi", "*.nsh", NULL);
638 ft->comment_open = g_strdup(";");
639 ft->comment_close = NULL;
640 ft->group = GEANY_FILETYPE_GROUP_MISC;
642 #define ADA
643 ft = filetypes[GEANY_FILETYPES_ADA];
644 ft->lang = -2;
645 ft->name = g_strdup("Ada");
646 filetype_make_title(ft, TITLE_SOURCE_FILE);
647 ft->extension = g_strdup("adb");
648 ft->pattern = utils_strv_new("*.adb", "*.ads", NULL);
649 ft->comment_open = g_strdup("--");
650 ft->comment_close = NULL;
651 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
655 /* initialize fields. */
656 static GeanyFiletype *filetype_new(void)
658 GeanyFiletype *ft = g_new0(GeanyFiletype, 1);
660 ft->lang = -2; /* assume no tagmanager parser */
661 ft->project_list_entry = -1; /* no entry */
663 ft->priv = g_new0(GeanyFiletypePrivate, 1);
664 return ft;
668 static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2)
670 const GeanyFiletype *ft1 = pft1, *ft2 = pft2;
672 if (G_UNLIKELY(ft1->id == GEANY_FILETYPES_NONE))
673 return -1;
674 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
675 return 1;
677 return utils_str_casecmp(ft1->title, ft2->title);
681 /* Add a filetype pointer to the lists of available filetypes,
682 * and set the filetype::id field. */
683 static void filetype_add(GeanyFiletype *ft)
685 g_return_if_fail(ft);
686 g_return_if_fail(ft->name);
688 ft->id = filetypes_array->len; /* len will be the index for filetype_array */
689 g_ptr_array_add(filetypes_array, ft);
690 g_hash_table_insert(filetypes_hash, ft->name, ft);
692 filetypes_by_title = g_slist_insert_sorted(filetypes_by_title, ft, cmp_filetype);
696 static void add_custom_filetype(const gchar *filename)
698 gchar *fn = utils_strdupa(strstr(filename, ".") + 1);
699 gchar *dot = g_strrstr(fn, ".conf");
700 GeanyFiletype *ft;
702 g_return_if_fail(dot);
704 *dot = 0x0;
706 if (g_hash_table_lookup(filetypes_hash, fn))
707 return;
709 ft = filetype_new();
710 ft->name = g_strdup(fn);
711 filetype_make_title(ft, TITLE_FILE);
712 ft->pattern = g_new0(gchar*, 1);
713 ft->group = GEANY_FILETYPE_GROUP_CUSTOM;
714 ft->priv->custom = TRUE;
715 filetype_add(ft);
716 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
720 static void init_custom_filetypes(const gchar *path)
722 GDir *dir;
723 const gchar *filename;
725 g_return_if_fail(path);
727 dir = g_dir_open(path, 0, NULL);
728 if (dir == NULL)
729 return;
731 foreach_dir(filename, dir)
733 const gchar prefix[] = "filetypes.";
735 if (g_str_has_prefix(filename, prefix) &&
736 g_str_has_suffix(filename + strlen(prefix), ".conf"))
738 add_custom_filetype(filename);
741 g_dir_close(dir);
745 /* Create the filetypes array and fill it with the known filetypes. */
746 void filetypes_init_types()
748 filetype_id ft_id;
750 g_return_if_fail(filetypes_array == NULL);
751 g_return_if_fail(filetypes_hash == NULL);
753 filetypes_array = g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES);
754 filetypes_hash = g_hash_table_new(g_str_hash, g_str_equal);
756 /* Create built-in filetypes */
757 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
759 filetypes[ft_id] = filetype_new();
761 init_builtin_filetypes();
763 /* Add built-in filetypes to the hash now the name fields are set */
764 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
766 filetype_add(filetypes[ft_id]);
768 init_custom_filetypes(app->datadir);
769 init_custom_filetypes(utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, NULL));
773 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
775 g_return_if_fail(NZV(doc->real_path));
777 if (utils_str_equal(doc->real_path,
778 utils_build_path(app->configdir, "filetype_extensions.conf", NULL)))
779 filetypes_read_extensions();
780 else if (utils_str_equal(doc->real_path,
781 utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL)))
783 guint i;
785 /* Note: we don't reload other filetypes, even though the named styles may have changed.
786 * The user can do this manually with 'Tools->Reload Configuration' */
787 filetypes_load_config(GEANY_FILETYPES_NONE, TRUE);
789 foreach_document(i)
790 document_reload_config(documents[i]);
795 static void setup_config_file_menus(void)
797 ui_add_config_file_menu_item(
798 utils_build_path(app->configdir, "filetype_extensions.conf", NULL), NULL, NULL);
799 ui_add_config_file_menu_item(
800 utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL), NULL, NULL);
802 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
806 static GtkWidget *group_menus[GEANY_FILETYPE_GROUP_COUNT] = {NULL};
808 static void create_sub_menu(GtkWidget *parent, gsize group_id, const gchar *title)
810 GtkWidget *menu, *item;
812 menu = gtk_menu_new();
813 item = gtk_menu_item_new_with_mnemonic((title));
814 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
815 gtk_container_add(GTK_CONTAINER(parent), item);
816 gtk_widget_show(item);
817 group_menus[group_id] = menu;
821 static void create_set_filetype_menu(void)
823 GSList *node;
824 GtkWidget *filetype_menu = ui_lookup_widget(main_widgets.window, "set_filetype1_menu");
826 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_COMPILED, _("_Programming Languages"));
827 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_SCRIPT, _("_Scripting Languages"));
828 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MARKUP, _("_Markup Languages"));
829 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MISC, _("M_iscellaneous Languages"));
830 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_CUSTOM, _("_Custom Filetypes"));
832 /* Append all filetypes to the filetype menu */
833 foreach_slist(node, filetypes_by_title)
835 GeanyFiletype *ft = node->data;
837 if (ft->group != GEANY_FILETYPE_GROUP_NONE)
838 create_radio_menu_item(group_menus[ft->group], ft);
839 else
840 create_radio_menu_item(filetype_menu, ft);
845 void filetypes_init()
847 filetypes_init_types();
849 create_set_filetype_menu();
850 setup_config_file_menus();
854 /* Find a filetype that predicate returns TRUE for, otherwise return NULL. */
855 GeanyFiletype *filetypes_find(GCompareFunc predicate, gpointer user_data)
857 guint i;
859 for (i = 0; i < filetypes_array->len; i++)
861 GeanyFiletype *ft = filetypes[i];
863 if (predicate(ft, user_data))
864 return ft;
866 return NULL;
870 static gboolean match_basename(gconstpointer pft, gconstpointer user_data)
872 const GeanyFiletype *ft = pft;
873 const gchar *base_filename = user_data;
874 gint j;
875 gboolean ret = FALSE;
877 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
878 return FALSE;
880 for (j = 0; ft->pattern[j] != NULL; j++)
882 GPatternSpec *pattern = g_pattern_spec_new(ft->pattern[j]);
884 if (g_pattern_match_string(pattern, base_filename))
886 ret = TRUE;
887 g_pattern_spec_free(pattern);
888 break;
890 g_pattern_spec_free(pattern);
892 return ret;
896 /* Detect filetype only based on the filename extension.
897 * utf8_filename can include the full path. */
898 GeanyFiletype *filetypes_detect_from_extension(const gchar *utf8_filename)
900 gchar *base_filename;
901 GeanyFiletype *ft;
903 /* to match against the basename of the file (because of Makefile*) */
904 base_filename = g_path_get_basename(utf8_filename);
905 #ifdef G_OS_WIN32
906 /* use lower case basename */
907 setptr(base_filename, g_utf8_strdown(base_filename, -1));
908 #endif
910 ft = filetypes_find(match_basename, base_filename);
911 if (ft == NULL)
912 ft = filetypes[GEANY_FILETYPES_NONE];
914 g_free(base_filename);
915 return ft;
919 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
920 * terminated by -1.
921 * The detected filetype of the file is checked against every id in the passed list and if
922 * there is a match, TRUE is returned. */
923 static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first, ...)
925 GeanyFiletype *ft = NULL;
926 gint test;
927 gboolean result = FALSE;
928 va_list args;
930 ft = filetypes_detect_from_extension(utf8_filename);
931 if (ft == NULL || ft->id >= filetypes_array->len)
932 return FALSE;
934 va_start(args, first);
935 test = first;
936 while (1)
938 if (test == -1)
939 break;
941 if (ft->id == (guint) test)
943 result = TRUE;
944 break;
946 test = va_arg(args, gint);
948 va_end(args);
950 return result;
954 static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line)
956 GeanyFiletype *ft = NULL;
958 if (strlen(line) > 2 && line[0] == '#' && line[1] == '!')
960 gchar *tmp = g_path_get_basename(line + 2);
961 gchar *basename_interpreter = tmp;
963 if (strncmp(tmp, "env ", 4) == 0 && strlen(tmp) > 4)
964 { /* skip "env" and read the following interpreter */
965 basename_interpreter += 4;
968 if (strncmp(basename_interpreter, "sh", 2) == 0)
969 ft = filetypes[GEANY_FILETYPES_SH];
970 else if (strncmp(basename_interpreter, "bash", 4) == 0)
971 ft = filetypes[GEANY_FILETYPES_SH];
972 else if (strncmp(basename_interpreter, "perl", 4) == 0)
973 ft = filetypes[GEANY_FILETYPES_PERL];
974 else if (strncmp(basename_interpreter, "python", 6) == 0)
975 ft = filetypes[GEANY_FILETYPES_PYTHON];
976 else if (strncmp(basename_interpreter, "php", 3) == 0)
977 ft = filetypes[GEANY_FILETYPES_PHP];
978 else if (strncmp(basename_interpreter, "ruby", 4) == 0)
979 ft = filetypes[GEANY_FILETYPES_RUBY];
980 else if (strncmp(basename_interpreter, "tcl", 3) == 0)
981 ft = filetypes[GEANY_FILETYPES_TCL];
982 else if (strncmp(basename_interpreter, "make", 4) == 0)
983 ft = filetypes[GEANY_FILETYPES_MAKE];
984 else if (strncmp(basename_interpreter, "zsh", 3) == 0)
985 ft = filetypes[GEANY_FILETYPES_SH];
986 else if (strncmp(basename_interpreter, "ksh", 3) == 0)
987 ft = filetypes[GEANY_FILETYPES_SH];
988 else if (strncmp(basename_interpreter, "csh", 3) == 0)
989 ft = filetypes[GEANY_FILETYPES_SH];
990 else if (strncmp(basename_interpreter, "ash", 3) == 0)
991 ft = filetypes[GEANY_FILETYPES_SH];
992 else if (strncmp(basename_interpreter, "dmd", 3) == 0)
993 ft = filetypes[GEANY_FILETYPES_D];
994 else if (strncmp(basename_interpreter, "wish", 4) == 0)
995 ft = filetypes[GEANY_FILETYPES_TCL];
997 g_free(tmp);
999 /* detect HTML files */
1000 if (strncmp(line, "<!DOCTYPE html", 14) == 0 || strncmp(line, "<html", 5) == 0)
1002 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
1003 * extension and use the detected filetype, else assume HTML */
1004 if (! shebang_find_and_match_filetype(utf8_filename,
1005 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
1007 ft = filetypes[GEANY_FILETYPES_HTML];
1010 /* detect XML files */
1011 else if (utf8_filename && strncmp(line, "<?xml", 5) == 0)
1013 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
1014 * extension and use the detected filetype, else assume XML */
1015 if (! shebang_find_and_match_filetype(utf8_filename,
1016 GEANY_FILETYPES_HTML, GEANY_FILETYPES_DOCBOOK,
1017 /* Perl, Python and PHP only to be safe */
1018 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
1020 ft = filetypes[GEANY_FILETYPES_XML];
1023 else if (strncmp(line, "<?php", 5) == 0)
1025 ft = filetypes[GEANY_FILETYPES_PHP];
1028 return ft;
1032 /* Detect the filetype checking for a shebang, then filename extension. */
1033 static GeanyFiletype *filetypes_detect_from_file_internal(const gchar *utf8_filename,
1034 const gchar *line)
1036 GeanyFiletype *ft;
1038 /* try to find a shebang and if found use it prior to the filename extension
1039 * also checks for <?xml */
1040 ft = find_shebang(utf8_filename, line);
1041 if (ft != NULL)
1042 return ft;
1044 if (utf8_filename == NULL)
1045 return filetypes[GEANY_FILETYPES_NONE];
1047 return filetypes_detect_from_extension(utf8_filename);
1051 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
1052 GeanyFiletype *filetypes_detect_from_document(GeanyDocument *doc)
1054 GeanyFiletype *ft;
1055 gchar *line;
1057 if (doc == NULL)
1058 return filetypes[GEANY_FILETYPES_NONE];
1060 line = sci_get_line(doc->editor->sci, 0);
1061 ft = filetypes_detect_from_file_internal(doc->file_name, line);
1062 g_free(line);
1063 return ft;
1067 #ifdef HAVE_PLUGINS
1068 /* Currently only used by external plugins (e.g. geanyprj). */
1070 * Detects filetype based on a shebang line in the file or the filename extension.
1072 * @param utf8_filename The filename in UTF-8 encoding.
1074 * @return The detected filetype for @a utf8_filename or @c filetypes[GEANY_FILETYPES_NONE]
1075 * if it could not be detected.
1077 GeanyFiletype *filetypes_detect_from_file(const gchar *utf8_filename)
1079 gchar line[1024];
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 return filetypes_detect_from_file_internal(utf8_filename, line);
1092 fclose(f);
1094 return filetypes_detect_from_extension(utf8_filename);
1096 #endif
1099 void filetypes_select_radio_item(const GeanyFiletype *ft)
1101 /* ignore_callback has to be set by the caller */
1102 g_return_if_fail(ignore_callback);
1104 if (ft == NULL)
1105 ft = filetypes[GEANY_FILETYPES_NONE];
1107 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
1111 static void
1112 on_filetype_change (GtkCheckMenuItem *menuitem,
1113 gpointer user_data)
1115 GeanyDocument *doc = document_get_current();
1116 if (ignore_callback || doc == NULL || ! gtk_check_menu_item_get_active(menuitem))
1117 return;
1119 document_set_filetype(doc, (GeanyFiletype*)user_data);
1123 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
1125 static GSList *group = NULL;
1126 GtkWidget *tmp;
1128 tmp = gtk_radio_menu_item_new_with_label(group, ftype->title);
1129 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp));
1130 ftype->priv->menu_item = tmp;
1131 gtk_widget_show(tmp);
1132 gtk_container_add(GTK_CONTAINER(menu), tmp);
1133 g_signal_connect(tmp, "activate", G_CALLBACK(on_filetype_change), (gpointer) ftype);
1137 #if 0
1138 /* Remove a filetype pointer from the list of available filetypes. */
1139 static void filetype_remove(GeanyFiletype *ft)
1141 g_return_if_fail(ft);
1143 g_ptr_array_remove(filetypes_array, ft);
1145 if (!g_hash_table_remove(filetypes_hash, ft))
1146 g_warning("Could not remove filetype %p!", ft);
1148 #endif
1151 static void set_error_regex(GeanyFiletype *ft, gchar *string)
1153 setptr(ft->error_regex_string, string);
1155 #ifdef HAVE_REGCOMP
1156 if (ft->priv->error_regex_compiled)
1157 regfree(&ft->priv->error_regex);
1159 ft->priv->error_regex_compiled = FALSE;
1160 /* regex will be compiled when needed */
1161 #endif
1165 static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
1167 GeanyFiletype *ft = data;
1169 g_return_if_fail(ft != NULL);
1171 g_free(ft->name);
1172 g_free(ft->title);
1173 g_free(ft->extension);
1174 g_free(ft->comment_open);
1175 g_free(ft->comment_close);
1176 g_free(ft->context_action_cmd);
1177 g_free(ft->filecmds);
1178 g_free(ft->ftdefcmds);
1179 g_free(ft->execcmds);
1180 set_error_regex(ft, NULL);
1182 g_strfreev(ft->pattern);
1183 g_free(ft->priv);
1184 g_free(ft);
1188 /* frees the array and all related pointers */
1189 void filetypes_free_types(void)
1191 g_return_if_fail(filetypes_array != NULL);
1192 g_return_if_fail(filetypes_hash != NULL);
1194 g_ptr_array_foreach(filetypes_array, filetype_free, NULL);
1195 g_ptr_array_free(filetypes_array, TRUE);
1196 g_hash_table_destroy(filetypes_hash);
1200 static void load_settings(gint ft_id, GKeyFile *config, GKeyFile *configh)
1202 GeanyFiletype *ft = filetypes[ft_id];
1203 gchar *result;
1204 GError *error = NULL;
1205 gboolean tmp;
1207 /* default extension */
1208 result = g_key_file_get_string(configh, "settings", "extension", NULL);
1209 if (result == NULL) result = g_key_file_get_string(config, "settings", "extension", NULL);
1210 if (G_LIKELY(result != NULL))
1212 setptr(filetypes[ft_id]->extension, result);
1215 /* read comment notes */
1216 result = g_key_file_get_string(configh, "settings", "comment_open", NULL);
1217 if (result == NULL) result = g_key_file_get_string(config, "settings", "comment_open", NULL);
1218 if (G_LIKELY(result != NULL))
1220 g_free(filetypes[ft_id]->comment_open);
1221 filetypes[ft_id]->comment_open = result;
1224 result = g_key_file_get_string(configh, "settings", "comment_close", NULL);
1225 if (result == NULL) result = g_key_file_get_string(config, "settings", "comment_close", NULL);
1226 if (G_LIKELY(result != NULL))
1228 g_free(filetypes[ft_id]->comment_close);
1229 filetypes[ft_id]->comment_close = result;
1232 tmp = g_key_file_get_boolean(configh, "settings", "comment_use_indent", &error);
1233 if (error)
1235 g_error_free(error);
1236 error = NULL;
1237 tmp = g_key_file_get_boolean(config, "settings", "comment_use_indent", &error);
1238 if (error) g_error_free(error);
1239 else filetypes[ft_id]->comment_use_indent = tmp;
1241 else filetypes[ft_id]->comment_use_indent = tmp;
1243 /* read context action */
1244 result = g_key_file_get_string(configh, "settings", "context_action_cmd", NULL);
1245 if (result == NULL) result = g_key_file_get_string(config, "settings", "context_action_cmd", NULL);
1246 if (G_LIKELY(result != NULL))
1248 filetypes[ft_id]->context_action_cmd = result;
1251 result = utils_get_setting_string(configh, "settings", "tag_parser", NULL);
1252 if (!result)
1253 result = utils_get_setting_string(config, "settings", "tag_parser", NULL);
1254 if (result)
1256 ft->lang = tm_source_file_get_named_lang(result);
1257 if (ft->lang < 0)
1258 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result, ft->name);
1259 g_free(result);
1262 result = utils_get_setting_string(configh, "settings", "lexer_filetype", NULL);
1263 if (!result)
1264 result = utils_get_setting_string(config, "settings", "lexer_filetype", NULL);
1265 if (result)
1267 ft->lexer_filetype = filetypes_lookup_by_name(result);
1268 if (!ft->lexer_filetype)
1269 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result, ft->name);
1270 g_free(result);
1273 /* read build settings */
1274 build_load_menu(config, GEANY_BCS_FT, (gpointer)ft);
1275 build_load_menu(configh, GEANY_BCS_HOME_FT, (gpointer)ft);
1279 /* simple wrapper function to print file errors in DEBUG mode */
1280 static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags,
1281 GeanyFiletype *ft)
1283 GError *error = NULL;
1284 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1286 if (error != NULL)
1288 if (!done && !ft->priv->custom)
1289 geany_debug("Failed to open %s (%s)", file, error->message);
1291 g_error_free(error);
1292 error = NULL;
1297 /* Load the configuration file for the associated filetype id.
1298 * This should only be called when the filetype is needed, to save loading
1299 * 20+ configuration files all at once. */
1300 void filetypes_load_config(gint ft_id, gboolean reload)
1302 GKeyFile *config, *config_home;
1303 GeanyFiletypePrivate *pft;
1304 GeanyFiletype *ft;
1306 g_return_if_fail(ft_id >= 0 && ft_id < (gint) filetypes_array->len);
1308 ft = filetypes[ft_id];
1309 pft = ft->priv;
1311 /* when reloading, proceed only if the settings were already loaded */
1312 if (reload && G_UNLIKELY(! pft->keyfile_loaded))
1313 return;
1315 /* when not reloading, load the settings only once */
1316 if (! reload && G_LIKELY(pft->keyfile_loaded))
1317 return;
1318 pft->keyfile_loaded = TRUE;
1320 config = g_key_file_new();
1321 config_home = g_key_file_new();
1323 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1324 gchar *ext = (ft_id != GEANY_FILETYPES_NONE) ?
1325 filetypes_get_conf_extension(ft_id) : g_strdup("common");
1326 gchar *f0 = g_strconcat(app->datadir, G_DIR_SEPARATOR_S "filetypes.", ext, NULL);
1327 gchar *f = g_strconcat(app->configdir,
1328 G_DIR_SEPARATOR_S GEANY_FILEDEFS_SUBDIR G_DIR_SEPARATOR_S "filetypes.", ext, NULL);
1330 load_system_keyfile(config, f0, G_KEY_FILE_KEEP_COMMENTS, ft);
1331 g_key_file_load_from_file(config_home, f, G_KEY_FILE_KEEP_COMMENTS, NULL);
1333 g_free(ext);
1334 g_free(f);
1335 g_free(f0);
1338 load_settings(ft_id, config, config_home);
1339 highlighting_init_styles(ft_id, config, config_home);
1341 g_key_file_free(config);
1342 g_key_file_free(config_home);
1346 gchar *filetypes_get_conf_extension(gint filetype_idx)
1348 gchar *result;
1349 GeanyFiletype *ft = filetypes[filetype_idx];
1351 if (ft->priv->custom)
1352 return g_strconcat(ft->name, ".conf", NULL);
1354 /* Handle any special extensions different from lowercase filetype->name */
1355 switch (filetype_idx)
1357 case GEANY_FILETYPES_CPP: result = g_strdup("cpp"); break;
1358 case GEANY_FILETYPES_CS: result = g_strdup("cs"); break;
1359 case GEANY_FILETYPES_MAKE: result = g_strdup("makefile"); break;
1360 default: result = g_ascii_strdown(ft->name, -1); break;
1362 return result;
1366 void filetypes_save_commands(void)
1368 gchar *conf_prefix = g_strconcat(app->configdir,
1369 G_DIR_SEPARATOR_S GEANY_FILEDEFS_SUBDIR G_DIR_SEPARATOR_S "filetypes.", NULL);
1370 guint i;
1372 for (i = 1; i < filetypes_array->len; i++)
1374 GKeyFile *config_home;
1375 gchar *fname, *ext, *data;
1377 if (filetypes[i]->home_save_needed)
1379 ext = filetypes_get_conf_extension(i);
1380 fname = g_strconcat(conf_prefix, ext, NULL);
1381 g_free(ext);
1382 config_home = g_key_file_new();
1383 g_key_file_load_from_file(config_home, fname, G_KEY_FILE_KEEP_COMMENTS, NULL);
1384 build_save_menu(config_home, (gpointer)(filetypes[i]), GEANY_BCS_HOME_FT);
1385 data = g_key_file_to_data(config_home, NULL, NULL);
1386 utils_write_file(fname, data);
1387 g_free(data);
1388 g_key_file_free(config_home);
1389 g_free(fname);
1392 g_free(conf_prefix);
1396 /* create one file filter which has each file pattern of each filetype */
1397 GtkFileFilter *filetypes_create_file_filter_all_source(void)
1399 GtkFileFilter *new_filter;
1400 guint i, j;
1402 new_filter = gtk_file_filter_new();
1403 gtk_file_filter_set_name(new_filter, _("All Source"));
1405 for (i = 0; i < filetypes_array->len; i++)
1407 if (G_UNLIKELY(i == GEANY_FILETYPES_NONE))
1408 continue;
1410 for (j = 0; filetypes[i]->pattern[j]; j++)
1412 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1415 return new_filter;
1419 GtkFileFilter *filetypes_create_file_filter(const GeanyFiletype *ft)
1421 GtkFileFilter *new_filter;
1422 gint i;
1423 const gchar *title;
1425 g_return_val_if_fail(ft != NULL, NULL);
1427 new_filter = gtk_file_filter_new();
1428 title = ft->id == GEANY_FILETYPES_NONE ? _("All files") : ft->title;
1429 gtk_file_filter_set_name(new_filter, title);
1431 for (i = 0; ft->pattern[i]; i++)
1433 gtk_file_filter_add_pattern(new_filter, ft->pattern[i]);
1436 return new_filter;
1440 /* Indicates whether there is a tag parser for the filetype or not.
1441 * Only works for custom filetypes if the filetype settings have been loaded. */
1442 gboolean filetype_has_tags(GeanyFiletype *ft)
1444 g_return_val_if_fail(ft != NULL, FALSE);
1446 return ft->lang >= 0;
1450 /** Finds a filetype pointer from its @a name field.
1451 * @param name Filetype name.
1452 * @return The filetype found, or @c NULL.
1454 * @since 0.15
1456 GeanyFiletype *filetypes_lookup_by_name(const gchar *name)
1458 GeanyFiletype *ft;
1460 g_return_val_if_fail(NZV(name), NULL);
1462 ft = g_hash_table_lookup(filetypes_hash, name);
1463 if (G_UNLIKELY(ft == NULL))
1464 geany_debug("Could not find filetype '%s'.", name);
1465 return ft;
1469 #ifdef HAVE_REGCOMP
1470 static gchar *get_regex_match_string(const gchar *message, regmatch_t *pmatch, gint match_idx)
1472 return g_strndup(&message[pmatch[match_idx].rm_so],
1473 pmatch[match_idx].rm_eo - pmatch[match_idx].rm_so);
1477 static void compile_regex(GeanyFiletype *ft, regex_t *regex, gchar *regstr)
1479 gint retval = regcomp(regex, regstr, REG_EXTENDED);
1481 ft->priv->error_regex_compiled = (retval == 0); /* prevent recompilation */
1483 if (G_UNLIKELY(retval != 0))
1485 gchar buf[256];
1486 regerror(retval, regex, buf, sizeof buf);
1487 ui_set_statusbar(TRUE, _("Bad regex for filetype %s: %s"),
1488 ft->name, buf);
1490 /* regex will be freed in set_error_regex(). */
1492 #endif
1495 gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message,
1496 gchar **filename, gint *line)
1498 gchar *regstr;
1499 gchar **tmp;
1500 GeanyDocument *doc;
1501 #ifdef HAVE_REGCOMP
1502 regex_t *regex;
1503 regmatch_t pmatch[3];
1504 #endif
1505 if (ft == NULL)
1507 doc = document_get_current();
1508 if (doc != NULL)
1509 ft = doc->file_type;
1511 tmp = build_get_regex(build_info.grp, ft, NULL);
1512 if (tmp == NULL)
1513 return FALSE;
1514 regstr = *tmp;
1515 #ifndef HAVE_REGCOMP
1516 if (!NZV(regstr))
1517 geany_debug("No regex support - maybe you should configure with --enable-gnu-regex!");
1518 return FALSE;
1519 #else
1520 regex = &ft->priv->error_regex;
1522 *filename = NULL;
1523 *line = -1;
1525 if (!NZV(regstr))
1526 return FALSE;
1528 if (!ft->priv->error_regex_compiled || regstr != ft->priv->last_string)
1530 compile_regex(ft, regex, regstr);
1531 ft->priv->last_string = regstr;
1533 if (!ft->priv->error_regex_compiled) /* regex error */
1534 return FALSE;
1536 if (regexec(regex, message, G_N_ELEMENTS(pmatch), pmatch, 0) != 0)
1537 return FALSE;
1539 if (pmatch[0].rm_so != -1 && pmatch[1].rm_so != -1 && pmatch[2].rm_so != -1)
1541 gchar *first, *second, *end;
1542 glong l;
1544 first = get_regex_match_string(message, pmatch, 1);
1545 second = get_regex_match_string(message, pmatch, 2);
1546 l = strtol(first, &end, 10);
1547 if (*end == '\0') /* first is purely decimals */
1549 *line = l;
1550 g_free(first);
1551 *filename = second;
1553 else
1555 l = strtol(second, &end, 10);
1556 if (*end == '\0')
1558 *line = l;
1559 g_free(second);
1560 *filename = first;
1562 else
1564 g_free(first);
1565 g_free(second);
1569 return *filename != NULL;
1570 #endif
1574 #ifdef G_OS_WIN32
1575 static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1577 guint i;
1578 for (i = 0; i < len; i++)
1580 setptr(patterns[i], g_ascii_strdown(patterns[i], -1));
1583 #endif
1586 void filetypes_read_extensions(void)
1588 guint i;
1589 gsize len = 0;
1590 gchar *sysconfigfile = g_strconcat(app->datadir, G_DIR_SEPARATOR_S,
1591 "filetype_extensions.conf", NULL);
1592 gchar *userconfigfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
1593 "filetype_extensions.conf", NULL);
1594 GKeyFile *sysconfig = g_key_file_new();
1595 GKeyFile *userconfig = g_key_file_new();
1597 g_key_file_load_from_file(sysconfig, sysconfigfile, G_KEY_FILE_NONE, NULL);
1598 g_key_file_load_from_file(userconfig, userconfigfile, G_KEY_FILE_NONE, NULL);
1600 /* read the keys */
1601 for (i = 0; i < filetypes_array->len; i++)
1603 gboolean userset =
1604 g_key_file_has_key(userconfig, "Extensions", filetypes[i]->name, NULL);
1605 gchar **list = g_key_file_get_string_list(
1606 (userset) ? userconfig : sysconfig, "Extensions", filetypes[i]->name, &len, NULL);
1608 if (G_LIKELY(list) && G_LIKELY(len > 0))
1610 g_strfreev(filetypes[i]->pattern);
1611 filetypes[i]->pattern = list;
1612 #ifdef G_OS_WIN32
1613 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1614 #endif
1616 else
1617 g_strfreev(list);
1620 g_free(sysconfigfile);
1621 g_free(userconfigfile);
1622 g_key_file_free(sysconfig);
1623 g_key_file_free(userconfig);
1625 foreach_document(i)
1627 GeanyDocument *doc = documents[i];
1628 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1629 continue;
1630 document_set_filetype(doc, filetypes_detect_from_document(doc));
1635 /** Accessor function for @ref GeanyData::filetypes_array items.
1636 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1637 * @param idx @c filetypes_array index.
1638 * @return The filetype, or @c NULL if @a idx is out of range.
1640 * @since 0.16
1642 GeanyFiletype *filetypes_index(gint idx)
1644 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1648 void filetypes_reload(void)
1650 guint i;
1651 GeanyDocument *current_doc;
1653 /* save possibly changed commands before re-reading them */
1654 filetypes_save_commands();
1656 /* reload filetype configs */
1657 for (i = 0; i < filetypes_array->len; i++)
1659 /* filetypes_load_config() will skip not loaded filetypes */
1660 filetypes_load_config(i, TRUE);
1663 current_doc = document_get_current();
1664 if (!current_doc)
1665 return;
1667 /* update document styling */
1668 foreach_document(i)
1670 if (current_doc != documents[i])
1671 document_reload_config(documents[i]);
1673 /* process the current document at last */
1674 document_reload_config(current_doc);