geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

filetypes.c
Go to the documentation of this file.
1/*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2005 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/**
22 * @file filetypes.h
23 * Filetype detection, file extensions and filetype menu items.
24 */
25
26/* Note: we use GeanyFiletypeID for some function arguments, but GeanyFiletype is better; we should
27 * only use GeanyFiletype for API functions. */
28
29#ifdef HAVE_CONFIG_H
30# include "config.h"
31#endif
32
33#include "filetypes.h"
34
35#include "app.h"
36#include "callbacks.h" /* FIXME: for ignore_callback */
37#include "document.h"
38#include "filetypesprivate.h"
39#include "geany.h"
40#include "geanyobject.h"
41#include "highlighting.h"
42#include "projectprivate.h"
43#include "sciwrappers.h"
44#include "support.h"
45#include "symbols.h"
46#include "tm_parser.h"
47#include "utils.h"
48#include "ui_utils.h"
49
50#include <stdlib.h>
51#include <string.h>
52
53#include <glib/gstdio.h>
54
55#define GEANY_FILETYPE_SEARCH_LINES 2 /* lines of file to search for filetype */
56
57GPtrArray *filetypes_array = NULL;
58static GHashTable *filetypes_hash = NULL; /* Hash of filetype pointers based on name keys */
60
61
62static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype);
63
64static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft);
65static void read_filetype_config(void);
66static void create_set_filetype_menu(gboolean config);
67static gchar *filetypes_get_filename(GeanyFiletype *ft, gboolean user);
68
69
71{
77};
78
79/* Save adding many translation strings if the filetype name doesn't need translating */
80static gchar *filetype_make_title(const char *name, enum TitleType type)
81{
82 g_return_val_if_fail(name != NULL, NULL);
83
84 switch (type)
85 {
86 case TITLE_SOURCE_FILE: return g_strdup_printf(_("%s source file"), name);
87 case TITLE_FILE: return g_strdup_printf(_("%s file"), name);
88 case TITLE_SCRIPT: return g_strdup_printf(_("%s script"), name);
89 case TITLE_DOCUMENT: return g_strdup_printf(_("%s document"), name);
90 case TITLE_NONE: /* fall through */
91 default: return g_strdup(name);
92 }
93}
94
95
96/* name argument (ie filetype name) must not be translated as it is used for
97 * filetype lookup. Use filetypes_get_display_name() instead.*/
98static void ft_init(GeanyFiletypeID ft_id, TMParserType lang, const char *name,
99 const char *title_name, enum TitleType title_type,
100 GeanyFiletypeGroupID group_id)
101{
102 GeanyFiletype *ft = filetypes[ft_id];
103 ft->lang = lang;
104 ft->name = g_strdup(name);
105 ft->title = filetype_make_title((title_name != NULL) ? title_name : ft->name, title_type);
106 ft->group = group_id;
107}
108
109/* Evil macro to save typing and make init_builtin_filetypes() more readable */
110#define FT_INIT(ft_id, parser_id, name, title_name, title_type, group_id) \
111 ft_init(GEANY_FILETYPES_##ft_id, TM_PARSER_##parser_id, name, title_name, \
112 TITLE_##title_type, GEANY_FILETYPE_GROUP_##group_id)
113
114
115/* Note: remember to update HACKING if this function is renamed. */
116static void init_builtin_filetypes(void)
117{
118 /* Column legend:
119 * [0] = Filetype constant (GEANY_FILETYPES_*)
120 * [1] = CTags parser (TM_PARSER_*)
121 * [2] = Non-translated filetype name (*not* label for display)
122 * [3] = Translatable human filetype title prefix or NULL to use [2]
123 * [4] = Title type (TITLE_*) constant (ex. TITLE_SOURCE_FILE is 'source file' suffix)
124 * [5] = The filetype group constant (GEANY_FILETYPE_GROUP_*)
125 * --------------------------------------------------------------------------------------------------------------------------
126 * [0] [1] [2] [3] [4] [5] */
127 FT_INIT( NONE, NONE, "None", _("None"), NONE, NONE );
128 FT_INIT( C, C, "C", NULL, SOURCE_FILE, COMPILED );
129 FT_INIT( CPP, CPP, "C++", NULL, SOURCE_FILE, COMPILED );
130 FT_INIT( OBJECTIVEC, OBJC, "Objective-C", NULL, SOURCE_FILE, COMPILED );
131 FT_INIT( CS, CSHARP, "C#", NULL, SOURCE_FILE, COMPILED );
132 FT_INIT( VALA, VALA, "Vala", NULL, SOURCE_FILE, COMPILED );
133 FT_INIT( D, D, "D", NULL, SOURCE_FILE, COMPILED );
134 FT_INIT( JAVA, JAVA, "Java", NULL, SOURCE_FILE, COMPILED );
135 FT_INIT( PASCAL, PASCAL, "Pascal", NULL, SOURCE_FILE, COMPILED );
136 FT_INIT( ASM, ASM, "ASM", "Assembler", SOURCE_FILE, COMPILED );
137 FT_INIT( BASIC, FREEBASIC, "FreeBasic", NULL, SOURCE_FILE, COMPILED );
138 FT_INIT( FORTRAN, FORTRAN, "Fortran", "Fortran (F90)", SOURCE_FILE, COMPILED );
139 FT_INIT( F77, F77, "F77", "Fortran (F77)", SOURCE_FILE, COMPILED );
140 FT_INIT( GLSL, GLSL, "GLSL", NULL, SOURCE_FILE, COMPILED );
141 FT_INIT( CAML, NONE, "CAML", "(O)Caml", SOURCE_FILE, COMPILED );
142 FT_INIT( PERL, PERL, "Perl", NULL, SOURCE_FILE, SCRIPT );
143 FT_INIT( PHP, PHP, "PHP", NULL, SOURCE_FILE, SCRIPT );
144 FT_INIT( JS, JAVASCRIPT, "Javascript", NULL, SOURCE_FILE, SCRIPT );
145 FT_INIT( PYTHON, PYTHON, "Python", NULL, SOURCE_FILE, SCRIPT );
146 FT_INIT( RUBY, RUBY, "Ruby", NULL, SOURCE_FILE, SCRIPT );
147 FT_INIT( TCL, TCL, "Tcl", NULL, SOURCE_FILE, SCRIPT );
148 FT_INIT( LUA, LUA, "Lua", NULL, SOURCE_FILE, SCRIPT );
149 FT_INIT( FERITE, FERITE, "Ferite", NULL, SOURCE_FILE, SCRIPT );
150 FT_INIT( HASKELL, HASKELL, "Haskell", NULL, SOURCE_FILE, COMPILED );
151 FT_INIT( MARKDOWN, MARKDOWN, "Markdown", NULL, SOURCE_FILE, MARKUP );
152 FT_INIT( TXT2TAGS, TXT2TAGS, "Txt2tags", NULL, SOURCE_FILE, MARKUP );
153 FT_INIT( ABC, ABC, "Abc", NULL, FILE, MISC );
154 FT_INIT( SH, SH, "Sh", _("Shell"), SCRIPT, SCRIPT );
155 FT_INIT( MAKE, MAKEFILE, "Make", _("Makefile"), NONE, SCRIPT );
156 FT_INIT( XML, NONE, "XML", NULL, DOCUMENT, MARKUP );
157 FT_INIT( DOCBOOK, DOCBOOK, "Docbook", NULL, DOCUMENT, MARKUP );
158 FT_INIT( HTML, HTML, "HTML", NULL, DOCUMENT, MARKUP );
159 FT_INIT( CSS, CSS, "CSS", _("Cascading Stylesheet"), NONE, MARKUP ); /* not really markup but fit quite well to HTML */
160 FT_INIT( SQL, SQL, "SQL", NULL, FILE, MISC );
161 FT_INIT( COBOL, COBOL, "COBOL", NULL, SOURCE_FILE, COMPILED );
162 FT_INIT( LATEX, LATEX, "LaTeX", NULL, SOURCE_FILE, MARKUP );
163 FT_INIT( BIBTEX, BIBTEX, "BibTeX", NULL, SOURCE_FILE, MARKUP );
164 FT_INIT( VHDL, VHDL, "VHDL", NULL, SOURCE_FILE, COMPILED );
165 FT_INIT( VERILOG, VERILOG, "Verilog", NULL, SOURCE_FILE, COMPILED );
166 FT_INIT( DIFF, DIFF, "Diff", NULL, FILE, MISC );
167 FT_INIT( LISP, NONE, "Lisp", NULL, SOURCE_FILE, SCRIPT );
168 FT_INIT( ERLANG, ERLANG, "Erlang", NULL, SOURCE_FILE, COMPILED );
169 FT_INIT( CONF, CONF, "Conf", _("Config"), FILE, MISC );
170 FT_INIT( PO, NONE, "Po", _("Gettext translation"), FILE, MISC );
171 FT_INIT( HAXE, HAXE, "Haxe", NULL, SOURCE_FILE, COMPILED );
172 FT_INIT( AS, ACTIONSCRIPT, "ActionScript", NULL, SOURCE_FILE, SCRIPT );
173 FT_INIT( R, R, "R", NULL, SOURCE_FILE, SCRIPT );
174 FT_INIT( REST, REST, "reStructuredText", NULL, SOURCE_FILE, MARKUP );
175 FT_INIT( MATLAB, MATLAB, "Matlab/Octave", NULL, SOURCE_FILE, SCRIPT );
176 FT_INIT( YAML, NONE, "YAML", NULL, FILE, MISC );
177 FT_INIT( CMAKE, NONE, "CMake", NULL, SOURCE_FILE, SCRIPT );
178 FT_INIT( NSIS, NSIS, "NSIS", NULL, SOURCE_FILE, SCRIPT );
179 FT_INIT( ADA, NONE, "Ada", NULL, SOURCE_FILE, COMPILED );
180 FT_INIT( FORTH, NONE, "Forth", NULL, SOURCE_FILE, SCRIPT );
181 FT_INIT( ASCIIDOC, ASCIIDOC, "Asciidoc", NULL, SOURCE_FILE, MARKUP );
182 FT_INIT( ABAQUS, ABAQUS, "Abaqus", NULL, SOURCE_FILE, SCRIPT );
183 FT_INIT( BATCH, NONE, "Batch", NULL, SCRIPT, SCRIPT );
184 FT_INIT( POWERSHELL, POWERSHELL, "PowerShell", NULL, SOURCE_FILE, SCRIPT );
185 FT_INIT( RUST, RUST, "Rust", NULL, SOURCE_FILE, COMPILED );
186 FT_INIT( COFFEESCRIPT, NONE, "CoffeeScript", NULL, SOURCE_FILE, SCRIPT );
187 FT_INIT( GO, GO, "Go", NULL, SOURCE_FILE, COMPILED );
188 FT_INIT( ZEPHIR, ZEPHIR, "Zephir", NULL, SOURCE_FILE, COMPILED );
189 FT_INIT( SMALLTALK, NONE, "Smalltalk", NULL, SOURCE_FILE, SCRIPT );
190 FT_INIT( JULIA, JULIA, "Julia", NULL, SOURCE_FILE, SCRIPT );
191}
192
193
194/* initialize fields. */
196{
197 GeanyFiletype *ft = g_new0(GeanyFiletype, 1);
198
200 ft->lang = TM_PARSER_NONE; /* assume no tagmanager parser */
201 /* pattern must not be null */
202 ft->pattern = g_new0(gchar*, 1);
203 ft->indent_width = -1;
204 ft->indent_type = -1;
205
206 ft->priv = g_new0(GeanyFiletypePrivate, 1);
207 ft->priv->project_list_entry = -1; /* no entry */
208
209 return ft;
210}
211
212
213static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2, gpointer data)
214{
215 gboolean by_name = GPOINTER_TO_INT(data);
216 const GeanyFiletype *ft1 = pft1, *ft2 = pft2;
217
218 if (G_UNLIKELY(ft1->id == GEANY_FILETYPES_NONE))
219 return -1;
220 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
221 return 1;
222
223 return by_name ?
224 utils_str_casecmp(ft1->name, ft2->name) :
225 utils_str_casecmp(ft1->title, ft2->title);
226}
227
228
229/** Gets a list of filetype pointers sorted by name.
230 * The list does not change on subsequent calls.
231 * @return @elementtype{GeanyFiletype} @transfer{none} The list - do not free.
232 * @see filetypes_by_title. */
233GEANY_API_SYMBOL
235{
236 static GSList *list = NULL;
237
238 g_return_val_if_fail(filetypes_by_title, NULL);
239
240 if (!list)
241 {
242 list = g_slist_copy(filetypes_by_title);
243 list = g_slist_sort_with_data(list, cmp_filetype, GINT_TO_POINTER(TRUE));
244 }
245 return list;
246}
247
248
249/* Add a filetype pointer to the lists of available filetypes,
250 * and set the filetype::id field. */
252{
253 g_return_if_fail(ft);
254 g_return_if_fail(ft->name);
255
256 ft->id = filetypes_array->len; /* len will be the index for filetype_array */
257 g_ptr_array_add(filetypes_array, ft);
258 g_hash_table_insert(filetypes_hash, ft->name, ft);
259
260 /* list will be sorted later */
261 filetypes_by_title = g_slist_prepend(filetypes_by_title, ft);
262}
263
264
265static void add_custom_filetype(const gchar *filename)
266{
267 gchar *fn = utils_strdupa(strstr(filename, ".") + 1);
268 gchar *dot = g_strrstr(fn, ".conf");
269 GeanyFiletype *ft;
270
271 g_return_if_fail(dot);
272
273 *dot = 0x0;
274
275 if (g_hash_table_lookup(filetypes_hash, fn))
276 return;
277
278 ft = filetype_new();
279 ft->name = g_strdup(fn);
281 ft->priv->custom = TRUE;
282 filetype_add(ft);
283 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
284}
285
286
287static void init_custom_filetypes(const gchar *path)
288{
289 GDir *dir;
290 const gchar *filename;
291
292 g_return_if_fail(path);
293
294 dir = g_dir_open(path, 0, NULL);
295 if (dir == NULL)
296 return;
297
299 {
300 const gchar prefix[] = "filetypes.";
301
302 if (g_str_has_prefix(filename, prefix) &&
303 g_str_has_suffix(filename + strlen(prefix), ".conf"))
304 {
306 }
307 }
308 g_dir_close(dir);
309}
310
311
312/* Create the filetypes array and fill it with the known filetypes.
313 * Warning: GTK isn't necessarily initialized yet. */
315{
316 GeanyFiletypeID ft_id;
317 gchar *f;
318
319 g_return_if_fail(filetypes_array == NULL);
320 g_return_if_fail(filetypes_hash == NULL);
321
322 filetypes_array = g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES);
323 filetypes_hash = g_hash_table_new(g_str_hash, g_str_equal);
324
325 /* Create built-in filetypes */
326 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
327 {
328 filetypes[ft_id] = filetype_new();
329 }
331
332 /* Add built-in filetypes to the hash now the name fields are set */
333 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
334 {
335 filetype_add(filetypes[ft_id]);
336 }
337 f = g_build_filename(app->datadir, GEANY_FILEDEFS_SUBDIR, NULL);
339 g_free(f);
340 f = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, NULL);
342 g_free(f);
343
344 /* sort last instead of on insertion to prevent exponential time */
345 filetypes_by_title = g_slist_sort_with_data(filetypes_by_title,
346 cmp_filetype, GINT_TO_POINTER(FALSE));
347
349}
350
351
352static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
353{
354 gchar *f, *basename;
355
356 g_return_if_fail(!EMPTY(doc->real_path));
357
358 f = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
359 if (utils_str_equal(doc->real_path, f))
361 g_free(f);
362
363 basename = g_path_get_basename(doc->real_path);
364 if (g_str_has_prefix(basename, "filetypes."))
365 {
366 guint i;
367
368 for (i = 0; i < filetypes_array->len; i++)
369 {
370 GeanyFiletype *ft = filetypes[i];
371
372 f = filetypes_get_filename(ft, TRUE);
373 if (utils_str_equal(doc->real_path, f))
374 {
375 guint j;
376
377 /* Note: we don't reload other filetypes, even though the named styles may have changed.
378 * The user can do this manually with 'Tools->Reload Configuration' */
379 filetypes_load_config(i, TRUE);
380
383
384 g_free(f);
385 break;
386 }
387 g_free(f);
388 }
389 }
390 g_free(basename);
391}
392
393
394static void setup_config_file_menus(void)
395{
396 gchar *f;
397
398 f = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
400 SETPTR(f, g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL));
402 g_free(f);
403
405
406 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
407}
408
409
410static GtkWidget *create_sub_menu(GtkWidget *parent, const gchar *title)
411{
412 GtkWidget *menu, *item;
413
414 menu = gtk_menu_new();
415 item = gtk_menu_item_new_with_mnemonic((title));
416 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
417 gtk_container_add(GTK_CONTAINER(parent), item);
418 gtk_widget_show(item);
419
420 return menu;
421}
422
423
424static void create_set_filetype_menu(gboolean config)
425{
426 GtkWidget *group_menus[GEANY_FILETYPE_GROUP_COUNT] = {NULL};
427 GSList *node;
428 GtkWidget *menu;
429
430 menu = config ? ui_widgets.config_files_filetype_menu :
431 ui_lookup_widget(main_widgets.window, "set_filetype1_menu");
432
433 group_menus[GEANY_FILETYPE_GROUP_COMPILED] = create_sub_menu(menu, _("_Programming Languages"));
434 group_menus[GEANY_FILETYPE_GROUP_SCRIPT] = create_sub_menu(menu, _("_Scripting Languages"));
435 group_menus[GEANY_FILETYPE_GROUP_MARKUP] = create_sub_menu(menu, _("_Markup Languages"));
436 group_menus[GEANY_FILETYPE_GROUP_MISC] = create_sub_menu(menu, _("M_iscellaneous"));
437
438 /* Append all filetypes to the menu */
440 {
441 GeanyFiletype *ft = node->data;
442 GtkWidget *parent = (ft->group != GEANY_FILETYPE_GROUP_NONE) ? group_menus[ft->group] : menu;
443
444 /* we already have filetypes.common config entry */
445 if (config && ft->id == GEANY_FILETYPES_NONE)
446 continue;
447
448 if (config)
449 {
450 gchar *filename = filetypes_get_filename(ft, TRUE);
451
452 ui_add_config_file_menu_item(filename, NULL, GTK_CONTAINER(parent));
453 g_free(filename);
454 }
455 else
456 create_radio_menu_item(parent, ft);
457 }
458}
459
460
462{
466}
467
468
469static guint match_basename(const GeanyFiletype *ft, const gchar *base_filename)
470{
471 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
472 return 0;
473
474 for (guint j = 0; ft->pattern[j] != NULL; j++)
475 {
476 gchar *pat = ft->pattern[j];
477
478 if (g_pattern_match_simple(pat, base_filename))
479 {
480 return strlen(pat);
481 }
482 }
483 return 0;
484}
485
486
487static GeanyFiletype *detect_filetype_conf_file(const gchar *utf8_filename)
488{
489 gchar *lfn = NULL;
490 gchar *path;
491 gboolean found = FALSE;
492
493#ifdef G_OS_WIN32
494 /* use lower case basename */
495 lfn = g_utf8_strdown(utf8_filename, -1);
496#else
497 lfn = g_strdup(utf8_filename);
498#endif
500
501 path = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL);
502 if (g_str_has_prefix(lfn, path))
503 found = TRUE;
504
505 SETPTR(path, g_build_filename(app->datadir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL));
506 if (g_str_has_prefix(lfn, path))
507 found = TRUE;
508
509 g_free(path);
510 g_free(lfn);
511 return found ? filetypes[GEANY_FILETYPES_CONF] : NULL;
512}
513
514
515/* Detect filetype only based on the filename extension.
516 * utf8_filename can include the full path. */
518{
519 gchar *base_filename;
520 GeanyFiletype *ft;
521 guint plen = 0;
522
523 ft = detect_filetype_conf_file(utf8_filename);
524 if (ft)
525 return ft;
526
527 /* to match against the basename of the file (because of Makefile*) */
528 base_filename = g_path_get_basename(utf8_filename);
529#ifdef G_OS_WIN32
530 /* use lower case basename */
531 SETPTR(base_filename, g_utf8_strdown(base_filename, -1));
532#endif
533
534 for (guint i = 0; i < filetypes_array->len; i++)
535 {
536 guint mlen = match_basename(filetypes[i], base_filename);
537
538 if (mlen > plen)
539 { // longest pattern match wins
540 plen = mlen;
541 ft = filetypes[i];
542 }
543 else if (mlen == plen && ft && !ft->priv->user_extensions &&
544 filetypes[i]->priv->user_extensions)
545 { // user config overrides system if pattern len same
546 ft = filetypes[i];
547 }
548 }
549 if (ft == NULL)
551
552 g_free(base_filename);
553 return ft;
554}
555
556
557/* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
558 * terminated by -1.
559 * The detected filetype of the file is checked against every id in the passed list and if
560 * there is a match, TRUE is returned. */
561static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first, ...)
562{
563 GeanyFiletype *ft = NULL;
564 gint test;
565 gboolean result = FALSE;
566 va_list args;
567
568 ft = filetypes_detect_from_extension(utf8_filename);
569 if (ft == NULL || ft->id >= filetypes_array->len)
570 return FALSE;
571
572 va_start(args, first);
573 test = first;
574 while (1)
575 {
576 if (test == -1)
577 break;
578
579 if (ft->id == (guint) test)
580 {
581 result = TRUE;
582 break;
583 }
584 test = va_arg(args, gint);
585 }
586 va_end(args);
587
588 return result;
589}
590
591
592static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line)
593{
594 GeanyFiletype *ft = NULL;
595
596 if (strlen(line) > 2 && line[0] == '#' && line[1] == '!')
597 {
598 static const struct {
599 const gchar *name;
600 GeanyFiletypeID filetype;
601 } intepreter_map[] = {
602 { "sh", GEANY_FILETYPES_SH },
603 { "bash", GEANY_FILETYPES_SH },
604 { "dash", GEANY_FILETYPES_SH },
605 { "perl", GEANY_FILETYPES_PERL },
606 { "python", GEANY_FILETYPES_PYTHON },
607 { "php", GEANY_FILETYPES_PHP },
608 { "ruby", GEANY_FILETYPES_RUBY },
609 { "tcl", GEANY_FILETYPES_TCL },
610 { "make", GEANY_FILETYPES_MAKE },
611 { "zsh", GEANY_FILETYPES_SH },
612 { "ksh", GEANY_FILETYPES_SH },
613 { "mksh", GEANY_FILETYPES_SH },
614 { "csh", GEANY_FILETYPES_SH },
615 { "tcsh", GEANY_FILETYPES_SH },
616 { "ash", GEANY_FILETYPES_SH },
617 { "dmd", GEANY_FILETYPES_D },
618 { "wish", GEANY_FILETYPES_TCL },
619 { "node", GEANY_FILETYPES_JS },
620 { "rust", GEANY_FILETYPES_RUST }
621 };
622 gchar *tmp = g_path_get_basename(line + 2);
623 gchar *basename_interpreter = tmp;
624 guint i;
625
626 if (g_str_has_prefix(tmp, "env "))
627 { /* skip "env" and read the following interpreter */
628 basename_interpreter += 4;
629 }
630
631 for (i = 0; ! ft && i < G_N_ELEMENTS(intepreter_map); i++)
632 {
633 if (g_str_has_prefix(basename_interpreter, intepreter_map[i].name))
634 ft = filetypes[intepreter_map[i].filetype];
635 }
636 g_free(tmp);
637 }
638 /* detect HTML files */
639 if (g_str_has_prefix(line, "<!DOCTYPE html") || g_str_has_prefix(line, "<html"))
640 {
641 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
642 * extension and use the detected filetype, else assume HTML */
643 if (! shebang_find_and_match_filetype(utf8_filename,
645 {
647 }
648 }
649 /* detect XML files */
650 else if (utf8_filename && g_str_has_prefix(line, "<?xml"))
651 {
652 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
653 * extension and use the detected filetype, else assume XML */
654 if (! shebang_find_and_match_filetype(utf8_filename,
656 /* Perl, Python and PHP only to be safe */
658 {
660 }
661 }
662 else if (g_str_has_prefix(line, "<?php"))
663 {
665 }
666 return ft;
667}
668
669
670/* Detect the filetype checking for a shebang, then filename extension.
671 * @lines: an strv of the lines to scan (must containing at least one line) */
672static GeanyFiletype *filetypes_detect_from_file_internal(const gchar *utf8_filename,
673 gchar **lines)
674{
675 GeanyFiletype *ft;
676 gint i;
677 GRegex *ft_regex;
678 GMatchInfo *match;
679 GError *regex_error = NULL;
680
681 /* try to find a shebang and if found use it prior to the filename extension
682 * also checks for <?xml */
683 ft = find_shebang(utf8_filename, lines[0]);
684 if (ft != NULL)
685 return ft;
686
687 /* try to extract the filetype using a regex capture */
688 ft_regex = g_regex_new(file_prefs.extract_filetype_regex,
689 G_REGEX_RAW | G_REGEX_MULTILINE, 0, &regex_error);
690 if (ft_regex != NULL)
691 {
692 for (i = 0; ft == NULL && lines[i] != NULL; i++)
693 {
694 if (g_regex_match(ft_regex, lines[i], 0, &match))
695 {
696 gchar *capture = g_match_info_fetch(match, 1);
697 if (capture != NULL)
698 {
699 ft = filetypes_lookup_by_name(capture);
700 g_free(capture);
701 }
702 }
703 g_match_info_free(match);
704 }
705 g_regex_unref(ft_regex);
706 }
707 else if (regex_error != NULL)
708 {
709 geany_debug("Filetype extract regex ignored: %s", regex_error->message);
710 g_error_free(regex_error);
711 }
712 if (ft != NULL)
713 return ft;
714
715 if (utf8_filename == NULL)
717
718 return filetypes_detect_from_extension(utf8_filename);
719}
720
721
722/* Detect the filetype for the document, checking for a shebang, then filename extension. */
724{
725 GeanyFiletype *ft;
727 gint i;
728
729 g_return_val_if_fail(doc == NULL || doc->is_valid, filetypes[GEANY_FILETYPES_NONE]);
730
731 if (doc == NULL)
733
734 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
735 {
736 lines[i] = sci_get_line(doc->editor->sci, i);
737 }
738 lines[i] = NULL;
740 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
741 {
742 g_free(lines[i]);
743 }
744 return ft;
745}
746
747
748#ifdef HAVE_PLUGINS
749/* Currently only used by external plugins (e.g. geanyprj). */
750/**
751 * Detects filetype based on a shebang line in the file or the filename extension.
752 *
753 * @param utf8_filename The filename in UTF-8 encoding.
754 *
755 * @return @transfer{none} The detected filetype for @a utf8_filename or
756 * @c filetypes[GEANY_FILETYPES_NONE] if it could not be detected.
757 **/
758GEANY_API_SYMBOL
759GeanyFiletype *filetypes_detect_from_file(const gchar *utf8_filename)
760{
761 gchar line[1024];
762 gchar *lines[2];
763 FILE *f;
764 gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
765
766 f = g_fopen(locale_name, "r");
767 g_free(locale_name);
768 if (f != NULL)
769 {
770 if (fgets(line, sizeof(line), f) != NULL)
771 {
772 fclose(f);
773 lines[0] = line;
774 lines[1] = NULL;
775 return filetypes_detect_from_file_internal(utf8_filename, lines);
776 }
777 fclose(f);
778 }
779 return filetypes_detect_from_extension(utf8_filename);
780}
781#endif
782
783
785{
786 /* ignore_callback has to be set by the caller */
787 g_return_if_fail(ignore_callback);
788
789 if (ft == NULL)
791
792 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
793}
794
795
796static void
797on_filetype_change(GtkCheckMenuItem *menuitem, gpointer user_data)
798{
800 if (ignore_callback || doc == NULL || ! gtk_check_menu_item_get_active(menuitem))
801 return;
802
803 document_set_filetype(doc, (GeanyFiletype*)user_data);
804}
805
806
807static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
808{
809 static GSList *group = NULL;
810 GtkWidget *tmp;
811
812 tmp = gtk_radio_menu_item_new_with_label(group, ftype->title);
813 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp));
814 ftype->priv->menu_item = tmp;
815 gtk_widget_show(tmp);
816 gtk_container_add(GTK_CONTAINER(menu), tmp);
817 g_signal_connect(tmp, "activate", G_CALLBACK(on_filetype_change), (gpointer) ftype);
818}
819
820
821static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
822{
823 GeanyFiletype *ft = data;
824
825 g_return_if_fail(ft != NULL);
826
827 g_free(ft->name);
828 g_free(ft->title);
829 g_free(ft->extension);
830 g_free(ft->mime_type);
831 g_free(ft->comment_open);
832 g_free(ft->comment_close);
833 g_free(ft->comment_single);
834 g_free(ft->context_action_cmd);
835 g_free(ft->priv->filecmds);
836 g_free(ft->priv->ftdefcmds);
837 g_free(ft->priv->execcmds);
838 g_free(ft->error_regex_string);
839 if (ft->icon)
840 g_object_unref(ft->icon);
841 g_strfreev(ft->pattern);
842
843 if (ft->priv->error_regex)
844 g_regex_unref(ft->priv->error_regex);
845 g_slist_foreach(ft->priv->tag_files, (GFunc) g_free, NULL);
846 g_slist_free(ft->priv->tag_files);
847
848 g_free(ft->priv);
849 g_free(ft);
850}
851
852
853/* frees the array and all related pointers */
855{
856 g_return_if_fail(filetypes_array != NULL);
857 g_return_if_fail(filetypes_hash != NULL);
858
859 g_ptr_array_foreach(filetypes_array, filetype_free, NULL);
860 g_ptr_array_free(filetypes_array, TRUE);
861 g_hash_table_destroy(filetypes_hash);
862}
863
864
865static void load_indent_settings(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
866{
867 ft->indent_width = utils_get_setting(integer, configh, config, "indentation", "width", -1);
868 ft->indent_type = utils_get_setting(integer, configh, config, "indentation", "type", -1);
869 /* check whether the indent type is OK */
870 switch (ft->indent_type)
871 {
875 case -1:
876 break;
877
878 default:
879 g_warning("Invalid indent type %d in file type %s", ft->indent_type, ft->name);
880 ft->indent_type = -1;
881 break;
882 }
883}
884
885
886static void load_settings(guint ft_id, GKeyFile *config, GKeyFile *configh)
887{
888 GeanyFiletype *ft = filetypes[ft_id];
889 gchar *result;
890
891 /* default extension */
892 result = utils_get_setting(string, configh, config, "settings", "extension", NULL);
893 if (result != NULL)
894 {
895 SETPTR(filetypes[ft_id]->extension, result);
896 }
897
898 /* MIME type */
899 result = utils_get_setting(string, configh, config, "settings", "mime_type", "text/plain");
900 SETPTR(filetypes[ft_id]->mime_type, result);
901
902 /* read comment notes */
903 result = utils_get_setting(string, configh, config, "settings", "comment_open", NULL);
904 if (result != NULL)
905 {
906 SETPTR(filetypes[ft_id]->comment_open, result);
907 }
908
909 result = utils_get_setting(string, configh, config, "settings", "comment_close", NULL);
910 if (result != NULL)
911 {
912 SETPTR(filetypes[ft_id]->comment_close, result);
913 }
914
915 result = utils_get_setting(string, configh, config, "settings", "comment_single", NULL);
916 if (result != NULL)
917 {
918 SETPTR(filetypes[ft_id]->comment_single, result);
919 }
920 /* import correctly filetypes that use old-style single comments */
921 else if (EMPTY(filetypes[ft_id]->comment_close))
922 {
923 SETPTR(filetypes[ft_id]->comment_single, filetypes[ft_id]->comment_open);
924 filetypes[ft_id]->comment_open = NULL;
925 }
926
927 filetypes[ft_id]->comment_use_indent = utils_get_setting(boolean, configh, config,
928 "settings", "comment_use_indent", FALSE);
929
930 /* read context action */
931 result = utils_get_setting(string, configh, config, "settings", "context_action_cmd", NULL);
932 if (result != NULL)
933 {
934 SETPTR(filetypes[ft_id]->context_action_cmd, result);
935 }
936
937 result = utils_get_setting(string, configh, config, "settings", "tag_parser", NULL);
938 if (result != NULL)
939 {
941 if (ft->lang == TM_PARSER_NONE)
942 geany_debug("Cannot find tags parser '%s' for custom filetype '%s'.", result, ft->name);
943 g_free(result);
944 }
945
946 result = utils_get_setting(string, configh, config, "settings", "lexer_filetype", NULL);
947 if (result != NULL)
948 {
950 if (!ft->lexer_filetype)
951 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result, ft->name);
952 g_free(result);
953 }
954
955 ft->priv->symbol_list_sort_mode = utils_get_setting(integer, configh, config, "settings",
956 "symbol_list_sort_mode", SYMBOLS_SORT_USE_PREVIOUS);
957 ft->priv->xml_indent_tags = utils_get_setting(boolean, configh, config, "settings",
958 "xml_indent_tags", FALSE);
959
960 /* read indent settings */
961 load_indent_settings(ft, config, configh);
962
963 /* read build settings */
964 build_load_menu(config, GEANY_BCS_FT, (gpointer)ft);
965 build_load_menu(configh, GEANY_BCS_HOME_FT, (gpointer)ft);
966}
967
968
969static void copy_keys(GKeyFile *dest, const gchar *dest_group,
970 GKeyFile *src, const gchar *src_group)
971{
972 gchar **keys = g_key_file_get_keys(src, src_group, NULL, NULL);
973 gchar **ptr;
974
975 foreach_strv(ptr, keys)
976 {
977 gchar *key = *ptr;
978 gchar *value = g_key_file_get_value(src, src_group, key, NULL);
979
980 g_key_file_set_value(dest, dest_group, key, value);
981 g_free(value);
982 }
983 g_strfreev(keys);
984}
985
986
987static gchar *filetypes_get_filename(GeanyFiletype *ft, gboolean user)
988{
989 gchar *ext = filetypes_get_conf_extension(ft);
990 gchar *base_name = g_strconcat("filetypes.", ext, NULL);
991 gchar *file_name;
992
993 if (user)
994 file_name = g_build_filename(app->configdir, GEANY_FILEDEFS_SUBDIR, base_name, NULL);
995 else
996 file_name = g_build_filename(app->datadir, GEANY_FILEDEFS_SUBDIR, base_name, NULL);
997
998 g_free(ext);
999 g_free(base_name);
1000
1001 return file_name;
1002}
1003
1004
1005static void add_group_keys(GKeyFile *kf, const gchar *group, GeanyFiletype *ft)
1006{
1007 gchar *files[2];
1008 gboolean loaded = FALSE;
1009 guint i;
1010
1011 files[0] = filetypes_get_filename(ft, FALSE);
1012 files[1] = filetypes_get_filename(ft, TRUE);
1013
1014 for (i = 0; i < G_N_ELEMENTS(files); i++)
1015 {
1016 GKeyFile *src = g_key_file_new();
1017
1018 if (g_key_file_load_from_file(src, files[i], G_KEY_FILE_NONE, NULL))
1019 {
1020 copy_keys(kf, group, src, group);
1021 loaded = TRUE;
1022 }
1023 g_key_file_free(src);
1024 }
1025
1026 if (!loaded)
1027 geany_debug("Could not read config file %s for [%s=%s]!", files[0], group, ft->name);
1028
1029 g_free(files[0]);
1030 g_free(files[1]);
1031}
1032
1033
1034static void copy_ft_groups(GKeyFile *kf)
1035{
1036 gchar **groups = g_key_file_get_groups(kf, NULL);
1037 gchar **ptr;
1038
1040 {
1041 gchar *group = *ptr;
1042 gchar *old_group;
1043 gchar *name = strchr(*ptr, '=');
1044 GeanyFiletype *ft;
1045
1046 if (!name || !name[1]) /* no name or no parent name */
1047 continue;
1048
1049 old_group = g_strdup(group);
1050
1051 /* terminate group at '=' */
1052 *name = 0;
1053 name++;
1054
1056 if (ft)
1057 {
1058 add_group_keys(kf, group, ft);
1059 /* move old group keys (foo=bar) to proper group name (foo) */
1060 copy_keys(kf, group, kf, old_group);
1061 }
1062 g_free(old_group);
1063 }
1064 g_strfreev(groups);
1065}
1066
1067
1068/* simple wrapper function to print file errors in DEBUG mode */
1069static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags,
1070 GeanyFiletype *ft)
1071{
1072 GError *error = NULL;
1073 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1074
1075 if (error != NULL)
1076 {
1077 if (!done && !ft->priv->custom)
1078 geany_debug("Failed to open %s (%s)", file, error->message);
1079
1080 g_error_free(error);
1081 error = NULL;
1082 }
1083}
1084
1085
1086/* Load the configuration file for the associated filetype id.
1087 * This should only be called when the filetype is needed, to save loading
1088 * 20+ configuration files all at once. */
1089void filetypes_load_config(guint ft_id, gboolean reload)
1090{
1091 GKeyFile *config, *config_home;
1093 GeanyFiletype *ft;
1094
1095 g_return_if_fail(ft_id < filetypes_array->len);
1096
1097 ft = filetypes[ft_id];
1098 pft = ft->priv;
1099
1100 /* when reloading, proceed only if the settings were already loaded */
1101 if (G_UNLIKELY(reload && ! pft->keyfile_loaded))
1102 return;
1103
1104 /* when not reloading, load the settings only once */
1105 if (G_LIKELY(! reload && pft->keyfile_loaded))
1106 return;
1107 pft->keyfile_loaded = TRUE;
1108
1109 config = g_key_file_new();
1110 config_home = g_key_file_new();
1111 {
1112 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1113 gchar *f;
1114
1115 f = filetypes_get_filename(ft, FALSE);
1116 load_system_keyfile(config, f, G_KEY_FILE_KEEP_COMMENTS, ft);
1117
1118 SETPTR(f, filetypes_get_filename(ft, TRUE));
1119 g_key_file_load_from_file(config_home, f, G_KEY_FILE_KEEP_COMMENTS, NULL);
1120 g_free(f);
1121 }
1122 /* Copy keys for any groups with [group=C] from system keyfile */
1123 copy_ft_groups(config);
1124 copy_ft_groups(config_home);
1125
1126 load_settings(ft_id, config, config_home);
1127 highlighting_init_styles(ft_id, config, config_home);
1128
1129 if (ft->icon)
1130 g_object_unref(ft->icon);
1131 ft->icon = ui_get_mime_icon(ft->mime_type);
1132
1133 g_key_file_free(config);
1134 g_key_file_free(config_home);
1135}
1136
1137
1139{
1140 gchar *result;
1141
1142 if (ft->priv->custom)
1143 return g_strconcat(ft->name, ".conf", NULL);
1144
1145 /* Handle any special extensions different from lowercase filetype->name */
1146 switch (ft->id)
1147 {
1148 case GEANY_FILETYPES_CPP: result = g_strdup("cpp"); break;
1149 case GEANY_FILETYPES_CS: result = g_strdup("cs"); break;
1150 case GEANY_FILETYPES_MAKE: result = g_strdup("makefile"); break;
1151 case GEANY_FILETYPES_NONE: result = g_strdup("common"); break;
1152 /* name is Matlab/Octave */
1153 case GEANY_FILETYPES_MATLAB: result = g_strdup("matlab"); break;
1154 /* name is Objective-C, and we don't want the hyphen */
1155 case GEANY_FILETYPES_OBJECTIVEC: result = g_strdup("objectivec"); break;
1156 default:
1157 result = g_ascii_strdown(ft->name, -1);
1158 break;
1159 }
1160 return result;
1161}
1162
1163
1165{
1166 GKeyFile *config_home;
1167 gchar *fname, *data;
1168
1169 fname = filetypes_get_filename(ft, TRUE);
1170 config_home = g_key_file_new();
1171 g_key_file_load_from_file(config_home, fname, G_KEY_FILE_KEEP_COMMENTS, NULL);
1172 build_save_menu(config_home, ft, GEANY_BCS_HOME_FT);
1173 data = g_key_file_to_data(config_home, NULL, NULL);
1174 utils_write_file(fname, data);
1175 g_free(data);
1176 g_key_file_free(config_home);
1177 g_free(fname);
1178}
1179
1180
1181/* create one file filter which has each file pattern of each filetype */
1183{
1184 GtkFileFilter *new_filter;
1185 guint i, j;
1186
1187 new_filter = gtk_file_filter_new();
1188 gtk_file_filter_set_name(new_filter, _("All Source"));
1189
1190 for (i = 0; i < filetypes_array->len; i++)
1191 {
1192 if (G_UNLIKELY(i == GEANY_FILETYPES_NONE))
1193 continue;
1194
1195 for (j = 0; filetypes[i]->pattern[j]; j++)
1196 {
1197 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1198 }
1199 }
1200 return new_filter;
1201}
1202
1203
1205{
1206 GtkFileFilter *new_filter;
1207 gint i;
1208 const gchar *title;
1209
1210 g_return_val_if_fail(ft != NULL, NULL);
1211
1212 new_filter = gtk_file_filter_new();
1213 title = ft->id == GEANY_FILETYPES_NONE ? _("All files") : ft->title;
1214 gtk_file_filter_set_name(new_filter, title);
1215
1216 for (i = 0; ft->pattern[i]; i++)
1217 {
1218 gtk_file_filter_add_pattern(new_filter, ft->pattern[i]);
1219 }
1220
1221 return new_filter;
1222}
1223
1224
1225/* Indicates whether there is a tag parser for the filetype or not.
1226 * Only works for custom filetypes if the filetype settings have been loaded. */
1228{
1229 g_return_val_if_fail(ft != NULL, FALSE);
1230
1231 return ft->lang != TM_PARSER_NONE;
1232}
1233
1234
1235/** Finds a filetype pointer from its @a name field.
1236 * @param name Filetype name.
1237 * @return @transfer{none} @nullable The filetype found, or @c NULL.
1238 *
1239 * @since 0.15
1240 **/
1241GEANY_API_SYMBOL
1243{
1244 GeanyFiletype *ft;
1245
1246 g_return_val_if_fail(!EMPTY(name), NULL);
1247
1248 ft = g_hash_table_lookup(filetypes_hash, name);
1249 if (G_UNLIKELY(ft == NULL))
1250 geany_debug("Could not find filetype '%s'.", name);
1251 return ft;
1252}
1253
1254
1255static void compile_regex(GeanyFiletype *ft, gchar *regstr)
1256{
1257 GError *error = NULL;
1258 GRegex *regex = g_regex_new(regstr, 0, 0, &error);
1259
1260 if (!regex)
1261 {
1262 ui_set_statusbar(TRUE, _("Bad regex for filetype %s: %s"),
1263 filetypes_get_display_name(ft), error->message);
1264 g_error_free(error);
1265 }
1266 if (ft->priv->error_regex)
1267 g_regex_unref(ft->priv->error_regex);
1268 ft->priv->error_regex = regex;
1269}
1270
1271
1272gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message,
1273 gchar **filename, gint *line)
1274{
1275 gchar *regstr;
1276 gchar **tmp;
1277 GeanyDocument *doc;
1278 GMatchInfo *minfo;
1279 gint i, n_match_groups;
1280 gchar *first, *second;
1281
1282 if (ft == NULL)
1283 {
1284 doc = document_get_current();
1285 if (doc != NULL)
1286 ft = doc->file_type;
1287 }
1288 tmp = build_get_regex(build_info.grp, ft, NULL);
1289 if (tmp == NULL)
1290 return FALSE;
1291 regstr = *tmp;
1292
1293 *filename = NULL;
1294 *line = -1;
1295
1296 if (G_UNLIKELY(EMPTY(regstr)))
1297 return FALSE;
1298
1299 if (!ft->priv->error_regex || regstr != ft->priv->last_error_pattern)
1300 {
1301 compile_regex(ft, regstr);
1302 ft->priv->last_error_pattern = regstr;
1303 }
1304 if (!ft->priv->error_regex)
1305 return FALSE;
1306
1307 if (!g_regex_match(ft->priv->error_regex, message, 0, &minfo))
1308 {
1309 g_match_info_free(minfo);
1310 return FALSE;
1311 }
1312
1313 n_match_groups = g_match_info_get_match_count(minfo);
1314 first = second = NULL;
1315
1316 for (i = 1; i < n_match_groups; i++)
1317 {
1318 gint start_pos;
1319
1320 g_match_info_fetch_pos(minfo, i, &start_pos, NULL);
1321 if (start_pos != -1)
1322 {
1323 if (first == NULL)
1324 first = g_match_info_fetch(minfo, i);
1325 else
1326 {
1327 second = g_match_info_fetch(minfo, i);
1328 break;
1329 }
1330 }
1331 }
1332
1333 if (second)
1334 {
1335 gchar *end;
1336 glong l;
1337
1338 l = strtol(first, &end, 10);
1339 if (*end == '\0') /* first is purely decimals */
1340 {
1341 *line = l;
1342 g_free(first);
1343 *filename = second;
1344 }
1345 else
1346 {
1347 l = strtol(second, &end, 10);
1348 if (*end == '\0')
1349 {
1350 *line = l;
1351 g_free(second);
1352 *filename = first;
1353 }
1354 else
1355 {
1356 g_free(first);
1357 g_free(second);
1358 }
1359 }
1360 }
1361 else
1362 g_free(first);
1363
1364 g_match_info_free(minfo);
1365 return *filename != NULL;
1366}
1367
1368
1369#ifdef G_OS_WIN32
1370static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1371{
1372 guint i;
1373 for (i = 0; i < len; i++)
1374 {
1375 SETPTR(patterns[i], g_ascii_strdown(patterns[i], -1));
1376 }
1377}
1378#endif
1379
1380
1381static void read_extensions(GKeyFile *sysconfig, GKeyFile *userconfig)
1382{
1383 guint i;
1384 gsize len = 0;
1385
1386 /* read the keys */
1387 for (i = 0; i < filetypes_array->len; i++)
1388 {
1389 gboolean userset =
1390 g_key_file_has_key(userconfig, "Extensions", filetypes[i]->name, NULL);
1391 gchar **list = g_key_file_get_string_list(
1392 (userset) ? userconfig : sysconfig, "Extensions", filetypes[i]->name, &len, NULL);
1393
1394 filetypes[i]->priv->user_extensions = userset;
1395 g_strfreev(filetypes[i]->pattern);
1396 /* Note: we allow 'Foo=' to remove all patterns */
1397 if (!list)
1398 list = g_new0(gchar*, 1);
1399 filetypes[i]->pattern = list;
1400
1401#ifdef G_OS_WIN32
1402 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1403#endif
1404 }
1405}
1406
1407
1408static void read_group(GKeyFile *config, const gchar *group_name, GeanyFiletypeGroupID group_id)
1409{
1410 gchar **names = g_key_file_get_string_list(config, "Groups", group_name, NULL, NULL);
1411 gchar **name;
1412
1413 foreach_strv(name, names)
1414 {
1416
1417 if (ft)
1418 {
1419 ft->group = group_id;
1420 if (ft->priv->custom &&
1422 {
1424 }
1425 }
1426 else
1427 geany_debug("Filetype '%s' not found for group '%s'!", *name, group_name);
1428 }
1429 g_strfreev(names);
1430}
1431
1432
1433static void read_groups(GKeyFile *config)
1434{
1435 read_group(config, "Programming", GEANY_FILETYPE_GROUP_COMPILED);
1436 read_group(config, "Script", GEANY_FILETYPE_GROUP_SCRIPT);
1437 read_group(config, "Markup", GEANY_FILETYPE_GROUP_MARKUP);
1438 read_group(config, "Misc", GEANY_FILETYPE_GROUP_MISC);
1439 read_group(config, "None", GEANY_FILETYPE_GROUP_NONE);
1440}
1441
1442
1443static void read_filetype_config(void)
1444{
1445 gchar *sysconfigfile = g_build_filename(app->datadir, "filetype_extensions.conf", NULL);
1446 gchar *userconfigfile = g_build_filename(app->configdir, "filetype_extensions.conf", NULL);
1447 GKeyFile *sysconfig = g_key_file_new();
1448 GKeyFile *userconfig = g_key_file_new();
1449
1450 g_key_file_load_from_file(sysconfig, sysconfigfile, G_KEY_FILE_NONE, NULL);
1451 g_key_file_load_from_file(userconfig, userconfigfile, G_KEY_FILE_NONE, NULL);
1452
1453 read_extensions(sysconfig, userconfig);
1454 read_groups(sysconfig);
1455 read_groups(userconfig);
1456
1457 g_free(sysconfigfile);
1458 g_free(userconfigfile);
1459 g_key_file_free(sysconfig);
1460 g_key_file_free(userconfig);
1461}
1462
1463
1465{
1466 guint i;
1467
1469
1470 /* Redetect filetype of any documents with none set */
1472 {
1473 GeanyDocument *doc = documents[i];
1474 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1475 continue;
1477 }
1478}
1479
1480
1481/** Accessor function for @ref GeanyData::filetypes_array items.
1482 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1483 * @param idx @c filetypes_array index.
1484 * @return @transfer{none} @nullable The filetype, or @c NULL if @a idx is out of range.
1485 *
1486 * @since 0.16
1487 */
1488GEANY_API_SYMBOL
1490{
1491 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1492}
1493
1494
1496{
1497 guint i;
1498 GeanyDocument *current_doc;
1499
1500 /* reload filetype configs */
1501 for (i = 0; i < filetypes_array->len; i++)
1502 {
1503 /* filetypes_load_config() will skip not loaded filetypes */
1504 filetypes_load_config(i, TRUE);
1505 }
1506
1507 current_doc = document_get_current();
1508 if (!current_doc)
1509 return;
1510
1511 /* update document styling */
1513 {
1514 if (current_doc != documents[i])
1516 }
1517 /* process the current document at last */
1518 document_reload_config(current_doc);
1519}
1520
1521
1522/** Gets @c ft->name or a translation for filetype None.
1523 * @param ft .
1524 * @return .
1525 * @since Geany 0.20 */
1526GEANY_API_SYMBOL
1528{
1529 return ft->id == GEANY_FILETYPES_NONE ? _("None") : ft->name;
1530}
1531
1532
1533/* gets comment_open/comment_close/comment_single strings from the filetype
1534 * @param single_first: whether single comment is preferred if both available
1535 * returns true if at least comment_open is set, false otherwise */
1536gboolean filetype_get_comment_open_close(const GeanyFiletype *ft, gboolean single_first,
1537 const gchar **co, const gchar **cc)
1538{
1539 g_return_val_if_fail(ft != NULL, FALSE);
1540 g_return_val_if_fail(co != NULL, FALSE);
1541 g_return_val_if_fail(cc != NULL, FALSE);
1542
1543 if (single_first)
1544 {
1545 *co = ft->comment_single;
1546 if (!EMPTY(*co))
1547 *cc = NULL;
1548 else
1549 {
1550 *co = ft->comment_open;
1551 *cc = ft->comment_close;
1552 }
1553 }
1554 else
1555 {
1556 *co = ft->comment_open;
1557 if (!EMPTY(*co))
1558 *cc = ft->comment_close;
1559 else
1560 {
1561 *co = ft->comment_single;
1562 *cc = NULL;
1563 }
1564 }
1565
1566 return !EMPTY(*co);
1567}
1568
1569static void *copy_(void *src) { return src; }
1570static void free_(void *doc) { }
1571
1572/** @gironly
1573 * Gets the GType of GeanyFiletype
1574 *
1575 * @return the GeanyFiletype type */
1576GEANY_API_SYMBOL
1578
Contains the GeanyApp.
GeanyBuildInfo build_info
Definition: build.c:67
void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
Definition: build.c:2576
void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
Definition: build.c:2356
GeanyBuildCommand ** ptr
Definition: build.c:2679
static const gchar * groups[GEANY_GBG_COUNT]
Definition: build.c:2287
gchar ** build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
Definition: build.c:438
@ GEANY_BCS_FT
System filetype values.
Definition: build.h:46
@ GEANY_BCS_HOME_FT
Filetypes in ~/.config/geany/filedefs.
Definition: build.h:47
gboolean ignore_callback
Definition: libmain.c:87
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
void document_set_filetype(GeanyDocument *doc, GeanyFiletype *type)
Sets the filetype of the document (which controls syntax highlighting and tags)
Definition: document.c:2826
const gchar * name
Definition: document.c:3219
gboolean loaded
Definition: document.c:3221
GeanyFilePrefs file_prefs
Definition: document.c:86
void document_reload_config(GeanyDocument *doc)
Definition: document.c:2864
Document related actions: new, save, open, etc.
#define documents
Wraps GeanyData::documents_array so it can be used with C array syntax.
Definition: document.h:130
#define foreach_document(i)
Iterates all valid document indexes.
Definition: document.h:153
@ GEANY_INDENT_TYPE_BOTH
Both.
Definition: editor.h:48
@ GEANY_INDENT_TYPE_TABS
Tabs.
Definition: editor.h:47
@ GEANY_INDENT_TYPE_SPACES
Spaces.
Definition: editor.h:46
@ NONE
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
currently not working see documentation comment_single
Definition: filetypes.d:45
Filetype detection, file extensions and filetype menu items.
GeanyFiletypeID
IDs of known filetypes.
Definition: filetypes.h:45
@ GEANY_FILETYPES_MATLAB
Definition: filetypes.h:50
@ GEANY_FILETYPES_CS
Definition: filetypes.h:72
@ GEANY_FILETYPES_CPP
Definition: filetypes.h:80
@ GEANY_FILETYPES_PHP
Definition: filetypes.h:48
@ GEANY_FILETYPES_SH
Definition: filetypes.h:81
@ GEANY_FILETYPES_NONE
Definition: filetypes.h:46
@ GEANY_FILETYPES_PYTHON
Definition: filetypes.h:71
@ GEANY_FILETYPES_TCL
Definition: filetypes.h:60
@ GEANY_FILETYPES_D
Definition: filetypes.h:86
@ GEANY_MAX_BUILT_IN_FILETYPES
Definition: filetypes.h:112
@ GEANY_FILETYPES_XML
Definition: filetypes.h:61
@ GEANY_FILETYPES_CONF
Definition: filetypes.h:78
@ GEANY_FILETYPES_DOCBOOK
Definition: filetypes.h:85
@ GEANY_FILETYPES_RUST
Definition: filetypes.h:104
@ GEANY_FILETYPES_JS
Definition: filetypes.h:87
@ GEANY_FILETYPES_HTML
Definition: filetypes.h:70
@ GEANY_FILETYPES_PERL
Definition: filetypes.h:73
@ GEANY_FILETYPES_RUBY
Definition: filetypes.h:51
@ GEANY_FILETYPES_OBJECTIVEC
Definition: filetypes.h:99
@ GEANY_FILETYPES_MAKE
Definition: filetypes.h:59
#define filetypes
Wraps GeanyData::filetypes_array so it can be used with C array syntax.
Definition: filetypes.h:178
GeanyFiletypeGroupID
Definition: filetypes.h:126
@ GEANY_FILETYPE_GROUP_MISC
Definition: filetypes.h:131
@ GEANY_FILETYPE_GROUP_COUNT
Definition: filetypes.h:132
@ GEANY_FILETYPE_GROUP_MARKUP
Definition: filetypes.h:130
@ GEANY_FILETYPE_GROUP_SCRIPT
Definition: filetypes.h:129
@ GEANY_FILETYPE_GROUP_NONE
Definition: filetypes.h:127
@ GEANY_FILETYPE_GROUP_COMPILED
Definition: filetypes.h:128
#define GEANY_FILEDEFS_SUBDIR
Definition: geany.h:41
vString * line
Definition: geany_cobol.c:133
tokenInfo * list
static bool match(const unsigned char *line, const char *word)
Definition: geany_tcl.c:55
GObject * geany_object
Definition: geanyobject.c:41
void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *configh)
Definition: highlighting.c:970
Syntax highlighting for the different filetypes, using the Scintilla lexers.
GeanyApp * app
Definition: libmain.c:86
void geany_debug(gchar const *format,...)
Definition: log.c:67
#define NULL
Definition: rbtree.h:150
char * strstr(const char *str, const char *substr)
Definition: routines.c:304
gchar * sci_get_line(ScintillaObject *sci, gint line_num)
Gets line contents.
Definition: sciwrappers.c:713
Wrapper functions for the Scintilla editor widget SCI_* messages.
GtkWidget * reload
Definition: sidebar.c:58
static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
Definition: filetypes.c:807
GeanyFiletype * filetypes_detect_from_file(const gchar *utf8_filename)
Detects filetype based on a shebang line in the file or the filename extension.
Definition: filetypes.c:759
void filetypes_reload(void)
Definition: filetypes.c:1495
const GSList * filetypes_get_sorted_by_name(void)
Gets a list of filetype pointers sorted by name.
Definition: filetypes.c:234
static GeanyFiletype * filetype_new(void)
Definition: filetypes.c:195
static void filetype_add(GeanyFiletype *ft)
Definition: filetypes.c:251
static GeanyFiletype * detect_filetype_conf_file(const gchar *utf8_filename)
Definition: filetypes.c:487
#define FT_INIT(ft_id, parser_id, name, title_name, title_type, group_id)
Definition: filetypes.c:110
static void init_builtin_filetypes(void)
Definition: filetypes.c:116
void filetypes_select_radio_item(const GeanyFiletype *ft)
Definition: filetypes.c:784
GeanyFiletype * filetypes_index(gint idx)
Accessor function for GeanyData::filetypes_array items.
Definition: filetypes.c:1489
static void create_set_filetype_menu(gboolean config)
Definition: filetypes.c:424
static void load_settings(guint ft_id, GKeyFile *config, GKeyFile *configh)
Definition: filetypes.c:886
gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message, gchar **filename, gint *line)
Definition: filetypes.c:1272
G_DEFINE_BOXED_TYPE(GeanyFiletype, filetype, copy_, free_)
static gchar * filetypes_get_conf_extension(const GeanyFiletype *ft)
Definition: filetypes.c:1138
void filetypes_init_types(void)
Definition: filetypes.c:314
void filetypes_reload_extensions(void)
Definition: filetypes.c:1464
static void compile_regex(GeanyFiletype *ft, gchar *regstr)
Definition: filetypes.c:1255
static guint match_basename(const GeanyFiletype *ft, const gchar *base_filename)
Definition: filetypes.c:469
static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first,...)
Definition: filetypes.c:561
static GeanyFiletype * filetypes_detect_from_file_internal(const gchar *utf8_filename, gchar **lines)
Definition: filetypes.c:672
void filetypes_init(void)
Definition: filetypes.c:461
static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
Definition: filetypes.c:352
static void * copy_(void *src)
Definition: filetypes.c:1569
static void free_(void *doc)
Definition: filetypes.c:1570
static void copy_ft_groups(GKeyFile *kf)
Definition: filetypes.c:1034
static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags, GeanyFiletype *ft)
Definition: filetypes.c:1069
GSList * filetypes_by_title
Definition: filetypes.c:59
void filetypes_save_commands(GeanyFiletype *ft)
Definition: filetypes.c:1164
static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
Definition: filetypes.c:821
GeanyFiletype * filetypes_detect_from_extension(const gchar *utf8_filename)
Definition: filetypes.c:517
static void read_filetype_config(void)
Definition: filetypes.c:1443
#define GEANY_FILETYPE_SEARCH_LINES
Definition: filetypes.c:55
GPtrArray * filetypes_array
Definition: filetypes.c:57
static void read_groups(GKeyFile *config)
Definition: filetypes.c:1433
static void setup_config_file_menus(void)
Definition: filetypes.c:394
static GtkWidget * create_sub_menu(GtkWidget *parent, const gchar *title)
Definition: filetypes.c:410
static void load_indent_settings(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
Definition: filetypes.c:865
static void ft_init(GeanyFiletypeID ft_id, TMParserType lang, const char *name, const char *title_name, enum TitleType title_type, GeanyFiletypeGroupID group_id)
Definition: filetypes.c:98
static void copy_keys(GKeyFile *dest, const gchar *dest_group, GKeyFile *src, const gchar *src_group)
Definition: filetypes.c:969
GtkFileFilter * filetypes_create_file_filter(const GeanyFiletype *ft)
Definition: filetypes.c:1204
void filetypes_free_types(void)
Definition: filetypes.c:854
const gchar * filetypes_get_display_name(GeanyFiletype *ft)
Gets ft->name or a translation for filetype None.
Definition: filetypes.c:1527
static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2, gpointer data)
Definition: filetypes.c:213
static void on_filetype_change(GtkCheckMenuItem *menuitem, gpointer user_data)
Definition: filetypes.c:797
static void init_custom_filetypes(const gchar *path)
Definition: filetypes.c:287
static gchar * filetype_make_title(const char *name, enum TitleType type)
Definition: filetypes.c:80
GtkFileFilter * filetypes_create_file_filter_all_source(void)
Definition: filetypes.c:1182
static void add_custom_filetype(const gchar *filename)
Definition: filetypes.c:265
static GHashTable * filetypes_hash
Definition: filetypes.c:58
static void read_extensions(GKeyFile *sysconfig, GKeyFile *userconfig)
Definition: filetypes.c:1381
gboolean filetype_has_tags(GeanyFiletype *ft)
Definition: filetypes.c:1227
void filetypes_load_config(guint ft_id, gboolean reload)
Definition: filetypes.c:1089
GType filetype_get_type(void)
static void read_group(GKeyFile *config, const gchar *group_name, GeanyFiletypeGroupID group_id)
Definition: filetypes.c:1408
TitleType
Definition: filetypes.c:71
@ TITLE_FILE
Definition: filetypes.c:74
@ TITLE_SCRIPT
Definition: filetypes.c:75
@ TITLE_DOCUMENT
Definition: filetypes.c:76
@ TITLE_SOURCE_FILE
Definition: filetypes.c:73
@ TITLE_NONE
Definition: filetypes.c:72
GeanyFiletype * filetypes_lookup_by_name(const gchar *name)
Finds a filetype pointer from its name field.
Definition: filetypes.c:1242
static gchar * filetypes_get_filename(GeanyFiletype *ft, gboolean user)
Definition: filetypes.c:987
static GeanyFiletype * find_shebang(const gchar *utf8_filename, const gchar *line)
Definition: filetypes.c:592
gboolean filetype_get_comment_open_close(const GeanyFiletype *ft, gboolean single_first, const gchar **co, const gchar **cc)
Definition: filetypes.c:1536
GeanyFiletype * filetypes_detect_from_document(GeanyDocument *doc)
Definition: filetypes.c:723
static void add_group_keys(GKeyFile *kf, const gchar *group, GeanyFiletype *ft)
Definition: filetypes.c:1005
StashGroup * group
Definition: stash-example.c:1
const gchar filename[]
Definition: stash-example.c:4
if(!stash_group_load_from_file(group, filename)) g_warning(_("Could not load keyfile %s!")
gtk_container_add(GTK_CONTAINER(dialog->vbox), check_button)
long lines
Definition: stats.c:32
long files
Definition: stats.c:32
gchar * configdir
User configuration directory, usually ~/.config/geany.
Definition: app.h:45
gchar * datadir
Definition: app.h:46
Structure for representing an open tab with all its properties.
Definition: document.h:81
gchar * file_name
The UTF-8 encoded file name.
Definition: document.h:92
GeanyFiletype * file_type
The filetype for this document, it's only a reference to one of the elements of the global filetypes ...
Definition: document.h:101
gchar * real_path
The link-dereferenced, locale-encoded file name.
Definition: document.h:115
gboolean is_valid
Flag used to check if this document is valid when iterating GeanyData::documents_array.
Definition: document.h:83
GeanyEditor * editor
The editor associated with the document.
Definition: document.h:98
ScintillaObject * sci
The Scintilla editor GtkWidget.
Definition: editor.h:152
gchar * extract_filetype_regex
Definition: document.h:64
GeanyBuildCommand * ftdefcmds
GeanyBuildCommand * filecmds
GeanyBuildCommand * execcmds
Represents a filetype.
Definition: filetypes.h:144
gchar * name
Untranslated short name, such as "C", "None".
Definition: filetypes.h:152
GIcon * icon
Definition: filetypes.h:165
GeanyFiletypeID id
Index in filetypes.
Definition: filetypes.h:145
gint indent_width
Definition: filetypes.h:169
gchar * comment_close
Definition: filetypes.h:159
TMParserType lang
Definition: filetypes.h:148
GeanyFiletypeGroupID group
Definition: filetypes.h:161
gchar * context_action_cmd
Definition: filetypes.h:157
gchar * comment_open
Definition: filetypes.h:158
gchar * error_regex_string
Definition: filetypes.h:162
gchar * mime_type
Definition: filetypes.h:164
gint indent_type
Definition: filetypes.h:168
gchar * title
Shown in the file open dialog, such as "C source file".
Definition: filetypes.h:154
struct GeanyFiletypePrivate * priv
Definition: filetypes.h:171
gchar * comment_single
Definition: filetypes.h:166
struct GeanyFiletype * lexer_filetype
Definition: filetypes.h:163
gchar * extension
Default file extension for new files, or NULL.
Definition: filetypes.h:155
gchar ** pattern
Array of filename-matching wildcard strings.
Definition: filetypes.h:156
GtkWidget * window
Main window.
Definition: ui_utils.h:80
Defines internationalization macros.
#define _(String)
Definition: support.h:42
Tag-related functions.
gint TMParserType
Definition: tm_parser.h:52
TMParserType tm_source_file_get_named_lang(const gchar *name)
GIcon * ui_get_mime_icon(const gchar *mime_type)
Definition: ui_utils.c:3058
GeanyMainWidgets main_widgets
Definition: ui_utils.c:72
void ui_set_statusbar(gboolean log, const gchar *format,...)
Displays text on the statusbar.
Definition: ui_utils.c:168
void ui_add_config_file_menu_item(const gchar *real_path, const gchar *label, GtkContainer *parent)
Definition: ui_utils.c:2144
GtkWidget * ui_lookup_widget(GtkWidget *widget, const gchar *widget_name)
Returns a widget from a name in a component, usually created by Glade.
Definition: ui_utils.c:2743
UIWidgets ui_widgets
Definition: ui_utils.c:75
User Interface general utility functions.
gint utils_write_file(const gchar *filename, const gchar *text)
Writes text into a file named filename.
Definition: utils.c:209
gint utils_str_casecmp(const gchar *s1, const gchar *s2)
A replacement function for g_strncasecmp() to compare strings case-insensitive.
Definition: utils.c:506
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
gchar * utils_get_locale_from_utf8(const gchar *utf8_text)
Converts the given UTF-8 encoded string into locale encoding.
Definition: utils.c:1243
General utility functions, non-GTK related.
#define foreach_slist(node, list)
Iterates all the nodes in list.
Definition: utils.h:121
#define foreach_strv(str_ptr, strv)
Iterates a null-terminated string vector.
Definition: utils.h:152
#define SETPTR(ptr, result)
Assigns result to ptr, then frees the old value.
Definition: utils.h:50
#define foreach_dir(filename, dir)
Iterates through each unsorted filename in a GDir.
Definition: utils.h:137
#define EMPTY(ptr)
Returns TRUE if ptr is NULL or *ptr is FALSE.
Definition: utils.h:38
#define utils_strdupa(str)
Duplicates a string on the stack using g_alloca().
Definition: utils.h:72
#define utils_get_setting(type, home, sys, group, key, default_val)
Definition: utils.h:77