2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 * Filetype detection, file extensions and filetype menu items.
29 /* Note: we use filetype_id for some function arguments, but GeanyFiletype is better; we should
30 * only use GeanyFiletype for API functions. */
33 #include <glib/gstdio.h>
36 #include "filetypes.h"
37 #include "highlighting.h"
39 #include "templates.h"
42 #include "msgwindow.h"
44 #include "sciwrappers.h"
53 # include "gnuregex.h"
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
;
65 gboolean error_regex_compiled
;
66 gchar
*last_string
; /* last one compiled */
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
);
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
;
101 case TITLE_SOURCE_FILE
: fmt
= _("%s source file"); break;
102 case TITLE_FILE
: fmt
= _("%s file"); break;
104 g_assert(!ft
->title
);
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)
115 #define NONE /* these macros are only to ease navigation */
116 ft
= filetypes
[GEANY_FILETYPES_NONE
];
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
;
127 ft
= filetypes
[GEANY_FILETYPES_C
];
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
;
138 ft
= filetypes
[GEANY_FILETYPES_CPP
];
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
;
150 ft
= filetypes
[GEANY_FILETYPES_CS
];
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
;
161 ft
= filetypes
[GEANY_FILETYPES_VALA
];
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
;
172 ft
= filetypes
[GEANY_FILETYPES_D
];
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
;
183 ft
= filetypes
[GEANY_FILETYPES_JAVA
];
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
];
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",
201 ft
->comment_open
= g_strdup("{");
202 ft
->comment_close
= g_strdup("}");
203 ft
->group
= GEANY_FILETYPE_GROUP_COMPILED
;
206 ft
= filetypes
[GEANY_FILETYPES_ASM
];
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
;
217 ft
= filetypes
[GEANY_FILETYPES_BASIC
];
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
;
228 ft
= filetypes
[GEANY_FILETYPES_FORTRAN
];
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
;
239 ft
= filetypes
[GEANY_FILETYPES_F77
];
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
;
250 ft
= filetypes
[GEANY_FILETYPES_GLSL
];
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
;
261 ft
= filetypes
[GEANY_FILETYPES_CAML
];
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
;
272 ft
= filetypes
[GEANY_FILETYPES_PERL
];
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",
279 ft
->comment_open
= g_strdup("#");
280 ft
->comment_close
= NULL
;
281 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
284 ft
= filetypes
[GEANY_FILETYPES_PHP
];
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",
291 ft
->comment_open
= g_strdup("//");
292 ft
->comment_close
= NULL
;
293 ft
->group
= GEANY_FILETYPE_GROUP_SCRIPT
;
296 ft
= filetypes
[GEANY_FILETYPES_JS
];
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
;
307 ft
= filetypes
[GEANY_FILETYPES_PYTHON
];
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
;
318 ft
= filetypes
[GEANY_FILETYPES_RUBY
];
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
;
329 ft
= filetypes
[GEANY_FILETYPES_TCL
];
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
;
340 ft
= filetypes
[GEANY_FILETYPES_LUA
];
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
;
351 ft
= filetypes
[GEANY_FILETYPES_FERITE
];
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
;
362 ft
= filetypes
[GEANY_FILETYPES_HASKELL
];
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
;
373 ft
= filetypes
[GEANY_FILETYPES_MARKDOWN
];
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
;
384 ft
= filetypes
[GEANY_FILETYPES_TXT2TAGS
];
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
;
395 ft
= filetypes
[GEANY_FILETYPES_ABC
];
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
;
406 ft
= filetypes
[GEANY_FILETYPES_SH
];
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
;
418 ft
= filetypes
[GEANY_FILETYPES_MAKE
];
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
;
430 ft
= filetypes
[GEANY_FILETYPES_XML
];
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
;
442 ft
= filetypes
[GEANY_FILETYPES_DOCBOOK
];
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
;
453 ft
= filetypes
[GEANY_FILETYPES_HTML
];
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
;
465 ft
= filetypes
[GEANY_FILETYPES_CSS
];
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 */
476 ft
= filetypes
[GEANY_FILETYPES_SQL
];
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
;
487 ft
= filetypes
[GEANY_FILETYPES_LATEX
];
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
;
498 ft
= filetypes
[GEANY_FILETYPES_VHDL
];
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
;
509 ft
= filetypes
[GEANY_FILETYPES_VERILOG
];
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
;
520 ft
= filetypes
[GEANY_FILETYPES_DIFF
];
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
;
531 ft
= filetypes
[GEANY_FILETYPES_CONF
];
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
;
543 ft
= filetypes
[GEANY_FILETYPES_PO
];
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
;
554 ft
= filetypes
[GEANY_FILETYPES_HAXE
];
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
;
565 ft
= filetypes
[GEANY_FILETYPES_AS
];
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
;
576 ft
= filetypes
[GEANY_FILETYPES_R
];
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
;
587 ft
= filetypes
[GEANY_FILETYPES_REST
];
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
;
599 ft
= filetypes
[GEANY_FILETYPES_MATLAB
];
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
;
610 ft
= filetypes
[GEANY_FILETYPES_YAML
];
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
;
621 ft
= filetypes
[GEANY_FILETYPES_CMAKE
];
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
;
632 ft
= filetypes
[GEANY_FILETYPES_NSIS
];
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
;
643 ft
= filetypes
[GEANY_FILETYPES_ADA
];
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);
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
))
674 if (G_UNLIKELY(ft2
->id
== GEANY_FILETYPES_NONE
))
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");
702 g_return_if_fail(dot
);
706 if (g_hash_table_lookup(filetypes_hash
, fn
))
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
;
716 geany_debug("Added filetype %s (%d).", ft
->name
, ft
->id
);
720 static void init_custom_filetypes(const gchar
*path
)
723 const gchar
*filename
;
725 g_return_if_fail(path
);
727 dir
= g_dir_open(path
, 0, NULL
);
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
);
745 /* Create the filetypes array and fill it with the known filetypes. */
746 void filetypes_init_types()
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
)))
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
);
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)
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
);
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
)
859 for (i
= 0; i
< filetypes_array
->len
; i
++)
861 GeanyFiletype
*ft
= filetypes
[i
];
863 if (predicate(ft
, user_data
))
870 static gboolean
match_basename(gconstpointer pft
, gconstpointer user_data
)
872 const GeanyFiletype
*ft
= pft
;
873 const gchar
*base_filename
= user_data
;
875 gboolean ret
= FALSE
;
877 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
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
))
887 g_pattern_spec_free(pattern
);
890 g_pattern_spec_free(pattern
);
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
;
903 /* to match against the basename of the file (because of Makefile*) */
904 base_filename
= g_path_get_basename(utf8_filename
);
906 /* use lower case basename */
907 setptr(base_filename
, g_utf8_strdown(base_filename
, -1));
910 ft
= filetypes_find(match_basename
, base_filename
);
912 ft
= filetypes
[GEANY_FILETYPES_NONE
];
914 g_free(base_filename
);
919 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
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
;
927 gboolean result
= FALSE
;
930 ft
= filetypes_detect_from_extension(utf8_filename
);
931 if (ft
== NULL
|| ft
->id
>= filetypes_array
->len
)
934 va_start(args
, first
);
941 if (ft
->id
== (guint
) test
)
946 test
= va_arg(args
, gint
);
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
];
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
];
1032 /* Detect the filetype checking for a shebang, then filename extension. */
1033 static GeanyFiletype
*filetypes_detect_from_file_internal(const gchar
*utf8_filename
,
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
);
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
)
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
);
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
)
1081 gchar
*locale_name
= utils_get_locale_from_utf8(utf8_filename
);
1083 f
= g_fopen(locale_name
, "r");
1084 g_free(locale_name
);
1087 if (fgets(line
, sizeof(line
), f
) != NULL
)
1090 return filetypes_detect_from_file_internal(utf8_filename
, line
);
1094 return filetypes_detect_from_extension(utf8_filename
);
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
);
1105 ft
= filetypes
[GEANY_FILETYPES_NONE
];
1107 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft
->priv
->menu_item
), TRUE
);
1112 on_filetype_change (GtkCheckMenuItem
*menuitem
,
1115 GeanyDocument
*doc
= document_get_current();
1116 if (ignore_callback
|| doc
== NULL
|| ! gtk_check_menu_item_get_active(menuitem
))
1119 document_set_filetype(doc
, (GeanyFiletype
*)user_data
);
1123 static void create_radio_menu_item(GtkWidget
*menu
, GeanyFiletype
*ftype
)
1125 static GSList
*group
= NULL
;
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
);
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
);
1151 static void set_error_regex(GeanyFiletype
*ft
, gchar
*string
)
1153 setptr(ft
->error_regex_string
, string
);
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 */
1165 static void filetype_free(gpointer data
, G_GNUC_UNUSED gpointer user_data
)
1167 GeanyFiletype
*ft
= data
;
1169 g_return_if_fail(ft
!= NULL
);
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
);
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
];
1204 GError
*error
= NULL
;
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
);
1235 g_error_free(error
);
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
);
1253 result
= utils_get_setting_string(config
, "settings", "tag_parser", NULL
);
1256 ft
->lang
= tm_source_file_get_named_lang(result
);
1258 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result
, ft
->name
);
1262 result
= utils_get_setting_string(configh
, "settings", "lexer_filetype", NULL
);
1264 result
= utils_get_setting_string(config
, "settings", "lexer_filetype", NULL
);
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
);
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
,
1283 GError
*error
= NULL
;
1284 gboolean done
= g_key_file_load_from_file(key_file
, file
, flags
, &error
);
1288 if (!done
&& !ft
->priv
->custom
)
1289 geany_debug("Failed to open %s (%s)", file
, error
->message
);
1291 g_error_free(error
);
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
;
1306 g_return_if_fail(ft_id
>= 0 && ft_id
< (gint
) filetypes_array
->len
);
1308 ft
= filetypes
[ft_id
];
1311 /* when reloading, proceed only if the settings were already loaded */
1312 if (reload
&& G_UNLIKELY(! pft
->keyfile_loaded
))
1315 /* when not reloading, load the settings only once */
1316 if (! reload
&& G_LIKELY(pft
->keyfile_loaded
))
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
);
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
)
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;
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
);
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
);
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
);
1388 g_key_file_free(config_home
);
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
;
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
))
1410 for (j
= 0; filetypes
[i
]->pattern
[j
]; j
++)
1412 gtk_file_filter_add_pattern(new_filter
, filetypes
[i
]->pattern
[j
]);
1419 GtkFileFilter
*filetypes_create_file_filter(const GeanyFiletype
*ft
)
1421 GtkFileFilter
*new_filter
;
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
]);
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.
1456 GeanyFiletype
*filetypes_lookup_by_name(const gchar
*name
)
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
);
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))
1486 regerror(retval
, regex
, buf
, sizeof buf
);
1487 ui_set_statusbar(TRUE
, _("Bad regex for filetype %s: %s"),
1490 /* regex will be freed in set_error_regex(). */
1495 gboolean
filetypes_parse_error_message(GeanyFiletype
*ft
, const gchar
*message
,
1496 gchar
**filename
, gint
*line
)
1503 regmatch_t pmatch
[3];
1507 doc
= document_get_current();
1509 ft
= doc
->file_type
;
1511 tmp
= build_get_regex(build_info
.grp
, ft
, NULL
);
1515 #ifndef HAVE_REGCOMP
1517 geany_debug("No regex support - maybe you should configure with --enable-gnu-regex!");
1520 regex
= &ft
->priv
->error_regex
;
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 */
1536 if (regexec(regex
, message
, G_N_ELEMENTS(pmatch
), pmatch
, 0) != 0)
1539 if (pmatch
[0].rm_so
!= -1 && pmatch
[1].rm_so
!= -1 && pmatch
[2].rm_so
!= -1)
1541 gchar
*first
, *second
, *end
;
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 */
1555 l
= strtol(second
, &end
, 10);
1569 return *filename
!= NULL
;
1575 static void convert_filetype_extensions_to_lower_case(gchar
**patterns
, gsize len
)
1578 for (i
= 0; i
< len
; i
++)
1580 setptr(patterns
[i
], g_ascii_strdown(patterns
[i
], -1));
1586 void filetypes_read_extensions(void)
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
);
1601 for (i
= 0; i
< filetypes_array
->len
; i
++)
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
;
1613 convert_filetype_extensions_to_lower_case(filetypes
[i
]->pattern
, len
);
1620 g_free(sysconfigfile
);
1621 g_free(userconfigfile
);
1622 g_key_file_free(sysconfig
);
1623 g_key_file_free(userconfig
);
1627 GeanyDocument
*doc
= documents
[i
];
1628 if (doc
->file_type
->id
!= GEANY_FILETYPES_NONE
)
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.
1642 GeanyFiletype
*filetypes_index(gint idx
)
1644 return (idx
>= 0 && idx
< (gint
) filetypes_array
->len
) ? filetypes
[idx
] : NULL
;
1648 void filetypes_reload(void)
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();
1667 /* update document styling */
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
);