From 18f9e1d1be413227c4989da1b5fc5d2e5db3ee79 Mon Sep 17 00:00:00 2001 From: xiota Date: Tue, 9 Nov 2021 15:48:07 -0800 Subject: [PATCH] Project Organizer: Set header filetype to match source filetype When opened, the filetype of header files is changed to match the source filetype. This is particularly useful for *.h files, which may be C or C++. Out of project files are matched with files in the same directory. Common code between swap header/source and change header filetypes are combined. When a project is not loaded, default file extensions are used. --- projectorganizer/src/prjorg-main.c | 2 + projectorganizer/src/prjorg-menu.c | 134 +----------------- projectorganizer/src/prjorg-project.c | 8 +- projectorganizer/src/prjorg-project.h | 5 + projectorganizer/src/prjorg-utils.c | 194 ++++++++++++++++++++++++++ projectorganizer/src/prjorg-utils.h | 4 + 6 files changed, 213 insertions(+), 134 deletions(-) diff --git a/projectorganizer/src/prjorg-main.c b/projectorganizer/src/prjorg-main.c index a32c4f784..5937685ce 100644 --- a/projectorganizer/src/prjorg-main.c +++ b/projectorganizer/src/prjorg-main.c @@ -28,6 +28,7 @@ #include "prjorg-project.h" #include "prjorg-sidebar.h" #include "prjorg-menu.h" +#include "prjorg-utils.h" GeanyPlugin *geany_plugin; @@ -54,6 +55,7 @@ static void on_doc_open(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GeanyDocument prjorg_project_remove_single_tm_file(doc->file_name); prjorg_sidebar_update(FALSE); + set_header_filetype(doc); } diff --git a/projectorganizer/src/prjorg-menu.c b/projectorganizer/src/prjorg-menu.c index e7e0c1400..2849b0a83 100644 --- a/projectorganizer/src/prjorg-menu.c +++ b/projectorganizer/src/prjorg-menu.c @@ -50,137 +50,12 @@ enum static GtkWidget *s_fif_item, *s_ff_item, *s_ft_item, *s_shs_item, *s_sep_item, *s_context_osf_item, *s_context_sep_item; -static gboolean try_swap_header_source(gchar *utf8_file_name, gboolean is_header, GSList *file_list, GSList *header_patterns, GSList *source_patterns) -{ - gchar *name_pattern; - GSList *elem = NULL; - GPatternSpec *pattern; - gboolean found = FALSE; - - name_pattern = g_path_get_basename(utf8_file_name); - SETPTR(name_pattern, utils_remove_ext_from_filename(name_pattern)); - SETPTR(name_pattern, g_strconcat(name_pattern, ".*", NULL)); - pattern = g_pattern_spec_new(name_pattern); - g_free(name_pattern); - - foreach_slist (elem, file_list) - { - gchar *full_name = elem->data; - gchar *base_name = g_path_get_basename(full_name); - - if (g_pattern_match_string(pattern, base_name) && - prjorg_project_is_in_project(full_name)) - { - if ((is_header && patterns_match(source_patterns, base_name)) || - (!is_header && patterns_match(header_patterns, base_name))) - { - open_file(full_name); - found = TRUE; - g_free(base_name); - break; - } - } - g_free(base_name); - } - - g_pattern_spec_free(pattern); - return found; -} - - static void on_swap_header_source(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data) { - GSList *header_patterns, *source_patterns; - GeanyDocument *doc; - gboolean known_type = TRUE; - gboolean is_header; - gchar *doc_basename; - doc = document_get_current(); - - if (!prj_org || !geany_data->app->project || !doc || !doc->file_name) - return; - - header_patterns = get_precompiled_patterns(prj_org->header_patterns); - source_patterns = get_precompiled_patterns(prj_org->source_patterns); - - doc_basename = g_path_get_basename(doc->file_name); - - if (patterns_match(header_patterns, doc_basename)) - is_header = TRUE; - else if (patterns_match(source_patterns, doc_basename)) - is_header = FALSE; - else - known_type = FALSE; - - if (known_type) - { - gboolean swapped; - GSList *elem = NULL, *list = NULL; - guint i = 0; - - foreach_document(i) - { - gchar *filename; - - filename = document_index(i)->file_name; - if (prjorg_project_is_in_project(filename)) - list = g_slist_prepend(list, filename); - } - swapped = try_swap_header_source(doc->file_name, is_header, list, header_patterns, source_patterns); - g_slist_free(list); - list = NULL; - - if (!swapped) - { - gchar *utf8_doc_dir; - gchar *locale_doc_dir; - - utf8_doc_dir = g_path_get_dirname(doc->file_name); - locale_doc_dir = utils_get_locale_from_utf8(utf8_doc_dir); - - list = utils_get_file_list(locale_doc_dir, NULL, NULL); - foreach_list (elem, list) - { - gchar *full_name; - - full_name = g_build_filename(locale_doc_dir, elem->data, NULL); - SETPTR(full_name, utils_get_utf8_from_locale(full_name)); - SETPTR(elem->data, full_name); - } - swapped = try_swap_header_source(doc->file_name, is_header, list, header_patterns, source_patterns); - g_slist_foreach(list, (GFunc) g_free, NULL); - g_slist_free(list); - g_free(utf8_doc_dir); - g_free(locale_doc_dir); - list = NULL; - } - - if (!swapped) - { - foreach_slist(elem, prj_org->roots) - { - GHashTableIter iter; - gpointer key, value; - PrjOrgRoot *root = elem->data; - - list = NULL; - g_hash_table_iter_init(&iter, root->file_table); - while (g_hash_table_iter_next(&iter, &key, &value)) - list = g_slist_prepend(list, key); - swapped = try_swap_header_source(doc->file_name, is_header, list, header_patterns, source_patterns); - g_slist_free(list); - if (swapped) - break; - } - } - } - - g_free(doc_basename); - - g_slist_foreach(header_patterns, (GFunc) g_pattern_spec_free, NULL); - g_slist_free(header_patterns); - g_slist_foreach(source_patterns, (GFunc) g_pattern_spec_free, NULL); - g_slist_free(source_patterns); + GeanyDocument *doc = document_get_current(); + gchar *full_name = find_header_source(doc); + open_file(full_name); + g_free(full_name); } @@ -424,7 +299,6 @@ void prjorg_menu_init(void) void prjorg_menu_activate_menu_items(gboolean activate) { gtk_widget_set_sensitive(s_context_osf_item, activate); - gtk_widget_set_sensitive(s_shs_item, activate); gtk_widget_set_sensitive(s_ff_item, activate); gtk_widget_set_sensitive(s_ft_item, activate); gtk_widget_set_sensitive(s_fif_item, activate); diff --git a/projectorganizer/src/prjorg-project.c b/projectorganizer/src/prjorg-project.c index e9e7e580b..94a37abf2 100644 --- a/projectorganizer/src/prjorg-project.c +++ b/projectorganizer/src/prjorg-project.c @@ -500,16 +500,16 @@ void prjorg_project_open(GKeyFile * key_file) source_patterns = g_key_file_get_string_list(key_file, "prjorg", "source_patterns", NULL, NULL); if (!source_patterns) - source_patterns = g_strsplit("*.c *.C *.cpp *.cxx *.c++ *.cc *.m", " ", -1); + source_patterns = g_strsplit(PRJORG_PATTERNS_SOURCE, " ", -1); header_patterns = g_key_file_get_string_list(key_file, "prjorg", "header_patterns", NULL, NULL); if (!header_patterns) - header_patterns = g_strsplit("*.h *.H *.hpp *.hxx *.h++ *.hh", " ", -1); + header_patterns = g_strsplit(PRJORG_PATTERNS_HEADER, " ", -1); ignored_dirs_patterns = g_key_file_get_string_list(key_file, "prjorg", "ignored_dirs_patterns", NULL, NULL); if (!ignored_dirs_patterns) - ignored_dirs_patterns = g_strsplit(".* CVS", " ", -1); + ignored_dirs_patterns = g_strsplit(PRJORG_PATTERNS_IGNORED_DIRS, " ", -1); ignored_file_patterns = g_key_file_get_string_list(key_file, "prjorg", "ignored_file_patterns", NULL, NULL); if (!ignored_file_patterns) - ignored_file_patterns = g_strsplit("*.o *.obj *.a *.lib *.so *.dll *.lo *.la *.class *.jar *.pyc *.mo *.gmo", " ", -1); + ignored_file_patterns = g_strsplit(PRJORG_PATTERNS_IGNORED_FILE, " ", -1); generate_tag_prefs = utils_get_setting_integer(key_file, "prjorg", "generate_tag_prefs", PrjOrgTagAuto); show_empty_dirs = utils_get_setting_boolean(key_file, "prjorg", "show_empty_dirs", TRUE); diff --git a/projectorganizer/src/prjorg-project.h b/projectorganizer/src/prjorg-project.h index d44e1e651..f168f9b5b 100644 --- a/projectorganizer/src/prjorg-project.h +++ b/projectorganizer/src/prjorg-project.h @@ -19,6 +19,11 @@ #ifndef __PRJORG_PROJECT_H__ #define __PRJORG_PROJECT_H__ +#define PRJORG_PATTERNS_SOURCE "*.c *.C *.cpp *.cxx *.c++ *.cc *.m" +#define PRJORG_PATTERNS_HEADER "*.h *.H *.hpp *.hxx *.h++ *.hh" +#define PRJORG_PATTERNS_IGNORED_DIRS ".* CVS" +#define PRJORG_PATTERNS_IGNORED_FILE "*.o *.obj *.a *.lib *.so *.dll *.lo *.la *.class *.jar *.pyc *.mo *.gmo" + typedef struct { gchar *base_dir; diff --git a/projectorganizer/src/prjorg-utils.c b/projectorganizer/src/prjorg-utils.c index f460923eb..97a38a80a 100644 --- a/projectorganizer/src/prjorg-utils.c +++ b/projectorganizer/src/prjorg-utils.c @@ -26,6 +26,7 @@ #include +#include "prjorg-project.h" #include "prjorg-utils.h" extern GeanyData *geany_data; @@ -241,3 +242,196 @@ GtkWidget *menu_item_new(const gchar *icon_name, const gchar *label) gtk_widget_show(item); return item; } + +gchar *try_find_header_source(gchar *utf8_file_name, gboolean is_header, GSList *file_list, GSList *header_patterns, GSList *source_patterns) +{ + gchar *full_name; + gchar *name_pattern; + GSList *elem = NULL; + GPatternSpec *pattern; + gboolean found = FALSE; + + name_pattern = g_path_get_basename(utf8_file_name); + SETPTR(name_pattern, utils_remove_ext_from_filename(name_pattern)); + SETPTR(name_pattern, g_strconcat(name_pattern, ".*", NULL)); + pattern = g_pattern_spec_new(name_pattern); + g_free(name_pattern); + + foreach_slist (elem, file_list) + { + full_name = elem->data; + gchar *base_name = g_path_get_basename(full_name); + + if (g_pattern_match_string(pattern, base_name)) + { + if ((is_header && patterns_match(source_patterns, base_name)) || + (!is_header && patterns_match(header_patterns, base_name))) + { + g_free(base_name); + found = TRUE; + break; + } + } + g_free(base_name); + } + + g_pattern_spec_free(pattern); + + if(found) + return g_strdup(full_name); + + return NULL; +} + +gchar *find_header_source(GeanyDocument *doc) +{ + GSList *header_patterns, *source_patterns; + gboolean known_type = TRUE; + gboolean is_header; + gchar *found_name = NULL; + + if (!doc || !doc->file_name) + return NULL; + + if (prj_org) + { + header_patterns = get_precompiled_patterns(prj_org->header_patterns); + source_patterns = get_precompiled_patterns(prj_org->source_patterns); + } + else + { + // use default header/source patterns if project isn't open + gchar **headers, **source; + headers = g_strsplit(PRJORG_PATTERNS_HEADER, " ", -1); + source = g_strsplit(PRJORG_PATTERNS_SOURCE, " ", -1); + + header_patterns = get_precompiled_patterns(headers); + source_patterns = get_precompiled_patterns(source); + + g_strfreev(headers); + g_strfreev(source); + } + + // check if file matches patterns + { + gchar *doc_basename; + doc_basename = g_path_get_basename(doc->file_name); + + if (patterns_match(header_patterns, doc_basename)) + is_header = TRUE; + else if (patterns_match(source_patterns, doc_basename)) + is_header = FALSE; + else + known_type = FALSE; + + g_free(doc_basename); + } + + if (known_type) + { + GSList *elem = NULL, *list = NULL; + guint i = 0; + + /* check open documents */ + foreach_document(i) + { + gchar *filename; + filename = document_index(i)->file_name; + list = g_slist_prepend(list, filename); + } + found_name = try_find_header_source(doc->file_name, is_header, list, header_patterns, source_patterns); + g_slist_free(list); + list = NULL; + + /* check document directory */ + if (!found_name) + { + gchar *utf8_doc_dir; + gchar *locale_doc_dir; + + utf8_doc_dir = g_path_get_dirname(doc->file_name); + locale_doc_dir = utils_get_locale_from_utf8(utf8_doc_dir); + + list = utils_get_file_list(locale_doc_dir, NULL, NULL); + foreach_list (elem, list) + { + gchar *full_name; + full_name = g_build_filename(locale_doc_dir, elem->data, NULL); + SETPTR(full_name, utils_get_utf8_from_locale(full_name)); + SETPTR(elem->data, full_name); + } + found_name = try_find_header_source(doc->file_name, is_header, list, header_patterns, source_patterns); + g_slist_foreach(list, (GFunc) g_free, NULL); + g_slist_free(list); + g_free(utf8_doc_dir); + g_free(locale_doc_dir); + list = NULL; + } + + /* check project files */ + if (!found_name && prj_org) + { + foreach_slist(elem, prj_org->roots) + { + GHashTableIter iter; + gpointer key, value; + PrjOrgRoot *root = elem->data; + + list = NULL; + g_hash_table_iter_init(&iter, root->file_table); + while (g_hash_table_iter_next(&iter, &key, &value)) + list = g_slist_prepend(list, key); + found_name = try_find_header_source(doc->file_name, is_header, list, header_patterns, source_patterns); + g_slist_free(list); + if (found_name) + break; + } + } + } + + g_slist_foreach(header_patterns, (GFunc) g_pattern_spec_free, NULL); + g_slist_free(header_patterns); + g_slist_foreach(source_patterns, (GFunc) g_pattern_spec_free, NULL); + g_slist_free(source_patterns); + + return found_name; +} + + +/* sets filetype of header to match source + * changes filetype only if document is a header */ +void set_header_filetype(GeanyDocument * doc) +{ + gchar *doc_basename, *full_name; + gboolean is_header; + GSList * header_patterns; + + if (!doc || !doc->file_name) + return; + + if (prj_org) + { + header_patterns = get_precompiled_patterns(prj_org->header_patterns); + } + else + { + /* use default patterns when project isn't open */ + gchar ** patterns = g_strsplit(PRJORG_PATTERNS_HEADER, " ", -1); + header_patterns = get_precompiled_patterns(patterns); + g_strfreev(patterns); + } + + doc_basename = g_path_get_basename(doc->file_name); + is_header = patterns_match(header_patterns, doc_basename); + full_name = is_header ? find_header_source(doc) : NULL; + + if (full_name) + { + GeanyFiletype * source_filetype = filetypes_detect_from_file(full_name); + document_set_filetype(doc, source_filetype); + g_free(full_name); + } + + g_free(doc_basename); + g_slist_free(header_patterns); +} diff --git a/projectorganizer/src/prjorg-utils.h b/projectorganizer/src/prjorg-utils.h index cd9299cdd..c3719a92c 100644 --- a/projectorganizer/src/prjorg-utils.h +++ b/projectorganizer/src/prjorg-utils.h @@ -37,4 +37,8 @@ gchar *get_project_base_path(void); GtkWidget *menu_item_new(const gchar *icon_name, const gchar *label); +gchar *try_find_header_source(gchar *utf8_file_name, gboolean is_header, GSList *file_list, GSList *header_patterns, GSList *source_patterns); +gchar *find_header_source(GeanyDocument *doc); +void set_header_filetype(GeanyDocument * doc); + #endif