2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Filetype detection, file extensions and filetype menu items.
27 /* Note: we use GeanyFiletypeID for some function arguments, but GeanyFiletype is better; we should
28 * only use GeanyFiletype for API functions. */
34 #include "filetypes.h"
37 #include "callbacks.h" /* FIXME: for ignore_callback */
39 #include "filetypesprivate.h"
41 #include "geanyobject.h"
42 #include "highlighting.h"
43 #include "projectprivate.h"
44 #include "sciwrappers.h"
47 #include "tm_parser.h"
54 #include <glib/gstdio.h>
56 #define GEANY_FILETYPE_SEARCH_LINES 2 /* lines of file to search for filetype */
58 /** Dynamic array of filetype pointers
60 * List the list is dynamically expanded for custom filetypes filetypes so don't expect
61 * the list of known filetypes to be a constant.
63 * @elementtype{GeanyFiletype}
65 GPtrArray
*filetypes_array
= NULL
;
67 static GHashTable
*filetypes_hash
= NULL
; /* Hash of filetype pointers based on name keys */
69 /** List of filetype pointers sorted by name, but with @c filetypes_index(GEANY_FILETYPES_NONE)
70 * first, as this is usually treated specially.
71 * The list does not change (after filetypes have been initialized), so you can use
72 * @code g_slist_nth_data(filetypes_by_title, n) @endcode and expect the same result at different times.
73 * @see filetypes_get_sorted_by_name(). */
74 GSList
*filetypes_by_title
= NULL
;
77 static void create_radio_menu_item(GtkWidget
*menu
, GeanyFiletype
*ftype
);
79 static gchar
*filetypes_get_conf_extension(const GeanyFiletype
*ft
);
80 static void read_filetype_config(void);
81 static void create_set_filetype_menu(gboolean config
);
82 static gchar
*filetypes_get_filename(GeanyFiletype
*ft
, gboolean user
);
94 /* Save adding many translation strings if the filetype name doesn't need translating */
95 static gchar
*filetype_make_title(const char *name
, enum TitleType type
)
97 g_return_val_if_fail(name
!= NULL
, NULL
);
101 case TITLE_SOURCE_FILE
: return g_strdup_printf(_("%s source file"), name
);
102 case TITLE_FILE
: return g_strdup_printf(_("%s file"), name
);
103 case TITLE_SCRIPT
: return g_strdup_printf(_("%s script"), name
);
104 case TITLE_DOCUMENT
: return g_strdup_printf(_("%s document"), name
);
105 case TITLE_NONE
: /* fall through */
106 default: return g_strdup(name
);
111 /* name argument (ie filetype name) must not be translated as it is used for
112 * filetype lookup. Use filetypes_get_display_name() instead.*/
113 static void ft_init(GeanyFiletypeID ft_id
, TMParserType lang
, const char *name
,
114 const char *title_name
, enum TitleType title_type
,
115 GeanyFiletypeGroupID group_id
)
117 GeanyFiletype
*ft
= filetypes
[ft_id
];
119 ft
->name
= g_strdup(name
);
120 ft
->title
= filetype_make_title((title_name
!= NULL
) ? title_name
: ft
->name
, title_type
);
121 ft
->group
= group_id
;
124 /* Evil macro to save typing and make init_builtin_filetypes() more readable */
125 #define FT_INIT(ft_id, parser_id, name, title_name, title_type, group_id) \
126 ft_init(GEANY_FILETYPES_##ft_id, TM_PARSER_##parser_id, name, title_name, \
127 TITLE_##title_type, GEANY_FILETYPE_GROUP_##group_id)
130 /* Note: remember to update HACKING if this function is renamed. */
131 static void init_builtin_filetypes(void)
134 * [0] = Filetype constant (GEANY_FILETYPES_*)
135 * [1] = CTags parser (TM_PARSER_*)
136 * [2] = Non-translated filetype name (*not* label for display)
137 * [3] = Translatable human filetype title prefix or NULL to use [2]
138 * [4] = Title type (TITLE_*) constant (ex. TITLE_SOURCE_FILE is 'source file' suffix)
139 * [5] = The filetype group constant (GEANY_FILETYPE_GROUP_*)
140 * --------------------------------------------------------------------------------------------------------------------------
141 * [0] [1] [2] [3] [4] [5] */
142 FT_INIT( NONE
, NONE
, "None", _("None"), NONE
, NONE
);
143 FT_INIT( C
, C
, "C", NULL
, SOURCE_FILE
, COMPILED
);
144 FT_INIT( CPP
, CPP
, "C++", NULL
, SOURCE_FILE
, COMPILED
);
145 FT_INIT( OBJECTIVEC
, OBJC
, "Objective-C", NULL
, SOURCE_FILE
, COMPILED
);
146 FT_INIT( CS
, CSHARP
, "C#", NULL
, SOURCE_FILE
, COMPILED
);
147 FT_INIT( VALA
, VALA
, "Vala", NULL
, SOURCE_FILE
, COMPILED
);
148 FT_INIT( D
, D
, "D", NULL
, SOURCE_FILE
, COMPILED
);
149 FT_INIT( JAVA
, JAVA
, "Java", NULL
, SOURCE_FILE
, COMPILED
);
150 FT_INIT( PASCAL
, PASCAL
, "Pascal", NULL
, SOURCE_FILE
, COMPILED
);
151 FT_INIT( ASM
, ASM
, "ASM", "Assembler", SOURCE_FILE
, COMPILED
);
152 FT_INIT( BASIC
, FREEBASIC
, "FreeBasic", NULL
, SOURCE_FILE
, COMPILED
);
153 FT_INIT( FORTRAN
, FORTRAN
, "Fortran", "Fortran (F90)", SOURCE_FILE
, COMPILED
);
154 FT_INIT( F77
, F77
, "F77", "Fortran (F77)", SOURCE_FILE
, COMPILED
);
155 FT_INIT( GLSL
, GLSL
, "GLSL", NULL
, SOURCE_FILE
, COMPILED
);
156 FT_INIT( CAML
, NONE
, "CAML", "(O)Caml", SOURCE_FILE
, COMPILED
);
157 FT_INIT( PERL
, PERL
, "Perl", NULL
, SOURCE_FILE
, SCRIPT
);
158 FT_INIT( PHP
, PHP
, "PHP", NULL
, SOURCE_FILE
, SCRIPT
);
159 FT_INIT( JS
, JAVASCRIPT
, "Javascript", NULL
, SOURCE_FILE
, SCRIPT
);
160 FT_INIT( PYTHON
, PYTHON
, "Python", NULL
, SOURCE_FILE
, SCRIPT
);
161 FT_INIT( RUBY
, RUBY
, "Ruby", NULL
, SOURCE_FILE
, SCRIPT
);
162 FT_INIT( TCL
, TCL
, "Tcl", NULL
, SOURCE_FILE
, SCRIPT
);
163 FT_INIT( LUA
, LUA
, "Lua", NULL
, SOURCE_FILE
, SCRIPT
);
164 FT_INIT( FERITE
, FERITE
, "Ferite", NULL
, SOURCE_FILE
, SCRIPT
);
165 FT_INIT( HASKELL
, HASKELL
, "Haskell", NULL
, SOURCE_FILE
, COMPILED
);
166 FT_INIT( MARKDOWN
, MARKDOWN
, "Markdown", NULL
, SOURCE_FILE
, MARKUP
);
167 FT_INIT( TXT2TAGS
, TXT2TAGS
, "Txt2tags", NULL
, SOURCE_FILE
, MARKUP
);
168 FT_INIT( ABC
, ABC
, "Abc", NULL
, FILE, MISC
);
169 FT_INIT( SH
, SH
, "Sh", _("Shell"), SCRIPT
, SCRIPT
);
170 FT_INIT( MAKE
, MAKEFILE
, "Make", _("Makefile"), NONE
, SCRIPT
);
171 FT_INIT( XML
, NONE
, "XML", NULL
, DOCUMENT
, MARKUP
);
172 FT_INIT( DOCBOOK
, DOCBOOK
, "Docbook", NULL
, DOCUMENT
, MARKUP
);
173 FT_INIT( HTML
, HTML
, "HTML", NULL
, DOCUMENT
, MARKUP
);
174 FT_INIT( CSS
, CSS
, "CSS", _("Cascading Stylesheet"), NONE
, MARKUP
); /* not really markup but fit quite well to HTML */
175 FT_INIT( SQL
, SQL
, "SQL", NULL
, FILE, MISC
);
176 FT_INIT( COBOL
, COBOL
, "COBOL", NULL
, SOURCE_FILE
, COMPILED
);
177 FT_INIT( LATEX
, LATEX
, "LaTeX", NULL
, SOURCE_FILE
, MARKUP
);
178 FT_INIT( VHDL
, VHDL
, "VHDL", NULL
, SOURCE_FILE
, COMPILED
);
179 FT_INIT( VERILOG
, VERILOG
, "Verilog", NULL
, SOURCE_FILE
, COMPILED
);
180 FT_INIT( DIFF
, DIFF
, "Diff", NULL
, FILE, MISC
);
181 FT_INIT( LISP
, NONE
, "Lisp", NULL
, SOURCE_FILE
, SCRIPT
);
182 FT_INIT( ERLANG
, ERLANG
, "Erlang", NULL
, SOURCE_FILE
, COMPILED
);
183 FT_INIT( CONF
, CONF
, "Conf", _("Config"), FILE, MISC
);
184 FT_INIT( PO
, NONE
, "Po", _("Gettext translation"), FILE, MISC
);
185 FT_INIT( HAXE
, HAXE
, "Haxe", NULL
, SOURCE_FILE
, COMPILED
);
186 FT_INIT( AS
, ACTIONSCRIPT
, "ActionScript", NULL
, SOURCE_FILE
, SCRIPT
);
187 FT_INIT( R
, R
, "R", NULL
, SOURCE_FILE
, SCRIPT
);
188 FT_INIT( REST
, REST
, "reStructuredText", NULL
, SOURCE_FILE
, MARKUP
);
189 FT_INIT( MATLAB
, MATLAB
, "Matlab/Octave", NULL
, SOURCE_FILE
, SCRIPT
);
190 FT_INIT( YAML
, NONE
, "YAML", NULL
, FILE, MISC
);
191 FT_INIT( CMAKE
, NONE
, "CMake", NULL
, SOURCE_FILE
, SCRIPT
);
192 FT_INIT( NSIS
, NSIS
, "NSIS", NULL
, SOURCE_FILE
, SCRIPT
);
193 FT_INIT( ADA
, NONE
, "Ada", NULL
, SOURCE_FILE
, COMPILED
);
194 FT_INIT( FORTH
, NONE
, "Forth", NULL
, SOURCE_FILE
, SCRIPT
);
195 FT_INIT( ASCIIDOC
, ASCIIDOC
, "Asciidoc", NULL
, SOURCE_FILE
, MARKUP
);
196 FT_INIT( ABAQUS
, ABAQUS
, "Abaqus", NULL
, SOURCE_FILE
, SCRIPT
);
197 FT_INIT( BATCH
, NONE
, "Batch", NULL
, SCRIPT
, SCRIPT
);
198 FT_INIT( POWERSHELL
, POWERSHELL
, "PowerShell", NULL
, SOURCE_FILE
, SCRIPT
);
199 FT_INIT( RUST
, RUST
, "Rust", NULL
, SOURCE_FILE
, COMPILED
);
200 FT_INIT( COFFEESCRIPT
, NONE
, "CoffeeScript", NULL
, SOURCE_FILE
, SCRIPT
);
201 FT_INIT( GO
, GO
, "Go", NULL
, SOURCE_FILE
, COMPILED
);
202 FT_INIT( ZEPHIR
, ZEPHIR
, "Zephir", NULL
, SOURCE_FILE
, COMPILED
);
206 /* initialize fields. */
207 static GeanyFiletype
*filetype_new(void)
209 GeanyFiletype
*ft
= g_new0(GeanyFiletype
, 1);
211 ft
->group
= GEANY_FILETYPE_GROUP_NONE
;
212 ft
->lang
= TM_PARSER_NONE
; /* assume no tagmanager parser */
213 /* pattern must not be null */
214 ft
->pattern
= g_new0(gchar
*, 1);
215 ft
->indent_width
= -1;
216 ft
->indent_type
= -1;
218 ft
->priv
= g_new0(GeanyFiletypePrivate
, 1);
219 ft
->priv
->project_list_entry
= -1; /* no entry */
225 static gint
cmp_filetype(gconstpointer pft1
, gconstpointer pft2
, gpointer data
)
227 gboolean by_name
= GPOINTER_TO_INT(data
);
228 const GeanyFiletype
*ft1
= pft1
, *ft2
= pft2
;
230 if (G_UNLIKELY(ft1
->id
== GEANY_FILETYPES_NONE
))
232 if (G_UNLIKELY(ft2
->id
== GEANY_FILETYPES_NONE
))
236 utils_str_casecmp(ft1
->name
, ft2
->name
) :
237 utils_str_casecmp(ft1
->title
, ft2
->title
);
241 /** Gets a list of filetype pointers sorted by name.
242 * The list does not change on subsequent calls.
243 * @return @elementtype{GeanyFiletype} @transfer{none} The list - do not free.
244 * @see filetypes_by_title. */
246 const GSList
*filetypes_get_sorted_by_name(void)
248 static GSList
*list
= NULL
;
250 g_return_val_if_fail(filetypes_by_title
, NULL
);
254 list
= g_slist_copy(filetypes_by_title
);
255 list
= g_slist_sort_with_data(list
, cmp_filetype
, GINT_TO_POINTER(TRUE
));
261 /* Add a filetype pointer to the lists of available filetypes,
262 * and set the filetype::id field. */
263 static void filetype_add(GeanyFiletype
*ft
)
265 g_return_if_fail(ft
);
266 g_return_if_fail(ft
->name
);
268 ft
->id
= filetypes_array
->len
; /* len will be the index for filetype_array */
269 g_ptr_array_add(filetypes_array
, ft
);
270 g_hash_table_insert(filetypes_hash
, ft
->name
, ft
);
272 /* list will be sorted later */
273 filetypes_by_title
= g_slist_prepend(filetypes_by_title
, ft
);
277 static void add_custom_filetype(const gchar
*filename
)
279 gchar
*fn
= utils_strdupa(strstr(filename
, ".") + 1);
280 gchar
*dot
= g_strrstr(fn
, ".conf");
283 g_return_if_fail(dot
);
287 if (g_hash_table_lookup(filetypes_hash
, fn
))
291 ft
->name
= g_strdup(fn
);
292 ft
->title
= filetype_make_title(ft
->name
, TITLE_FILE
);
293 ft
->priv
->custom
= TRUE
;
295 geany_debug("Added filetype %s (%d).", ft
->name
, ft
->id
);
299 static void init_custom_filetypes(const gchar
*path
)
302 const gchar
*filename
;
304 g_return_if_fail(path
);
306 dir
= g_dir_open(path
, 0, NULL
);
310 foreach_dir(filename
, dir
)
312 const gchar prefix
[] = "filetypes.";
314 if (g_str_has_prefix(filename
, prefix
) &&
315 g_str_has_suffix(filename
+ strlen(prefix
), ".conf"))
317 add_custom_filetype(filename
);
324 /* Create the filetypes array and fill it with the known filetypes.
325 * Warning: GTK isn't necessarily initialized yet. */
326 void filetypes_init_types(void)
328 GeanyFiletypeID ft_id
;
331 g_return_if_fail(filetypes_array
== NULL
);
332 g_return_if_fail(filetypes_hash
== NULL
);
334 filetypes_array
= g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES
);
335 filetypes_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
337 /* Create built-in filetypes */
338 for (ft_id
= 0; ft_id
< GEANY_MAX_BUILT_IN_FILETYPES
; ft_id
++)
340 filetypes
[ft_id
] = filetype_new();
342 init_builtin_filetypes();
344 /* Add built-in filetypes to the hash now the name fields are set */
345 for (ft_id
= 0; ft_id
< GEANY_MAX_BUILT_IN_FILETYPES
; ft_id
++)
347 filetype_add(filetypes
[ft_id
]);
349 f
= g_build_filename(app
->datadir
, GEANY_FILEDEFS_SUBDIR
, NULL
);
350 init_custom_filetypes(f
);
352 f
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, NULL
);
353 init_custom_filetypes(f
);
356 /* sort last instead of on insertion to prevent exponential time */
357 filetypes_by_title
= g_slist_sort_with_data(filetypes_by_title
,
358 cmp_filetype
, GINT_TO_POINTER(FALSE
));
360 read_filetype_config();
364 static void on_document_save(G_GNUC_UNUSED GObject
*object
, GeanyDocument
*doc
)
368 g_return_if_fail(!EMPTY(doc
->real_path
));
370 f
= g_build_filename(app
->configdir
, "filetype_extensions.conf", NULL
);
371 if (utils_str_equal(doc
->real_path
, f
))
372 filetypes_reload_extensions();
375 basename
= g_path_get_basename(doc
->real_path
);
376 if (g_str_has_prefix(basename
, "filetypes."))
380 for (i
= 0; i
< filetypes_array
->len
; i
++)
382 GeanyFiletype
*ft
= filetypes
[i
];
384 f
= filetypes_get_filename(ft
, TRUE
);
385 if (utils_str_equal(doc
->real_path
, f
))
389 /* Note: we don't reload other filetypes, even though the named styles may have changed.
390 * The user can do this manually with 'Tools->Reload Configuration' */
391 filetypes_load_config(i
, TRUE
);
394 document_reload_config(documents
[j
]);
406 static void setup_config_file_menus(void)
410 f
= g_build_filename(app
->configdir
, "filetype_extensions.conf", NULL
);
411 ui_add_config_file_menu_item(f
, NULL
, NULL
);
412 SETPTR(f
, g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, "filetypes.common", NULL
));
413 ui_add_config_file_menu_item(f
, NULL
, NULL
);
416 create_set_filetype_menu(TRUE
);
418 g_signal_connect(geany_object
, "document-save", G_CALLBACK(on_document_save
), NULL
);
422 static GtkWidget
*create_sub_menu(GtkWidget
*parent
, const gchar
*title
)
424 GtkWidget
*menu
, *item
;
426 menu
= gtk_menu_new();
427 item
= gtk_menu_item_new_with_mnemonic((title
));
428 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), menu
);
429 gtk_container_add(GTK_CONTAINER(parent
), item
);
430 gtk_widget_show(item
);
436 static void create_set_filetype_menu(gboolean config
)
438 GtkWidget
*group_menus
[GEANY_FILETYPE_GROUP_COUNT
] = {NULL
};
442 menu
= config
? ui_widgets
.config_files_filetype_menu
:
443 ui_lookup_widget(main_widgets
.window
, "set_filetype1_menu");
445 group_menus
[GEANY_FILETYPE_GROUP_COMPILED
] = create_sub_menu(menu
, _("_Programming Languages"));
446 group_menus
[GEANY_FILETYPE_GROUP_SCRIPT
] = create_sub_menu(menu
, _("_Scripting Languages"));
447 group_menus
[GEANY_FILETYPE_GROUP_MARKUP
] = create_sub_menu(menu
, _("_Markup Languages"));
448 group_menus
[GEANY_FILETYPE_GROUP_MISC
] = create_sub_menu(menu
, _("M_iscellaneous"));
450 /* Append all filetypes to the menu */
451 foreach_slist(node
, filetypes_by_title
)
453 GeanyFiletype
*ft
= node
->data
;
454 GtkWidget
*parent
= (ft
->group
!= GEANY_FILETYPE_GROUP_NONE
) ? group_menus
[ft
->group
] : menu
;
456 /* we already have filetypes.common config entry */
457 if (config
&& ft
->id
== GEANY_FILETYPES_NONE
)
462 gchar
*filename
= filetypes_get_filename(ft
, TRUE
);
464 ui_add_config_file_menu_item(filename
, NULL
, GTK_CONTAINER(parent
));
468 create_radio_menu_item(parent
, ft
);
473 void filetypes_init(void)
475 filetypes_init_types();
476 create_set_filetype_menu(FALSE
);
477 setup_config_file_menus();
481 /* Find a filetype that predicate returns TRUE for, otherwise return NULL. */
482 GeanyFiletype
*filetypes_find(GCompareFunc predicate
, gpointer user_data
)
486 for (i
= 0; i
< filetypes_array
->len
; i
++)
488 GeanyFiletype
*ft
= filetypes
[i
];
490 if (predicate(ft
, user_data
))
497 static gboolean
match_basename(gconstpointer pft
, gconstpointer user_data
)
499 const GeanyFiletype
*ft
= pft
;
500 const gchar
*base_filename
= user_data
;
502 gboolean ret
= FALSE
;
504 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
507 for (j
= 0; ft
->pattern
[j
] != NULL
; j
++)
509 GPatternSpec
*pattern
= g_pattern_spec_new(ft
->pattern
[j
]);
511 if (g_pattern_match_string(pattern
, base_filename
))
514 g_pattern_spec_free(pattern
);
517 g_pattern_spec_free(pattern
);
523 static GeanyFiletype
*check_builtin_filenames(const gchar
*utf8_filename
)
527 gboolean found
= FALSE
;
530 /* use lower case basename */
531 lfn
= g_utf8_strdown(utf8_filename
, -1);
533 lfn
= g_strdup(utf8_filename
);
535 SETPTR(lfn
, utils_get_locale_from_utf8(lfn
));
537 path
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, "filetypes.", NULL
);
538 if (g_str_has_prefix(lfn
, path
))
541 SETPTR(path
, g_build_filename(app
->datadir
, GEANY_FILEDEFS_SUBDIR
, "filetypes.", NULL
));
542 if (g_str_has_prefix(lfn
, path
))
547 return found
? filetypes
[GEANY_FILETYPES_CONF
] : NULL
;
551 /* Detect filetype only based on the filename extension.
552 * utf8_filename can include the full path. */
553 GeanyFiletype
*filetypes_detect_from_extension(const gchar
*utf8_filename
)
555 gchar
*base_filename
;
558 ft
= check_builtin_filenames(utf8_filename
);
562 /* to match against the basename of the file (because of Makefile*) */
563 base_filename
= g_path_get_basename(utf8_filename
);
565 /* use lower case basename */
566 SETPTR(base_filename
, g_utf8_strdown(base_filename
, -1));
569 ft
= filetypes_find(match_basename
, base_filename
);
571 ft
= filetypes
[GEANY_FILETYPES_NONE
];
573 g_free(base_filename
);
578 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
580 * The detected filetype of the file is checked against every id in the passed list and if
581 * there is a match, TRUE is returned. */
582 static gboolean
shebang_find_and_match_filetype(const gchar
*utf8_filename
, gint first
, ...)
584 GeanyFiletype
*ft
= NULL
;
586 gboolean result
= FALSE
;
589 ft
= filetypes_detect_from_extension(utf8_filename
);
590 if (ft
== NULL
|| ft
->id
>= filetypes_array
->len
)
593 va_start(args
, first
);
600 if (ft
->id
== (guint
) test
)
605 test
= va_arg(args
, gint
);
613 static GeanyFiletype
*find_shebang(const gchar
*utf8_filename
, const gchar
*line
)
615 GeanyFiletype
*ft
= NULL
;
617 if (strlen(line
) > 2 && line
[0] == '#' && line
[1] == '!')
619 static const struct {
621 GeanyFiletypeID filetype
;
622 } intepreter_map
[] = {
623 { "sh", GEANY_FILETYPES_SH
},
624 { "bash", GEANY_FILETYPES_SH
},
625 { "dash", GEANY_FILETYPES_SH
},
626 { "perl", GEANY_FILETYPES_PERL
},
627 { "python", GEANY_FILETYPES_PYTHON
},
628 { "php", GEANY_FILETYPES_PHP
},
629 { "ruby", GEANY_FILETYPES_RUBY
},
630 { "tcl", GEANY_FILETYPES_TCL
},
631 { "make", GEANY_FILETYPES_MAKE
},
632 { "zsh", GEANY_FILETYPES_SH
},
633 { "ksh", GEANY_FILETYPES_SH
},
634 { "mksh", GEANY_FILETYPES_SH
},
635 { "csh", GEANY_FILETYPES_SH
},
636 { "tcsh", GEANY_FILETYPES_SH
},
637 { "ash", GEANY_FILETYPES_SH
},
638 { "dmd", GEANY_FILETYPES_D
},
639 { "wish", GEANY_FILETYPES_TCL
},
640 { "node", GEANY_FILETYPES_JS
},
641 { "rust", GEANY_FILETYPES_RUST
}
643 gchar
*tmp
= g_path_get_basename(line
+ 2);
644 gchar
*basename_interpreter
= tmp
;
647 if (g_str_has_prefix(tmp
, "env "))
648 { /* skip "env" and read the following interpreter */
649 basename_interpreter
+= 4;
652 for (i
= 0; ! ft
&& i
< G_N_ELEMENTS(intepreter_map
); i
++)
654 if (g_str_has_prefix(basename_interpreter
, intepreter_map
[i
].name
))
655 ft
= filetypes
[intepreter_map
[i
].filetype
];
659 /* detect HTML files */
660 if (g_str_has_prefix(line
, "<!DOCTYPE html") || g_str_has_prefix(line
, "<html"))
662 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
663 * extension and use the detected filetype, else assume HTML */
664 if (! shebang_find_and_match_filetype(utf8_filename
,
665 GEANY_FILETYPES_PERL
, GEANY_FILETYPES_PHP
, GEANY_FILETYPES_PYTHON
, -1))
667 ft
= filetypes
[GEANY_FILETYPES_HTML
];
670 /* detect XML files */
671 else if (utf8_filename
&& g_str_has_prefix(line
, "<?xml"))
673 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
674 * extension and use the detected filetype, else assume XML */
675 if (! shebang_find_and_match_filetype(utf8_filename
,
676 GEANY_FILETYPES_HTML
, GEANY_FILETYPES_DOCBOOK
,
677 /* Perl, Python and PHP only to be safe */
678 GEANY_FILETYPES_PERL
, GEANY_FILETYPES_PHP
, GEANY_FILETYPES_PYTHON
, -1))
680 ft
= filetypes
[GEANY_FILETYPES_XML
];
683 else if (g_str_has_prefix(line
, "<?php"))
685 ft
= filetypes
[GEANY_FILETYPES_PHP
];
691 /* Detect the filetype checking for a shebang, then filename extension.
692 * @lines: an strv of the lines to scan (must containing at least one line) */
693 static GeanyFiletype
*filetypes_detect_from_file_internal(const gchar
*utf8_filename
,
700 GError
*regex_error
= NULL
;
702 /* try to find a shebang and if found use it prior to the filename extension
703 * also checks for <?xml */
704 ft
= find_shebang(utf8_filename
, lines
[0]);
708 /* try to extract the filetype using a regex capture */
709 ft_regex
= g_regex_new(file_prefs
.extract_filetype_regex
,
710 G_REGEX_RAW
| G_REGEX_MULTILINE
, 0, ®ex_error
);
711 if (ft_regex
!= NULL
)
713 for (i
= 0; ft
== NULL
&& lines
[i
] != NULL
; i
++)
715 if (g_regex_match(ft_regex
, lines
[i
], 0, &match
))
717 gchar
*capture
= g_match_info_fetch(match
, 1);
720 ft
= filetypes_lookup_by_name(capture
);
724 g_match_info_free(match
);
726 g_regex_unref(ft_regex
);
728 else if (regex_error
!= NULL
)
730 geany_debug("Filetype extract regex ignored: %s", regex_error
->message
);
731 g_error_free(regex_error
);
736 if (utf8_filename
== NULL
)
737 return filetypes
[GEANY_FILETYPES_NONE
];
739 return filetypes_detect_from_extension(utf8_filename
);
743 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
744 GeanyFiletype
*filetypes_detect_from_document(GeanyDocument
*doc
)
747 gchar
*lines
[GEANY_FILETYPE_SEARCH_LINES
+ 1];
750 g_return_val_if_fail(doc
== NULL
|| doc
->is_valid
, filetypes
[GEANY_FILETYPES_NONE
]);
753 return filetypes
[GEANY_FILETYPES_NONE
];
755 for (i
= 0; i
< GEANY_FILETYPE_SEARCH_LINES
; ++i
)
757 lines
[i
] = sci_get_line(doc
->editor
->sci
, i
);
760 ft
= filetypes_detect_from_file_internal(doc
->file_name
, lines
);
761 for (i
= 0; i
< GEANY_FILETYPE_SEARCH_LINES
; ++i
)
770 /* Currently only used by external plugins (e.g. geanyprj). */
772 * Detects filetype based on a shebang line in the file or the filename extension.
774 * @param utf8_filename The filename in UTF-8 encoding.
776 * @return @transfer{none} The detected filetype for @a utf8_filename or
777 * @c filetypes[GEANY_FILETYPES_NONE] if it could not be detected.
780 GeanyFiletype
*filetypes_detect_from_file(const gchar
*utf8_filename
)
785 gchar
*locale_name
= utils_get_locale_from_utf8(utf8_filename
);
787 f
= g_fopen(locale_name
, "r");
791 if (fgets(line
, sizeof(line
), f
) != NULL
)
796 return filetypes_detect_from_file_internal(utf8_filename
, lines
);
800 return filetypes_detect_from_extension(utf8_filename
);
805 void filetypes_select_radio_item(const GeanyFiletype
*ft
)
807 /* ignore_callback has to be set by the caller */
808 g_return_if_fail(ignore_callback
);
811 ft
= filetypes
[GEANY_FILETYPES_NONE
];
813 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft
->priv
->menu_item
), TRUE
);
818 on_filetype_change(GtkCheckMenuItem
*menuitem
, gpointer user_data
)
820 GeanyDocument
*doc
= document_get_current();
821 if (ignore_callback
|| doc
== NULL
|| ! gtk_check_menu_item_get_active(menuitem
))
824 document_set_filetype(doc
, (GeanyFiletype
*)user_data
);
828 static void create_radio_menu_item(GtkWidget
*menu
, GeanyFiletype
*ftype
)
830 static GSList
*group
= NULL
;
833 tmp
= gtk_radio_menu_item_new_with_label(group
, ftype
->title
);
834 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp
));
835 ftype
->priv
->menu_item
= tmp
;
836 gtk_widget_show(tmp
);
837 gtk_container_add(GTK_CONTAINER(menu
), tmp
);
838 g_signal_connect(tmp
, "activate", G_CALLBACK(on_filetype_change
), (gpointer
) ftype
);
842 static void filetype_free(gpointer data
, G_GNUC_UNUSED gpointer user_data
)
844 GeanyFiletype
*ft
= data
;
846 g_return_if_fail(ft
!= NULL
);
850 g_free(ft
->extension
);
851 g_free(ft
->mime_type
);
852 g_free(ft
->comment_open
);
853 g_free(ft
->comment_close
);
854 g_free(ft
->comment_single
);
855 g_free(ft
->context_action_cmd
);
856 g_free(ft
->priv
->filecmds
);
857 g_free(ft
->priv
->ftdefcmds
);
858 g_free(ft
->priv
->execcmds
);
859 g_free(ft
->error_regex_string
);
861 g_object_unref(ft
->icon
);
862 g_strfreev(ft
->pattern
);
864 if (ft
->priv
->error_regex
)
865 g_regex_unref(ft
->priv
->error_regex
);
866 g_slist_foreach(ft
->priv
->tag_files
, (GFunc
) g_free
, NULL
);
867 g_slist_free(ft
->priv
->tag_files
);
874 /* frees the array and all related pointers */
875 void filetypes_free_types(void)
877 g_return_if_fail(filetypes_array
!= NULL
);
878 g_return_if_fail(filetypes_hash
!= NULL
);
880 g_ptr_array_foreach(filetypes_array
, filetype_free
, NULL
);
881 g_ptr_array_free(filetypes_array
, TRUE
);
882 g_hash_table_destroy(filetypes_hash
);
886 static void load_indent_settings(GeanyFiletype
*ft
, GKeyFile
*config
, GKeyFile
*configh
)
888 ft
->indent_width
= utils_get_setting(integer
, configh
, config
, "indentation", "width", -1);
889 ft
->indent_type
= utils_get_setting(integer
, configh
, config
, "indentation", "type", -1);
890 /* check whether the indent type is OK */
891 switch (ft
->indent_type
)
893 case GEANY_INDENT_TYPE_TABS
:
894 case GEANY_INDENT_TYPE_SPACES
:
895 case GEANY_INDENT_TYPE_BOTH
:
900 g_warning("Invalid indent type %d in file type %s", ft
->indent_type
, ft
->name
);
901 ft
->indent_type
= -1;
907 static void load_settings(guint ft_id
, GKeyFile
*config
, GKeyFile
*configh
)
909 GeanyFiletype
*ft
= filetypes
[ft_id
];
912 /* default extension */
913 result
= utils_get_setting(string
, configh
, config
, "settings", "extension", NULL
);
916 SETPTR(filetypes
[ft_id
]->extension
, result
);
920 result
= utils_get_setting(string
, configh
, config
, "settings", "mime_type", "text/plain");
921 SETPTR(filetypes
[ft_id
]->mime_type
, result
);
923 /* read comment notes */
924 result
= utils_get_setting(string
, configh
, config
, "settings", "comment_open", NULL
);
927 SETPTR(filetypes
[ft_id
]->comment_open
, result
);
930 result
= utils_get_setting(string
, configh
, config
, "settings", "comment_close", NULL
);
933 SETPTR(filetypes
[ft_id
]->comment_close
, result
);
936 result
= utils_get_setting(string
, configh
, config
, "settings", "comment_single", NULL
);
939 SETPTR(filetypes
[ft_id
]->comment_single
, result
);
941 /* import correctly filetypes that use old-style single comments */
942 else if (EMPTY(filetypes
[ft_id
]->comment_close
))
944 SETPTR(filetypes
[ft_id
]->comment_single
, filetypes
[ft_id
]->comment_open
);
945 filetypes
[ft_id
]->comment_open
= NULL
;
948 filetypes
[ft_id
]->comment_use_indent
= utils_get_setting(boolean
, configh
, config
,
949 "settings", "comment_use_indent", FALSE
);
951 /* read context action */
952 result
= utils_get_setting(string
, configh
, config
, "settings", "context_action_cmd", NULL
);
955 SETPTR(filetypes
[ft_id
]->context_action_cmd
, result
);
958 result
= utils_get_setting(string
, configh
, config
, "settings", "tag_parser", NULL
);
961 ft
->lang
= tm_source_file_get_named_lang(result
);
962 if (ft
->lang
== TM_PARSER_NONE
)
963 geany_debug("Cannot find tags parser '%s' for custom filetype '%s'.", result
, ft
->name
);
967 result
= utils_get_setting(string
, configh
, config
, "settings", "lexer_filetype", NULL
);
970 ft
->lexer_filetype
= filetypes_lookup_by_name(result
);
971 if (!ft
->lexer_filetype
)
972 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result
, ft
->name
);
976 ft
->priv
->symbol_list_sort_mode
= utils_get_setting(integer
, configh
, config
, "settings",
977 "symbol_list_sort_mode", SYMBOLS_SORT_USE_PREVIOUS
);
978 ft
->priv
->xml_indent_tags
= utils_get_setting(boolean
, configh
, config
, "settings",
979 "xml_indent_tags", FALSE
);
981 /* read indent settings */
982 load_indent_settings(ft
, config
, configh
);
984 /* read build settings */
985 build_load_menu(config
, GEANY_BCS_FT
, (gpointer
)ft
);
986 build_load_menu(configh
, GEANY_BCS_HOME_FT
, (gpointer
)ft
);
990 static void copy_keys(GKeyFile
*dest
, const gchar
*dest_group
,
991 GKeyFile
*src
, const gchar
*src_group
)
993 gchar
**keys
= g_key_file_get_keys(src
, src_group
, NULL
, NULL
);
996 foreach_strv(ptr
, keys
)
999 gchar
*value
= g_key_file_get_value(src
, src_group
, key
, NULL
);
1001 g_key_file_set_value(dest
, dest_group
, key
, value
);
1008 static gchar
*filetypes_get_filename(GeanyFiletype
*ft
, gboolean user
)
1010 gchar
*ext
= filetypes_get_conf_extension(ft
);
1011 gchar
*base_name
= g_strconcat("filetypes.", ext
, NULL
);
1015 file_name
= g_build_filename(app
->configdir
, GEANY_FILEDEFS_SUBDIR
, base_name
, NULL
);
1017 file_name
= g_build_filename(app
->datadir
, GEANY_FILEDEFS_SUBDIR
, base_name
, NULL
);
1026 static void add_group_keys(GKeyFile
*kf
, const gchar
*group
, GeanyFiletype
*ft
)
1029 gboolean loaded
= FALSE
;
1032 files
[0] = filetypes_get_filename(ft
, FALSE
);
1033 files
[1] = filetypes_get_filename(ft
, TRUE
);
1035 for (i
= 0; i
< G_N_ELEMENTS(files
); i
++)
1037 GKeyFile
*src
= g_key_file_new();
1039 if (g_key_file_load_from_file(src
, files
[i
], G_KEY_FILE_NONE
, NULL
))
1041 copy_keys(kf
, group
, src
, group
);
1044 g_key_file_free(src
);
1048 geany_debug("Could not read config file %s for [%s=%s]!", files
[0], group
, ft
->name
);
1055 static void copy_ft_groups(GKeyFile
*kf
)
1057 gchar
**groups
= g_key_file_get_groups(kf
, NULL
);
1060 foreach_strv(ptr
, groups
)
1062 gchar
*group
= *ptr
;
1064 gchar
*name
= strchr(*ptr
, '=');
1067 if (!name
|| !name
[1]) /* no name or no parent name */
1070 old_group
= g_strdup(group
);
1072 /* terminate group at '=' */
1076 ft
= filetypes_lookup_by_name(name
);
1079 add_group_keys(kf
, group
, ft
);
1080 /* move old group keys (foo=bar) to proper group name (foo) */
1081 copy_keys(kf
, group
, kf
, old_group
);
1089 /* simple wrapper function to print file errors in DEBUG mode */
1090 static void load_system_keyfile(GKeyFile
*key_file
, const gchar
*file
, GKeyFileFlags flags
,
1093 GError
*error
= NULL
;
1094 gboolean done
= g_key_file_load_from_file(key_file
, file
, flags
, &error
);
1098 if (!done
&& !ft
->priv
->custom
)
1099 geany_debug("Failed to open %s (%s)", file
, error
->message
);
1101 g_error_free(error
);
1107 /* Load the configuration file for the associated filetype id.
1108 * This should only be called when the filetype is needed, to save loading
1109 * 20+ configuration files all at once. */
1110 void filetypes_load_config(guint ft_id
, gboolean reload
)
1112 GKeyFile
*config
, *config_home
;
1113 GeanyFiletypePrivate
*pft
;
1116 g_return_if_fail(ft_id
< filetypes_array
->len
);
1118 ft
= filetypes
[ft_id
];
1121 /* when reloading, proceed only if the settings were already loaded */
1122 if (G_UNLIKELY(reload
&& ! pft
->keyfile_loaded
))
1125 /* when not reloading, load the settings only once */
1126 if (G_LIKELY(! reload
&& pft
->keyfile_loaded
))
1128 pft
->keyfile_loaded
= TRUE
;
1130 config
= g_key_file_new();
1131 config_home
= g_key_file_new();
1133 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1136 f
= filetypes_get_filename(ft
, FALSE
);
1137 load_system_keyfile(config
, f
, G_KEY_FILE_KEEP_COMMENTS
, ft
);
1139 SETPTR(f
, filetypes_get_filename(ft
, TRUE
));
1140 g_key_file_load_from_file(config_home
, f
, G_KEY_FILE_KEEP_COMMENTS
, NULL
);
1143 /* Copy keys for any groups with [group=C] from system keyfile */
1144 copy_ft_groups(config
);
1145 copy_ft_groups(config_home
);
1147 load_settings(ft_id
, config
, config_home
);
1148 highlighting_init_styles(ft_id
, config
, config_home
);
1151 g_object_unref(ft
->icon
);
1152 ft
->icon
= ui_get_mime_icon(ft
->mime_type
);
1154 g_key_file_free(config
);
1155 g_key_file_free(config_home
);
1159 static gchar
*filetypes_get_conf_extension(const GeanyFiletype
*ft
)
1163 if (ft
->priv
->custom
)
1164 return g_strconcat(ft
->name
, ".conf", NULL
);
1166 /* Handle any special extensions different from lowercase filetype->name */
1169 case GEANY_FILETYPES_CPP
: result
= g_strdup("cpp"); break;
1170 case GEANY_FILETYPES_CS
: result
= g_strdup("cs"); break;
1171 case GEANY_FILETYPES_MAKE
: result
= g_strdup("makefile"); break;
1172 case GEANY_FILETYPES_NONE
: result
= g_strdup("common"); break;
1173 /* name is Matlab/Octave */
1174 case GEANY_FILETYPES_MATLAB
: result
= g_strdup("matlab"); break;
1175 /* name is Objective-C, and we don't want the hyphen */
1176 case GEANY_FILETYPES_OBJECTIVEC
: result
= g_strdup("objectivec"); break;
1178 result
= g_ascii_strdown(ft
->name
, -1);
1185 void filetypes_save_commands(GeanyFiletype
*ft
)
1187 GKeyFile
*config_home
;
1188 gchar
*fname
, *data
;
1190 fname
= filetypes_get_filename(ft
, TRUE
);
1191 config_home
= g_key_file_new();
1192 g_key_file_load_from_file(config_home
, fname
, G_KEY_FILE_KEEP_COMMENTS
, NULL
);
1193 build_save_menu(config_home
, ft
, GEANY_BCS_HOME_FT
);
1194 data
= g_key_file_to_data(config_home
, NULL
, NULL
);
1195 utils_write_file(fname
, data
);
1197 g_key_file_free(config_home
);
1202 /* create one file filter which has each file pattern of each filetype */
1203 GtkFileFilter
*filetypes_create_file_filter_all_source(void)
1205 GtkFileFilter
*new_filter
;
1208 new_filter
= gtk_file_filter_new();
1209 gtk_file_filter_set_name(new_filter
, _("All Source"));
1211 for (i
= 0; i
< filetypes_array
->len
; i
++)
1213 if (G_UNLIKELY(i
== GEANY_FILETYPES_NONE
))
1216 for (j
= 0; filetypes
[i
]->pattern
[j
]; j
++)
1218 gtk_file_filter_add_pattern(new_filter
, filetypes
[i
]->pattern
[j
]);
1225 GtkFileFilter
*filetypes_create_file_filter(const GeanyFiletype
*ft
)
1227 GtkFileFilter
*new_filter
;
1231 g_return_val_if_fail(ft
!= NULL
, NULL
);
1233 new_filter
= gtk_file_filter_new();
1234 title
= ft
->id
== GEANY_FILETYPES_NONE
? _("All files") : ft
->title
;
1235 gtk_file_filter_set_name(new_filter
, title
);
1237 for (i
= 0; ft
->pattern
[i
]; i
++)
1239 gtk_file_filter_add_pattern(new_filter
, ft
->pattern
[i
]);
1246 /* Indicates whether there is a tag parser for the filetype or not.
1247 * Only works for custom filetypes if the filetype settings have been loaded. */
1248 gboolean
filetype_has_tags(GeanyFiletype
*ft
)
1250 g_return_val_if_fail(ft
!= NULL
, FALSE
);
1252 return ft
->lang
!= TM_PARSER_NONE
;
1256 /** Finds a filetype pointer from its @a name field.
1257 * @param name Filetype name.
1258 * @return @transfer{none} @nullable The filetype found, or @c NULL.
1263 GeanyFiletype
*filetypes_lookup_by_name(const gchar
*name
)
1267 g_return_val_if_fail(!EMPTY(name
), NULL
);
1269 ft
= g_hash_table_lookup(filetypes_hash
, name
);
1270 if (G_UNLIKELY(ft
== NULL
))
1271 geany_debug("Could not find filetype '%s'.", name
);
1276 static void compile_regex(GeanyFiletype
*ft
, gchar
*regstr
)
1278 GError
*error
= NULL
;
1279 GRegex
*regex
= g_regex_new(regstr
, 0, 0, &error
);
1283 ui_set_statusbar(TRUE
, _("Bad regex for filetype %s: %s"),
1284 filetypes_get_display_name(ft
), error
->message
);
1285 g_error_free(error
);
1287 if (ft
->priv
->error_regex
)
1288 g_regex_unref(ft
->priv
->error_regex
);
1289 ft
->priv
->error_regex
= regex
;
1293 gboolean
filetypes_parse_error_message(GeanyFiletype
*ft
, const gchar
*message
,
1294 gchar
**filename
, gint
*line
)
1300 gint i
, n_match_groups
;
1301 gchar
*first
, *second
;
1305 doc
= document_get_current();
1307 ft
= doc
->file_type
;
1309 tmp
= build_get_regex(build_info
.grp
, ft
, NULL
);
1317 if (G_UNLIKELY(EMPTY(regstr
)))
1320 if (!ft
->priv
->error_regex
|| regstr
!= ft
->priv
->last_error_pattern
)
1322 compile_regex(ft
, regstr
);
1323 ft
->priv
->last_error_pattern
= regstr
;
1325 if (!ft
->priv
->error_regex
)
1328 if (!g_regex_match(ft
->priv
->error_regex
, message
, 0, &minfo
))
1330 g_match_info_free(minfo
);
1334 n_match_groups
= g_match_info_get_match_count(minfo
);
1335 first
= second
= NULL
;
1337 for (i
= 1; i
< n_match_groups
; i
++)
1341 g_match_info_fetch_pos(minfo
, i
, &start_pos
, NULL
);
1342 if (start_pos
!= -1)
1345 first
= g_match_info_fetch(minfo
, i
);
1348 second
= g_match_info_fetch(minfo
, i
);
1359 l
= strtol(first
, &end
, 10);
1360 if (*end
== '\0') /* first is purely decimals */
1368 l
= strtol(second
, &end
, 10);
1385 g_match_info_free(minfo
);
1386 return *filename
!= NULL
;
1391 static void convert_filetype_extensions_to_lower_case(gchar
**patterns
, gsize len
)
1394 for (i
= 0; i
< len
; i
++)
1396 SETPTR(patterns
[i
], g_ascii_strdown(patterns
[i
], -1));
1402 static void read_extensions(GKeyFile
*sysconfig
, GKeyFile
*userconfig
)
1408 for (i
= 0; i
< filetypes_array
->len
; i
++)
1411 g_key_file_has_key(userconfig
, "Extensions", filetypes
[i
]->name
, NULL
);
1412 gchar
**list
= g_key_file_get_string_list(
1413 (userset
) ? userconfig
: sysconfig
, "Extensions", filetypes
[i
]->name
, &len
, NULL
);
1415 g_strfreev(filetypes
[i
]->pattern
);
1416 /* Note: we allow 'Foo=' to remove all patterns */
1418 list
= g_new0(gchar
*, 1);
1419 filetypes
[i
]->pattern
= list
;
1422 convert_filetype_extensions_to_lower_case(filetypes
[i
]->pattern
, len
);
1428 static void read_group(GKeyFile
*config
, const gchar
*group_name
, GeanyFiletypeGroupID group_id
)
1430 gchar
**names
= g_key_file_get_string_list(config
, "Groups", group_name
, NULL
, NULL
);
1433 foreach_strv(name
, names
)
1435 GeanyFiletype
*ft
= filetypes_lookup_by_name(*name
);
1439 ft
->group
= group_id
;
1440 if (ft
->priv
->custom
&&
1441 (group_id
== GEANY_FILETYPE_GROUP_COMPILED
|| group_id
== GEANY_FILETYPE_GROUP_SCRIPT
))
1443 SETPTR(ft
->title
, filetype_make_title(ft
->name
, TITLE_SOURCE_FILE
));
1447 geany_debug("Filetype '%s' not found for group '%s'!", *name
, group_name
);
1453 static void read_groups(GKeyFile
*config
)
1455 read_group(config
, "Programming", GEANY_FILETYPE_GROUP_COMPILED
);
1456 read_group(config
, "Script", GEANY_FILETYPE_GROUP_SCRIPT
);
1457 read_group(config
, "Markup", GEANY_FILETYPE_GROUP_MARKUP
);
1458 read_group(config
, "Misc", GEANY_FILETYPE_GROUP_MISC
);
1459 read_group(config
, "None", GEANY_FILETYPE_GROUP_NONE
);
1463 static void read_filetype_config(void)
1465 gchar
*sysconfigfile
= g_build_filename(app
->datadir
, "filetype_extensions.conf", NULL
);
1466 gchar
*userconfigfile
= g_build_filename(app
->configdir
, "filetype_extensions.conf", NULL
);
1467 GKeyFile
*sysconfig
= g_key_file_new();
1468 GKeyFile
*userconfig
= g_key_file_new();
1470 g_key_file_load_from_file(sysconfig
, sysconfigfile
, G_KEY_FILE_NONE
, NULL
);
1471 g_key_file_load_from_file(userconfig
, userconfigfile
, G_KEY_FILE_NONE
, NULL
);
1473 read_extensions(sysconfig
, userconfig
);
1474 read_groups(sysconfig
);
1475 read_groups(userconfig
);
1477 g_free(sysconfigfile
);
1478 g_free(userconfigfile
);
1479 g_key_file_free(sysconfig
);
1480 g_key_file_free(userconfig
);
1484 void filetypes_reload_extensions(void)
1488 read_filetype_config();
1490 /* Redetect filetype of any documents with none set */
1493 GeanyDocument
*doc
= documents
[i
];
1494 if (doc
->file_type
->id
!= GEANY_FILETYPES_NONE
)
1496 document_set_filetype(doc
, filetypes_detect_from_document(doc
));
1501 /** Accessor function for @ref GeanyData::filetypes_array items.
1502 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1503 * @param idx @c filetypes_array index.
1504 * @return @transfer{none} @nullable The filetype, or @c NULL if @a idx is out of range.
1509 GeanyFiletype
*filetypes_index(gint idx
)
1511 return (idx
>= 0 && idx
< (gint
) filetypes_array
->len
) ? filetypes
[idx
] : NULL
;
1515 void filetypes_reload(void)
1518 GeanyDocument
*current_doc
;
1520 /* reload filetype configs */
1521 for (i
= 0; i
< filetypes_array
->len
; i
++)
1523 /* filetypes_load_config() will skip not loaded filetypes */
1524 filetypes_load_config(i
, TRUE
);
1527 current_doc
= document_get_current();
1531 /* update document styling */
1534 if (current_doc
!= documents
[i
])
1535 document_reload_config(documents
[i
]);
1537 /* process the current document at last */
1538 document_reload_config(current_doc
);
1542 /** Gets @c ft->name or a translation for filetype None.
1545 * @since Geany 0.20 */
1547 const gchar
*filetypes_get_display_name(GeanyFiletype
*ft
)
1549 return ft
->id
== GEANY_FILETYPES_NONE
? _("None") : ft
->name
;
1553 /* gets comment_open/comment_close/comment_single strings from the filetype
1554 * @param single_first: whether single comment is preferred if both available
1555 * returns true if at least comment_open is set, false otherwise */
1556 gboolean
filetype_get_comment_open_close(const GeanyFiletype
*ft
, gboolean single_first
,
1557 const gchar
**co
, const gchar
**cc
)
1559 g_return_val_if_fail(ft
!= NULL
, FALSE
);
1560 g_return_val_if_fail(co
!= NULL
, FALSE
);
1561 g_return_val_if_fail(cc
!= NULL
, FALSE
);
1565 *co
= ft
->comment_single
;
1570 *co
= ft
->comment_open
;
1571 *cc
= ft
->comment_close
;
1576 *co
= ft
->comment_open
;
1578 *cc
= ft
->comment_close
;
1581 *co
= ft
->comment_single
;