Branch: refs/heads/master Author: Colomban Wendling ban@herbesfolles.org Committer: Colomban Wendling ban@herbesfolles.org Date: Tue, 04 Dec 2018 21:43:18 UTC Commit: 9cc18f9143a50bb7402bbbd499ec7081d009f931 https://github.com/geany/geany/commit/9cc18f9143a50bb7402bbbd499ec7081d009f9...
Log Message: ----------- Merge pull request #1445 from kugel-/fix-1069
Improve goto-symbols popup
Modified Paths: -------------- doc/Doxyfile.in scripts/gen-api-gtkdoc.py src/symbols.c src/utils.c src/utils.h
Modified: doc/Doxyfile.in 2 lines changed, 2 insertions(+), 0 deletions(-) =================================================================== @@ -262,6 +262,8 @@ ALIASES += "optional=\noop \xmlonly <simplesect kind="geany:optio ALIASES += "cb=\noop \xmlonly <simplesect kind="geany:scope">notified</simplesect>\endxmlonly" ALIASES += "cbdata=\noop \xmlonly <simplesect kind="geany:closure"></simplesect>\endxmlonly" ALIASES += "cbfree=\noop \xmlonly <simplesect kind="geany:destroy"></simplesect>\endxmlonly" +ALIASES += "array=\noop \xmlonly <simplesect kind="geany:array"></simplesect>\endxmlonly" +ALIASES += "array{1}=\noop \xmlonly <simplesect kind="geany:array">\1</simplesect>\endxmlonly"
# This tag can be used to specify a number of word-keyword mappings (TCL only).
Modified: scripts/gen-api-gtkdoc.py 7 lines changed, 5 insertions(+), 2 deletions(-) =================================================================== @@ -75,11 +75,14 @@ def cb(self, type, str): "geany:closure", "geany:destroy"): self.annot.append(type.split(":")[1]) - elif type in ("geany:transfer", + elif type in ("geany:array", + "geany:transfer", "geany:element-type", "geany:scope"): type = type.split(":")[1] - self.annot.append("%s %s" % (type, str)) + if len(str): + str = " " + str + self.annot.append("%s%s" % (type, str)) elif (type == "see"): return "See " + str elif type in ("a", "c") and str in ("NULL", "TRUE", "FALSE"):
Modified: src/symbols.c 13 lines changed, 11 insertions(+), 2 deletions(-) =================================================================== @@ -1933,15 +1933,23 @@ static void show_goto_popup(GeanyDocument *doc, GPtrArray *tags, gboolean have_b GdkEventButton *button_event = NULL; TMTag *tmtag; guint i; - + gchar **short_names, **file_names; menu = gtk_menu_new();
+ /* If popup would show multiple files present a smart file list that allows + * to easily distinguish the files while avoiding the file paths in their entirety */ + file_names = g_new(gchar *, tags->len); + foreach_ptr_array(tmtag, i, tags) + file_names[i] = tmtag->file->file_name; + short_names = utils_strv_shorten_file_list(file_names, tags->len); + g_free(file_names); + foreach_ptr_array(tmtag, i, tags) { GtkWidget *item; GtkWidget *label; GtkWidget *image; - gchar *fname = g_path_get_basename(tmtag->file->file_name); + gchar *fname = short_names[i]; gchar *text;
if (! first && have_best) @@ -1964,6 +1972,7 @@ static void show_goto_popup(GeanyDocument *doc, GPtrArray *tags, gboolean have_b g_free(text); g_free(fname); } + g_free(short_names);
gtk_widget_show_all(menu);
Modified: src/utils.c 201 lines changed, 201 insertions(+), 0 deletions(-) =================================================================== @@ -2044,6 +2044,207 @@ gchar **utils_strv_join(gchar **first, gchar **second) return strv; }
+/* * Returns the common prefix in a list of strings. + * + * The size of the list may be given explicitely, but defaults to @c g_strv_length(strv). + * + * @param strv The list of strings to process. + * @param strv_len The number of strings contained in @a strv. Can be -1 if it's terminated by @c NULL. + * + * @return The common prefix that is part of all strings (maybe empty), or NULL if an empty list + * was passed in. + */ +static gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len) +{ + gsize num; + + if (!NZV(strv)) + return NULL; + + num = (strv_len == -1) ? g_strv_length(strv) : (gsize) strv_len; + + for (gsize i = 0; strv[0][i]; i++) + { + for (gsize j = 1; j < num; j++) + { + if (strv[j][i] != strv[0][i]) + { + /* return prefix on first mismatch */ + return g_strndup(strv[0], i); + } + } + } + + return g_strdup(strv[0]); +} + + +/* * Returns the longest common substring in a list of strings. + * + * The size of the list may be given explicitely, but defaults to @c g_strv_length(strv). + * + * @param strv The list of strings to process. + * @param strv_len The number of strings contained in @a strv. Can be -1 if it's terminated by @c NULL. + * + * @return The common prefix that is part of all strings. + */ +static gchar *utils_strv_find_lcs(gchar **strv, gssize strv_len) +{ + gchar *first, *_sub, *sub; + gsize num; + gsize n_chars; + gsize len; + gsize max = 0; + char *lcs; + gsize found; + + num = (strv_len == -1) ? g_strv_length(strv) : (gsize) strv_len; + if (num < 1) + return NULL; + + first = strv[0]; + len = strlen(first); + + /* sub is the working area where substrings from first are copied to */ + sub = g_malloc(len+1); + lcs = g_strdup(""); + foreach_str(_sub, first) + { + gsize chars_left = len - (_sub - first); + /* No point in continuing if the remainder is too short */ + if (max > chars_left) + break; + for (n_chars = 1; n_chars <= chars_left; n_chars++) + { + g_strlcpy(sub, _sub, n_chars+1); + found = 1; + for (gsize i = 1; i < num; i++) + { + if (strstr(strv[i], sub) == NULL) + break; + found++; + } + if (found == num && n_chars > max) + { + max = n_chars; + SETPTR(lcs, g_strdup(sub)); + } + } + } + g_free(sub); + + return lcs; +} + + +/** Transform file names in a list to be shorter. + * + * This function takes a list of file names (probably with absolute paths), and + * transforms the paths such that they are short but still unique. This is intended + * for dialogs which present the file list to the user, where the base name may result + * in duplicates (showing the full path might be inappropriate). + * + * The algorthm strips the common prefix (e-g. the user's home directory) and + * replaces the longest common substring with an ellipsis ("..."). + * + * @param file_names @array{length=file_names_len} The list of strings to process. + * @param file_names_len The number of strings contained in @a file_names. Can be -1 if it's + * terminated by @c NULL. + * @return @transfer{full} A newly-allocated array of transformed paths strings, terminated by + @c NULL. Use @c g_strfreev() to free it. + * + * @since 1.34 (API 239) + */ +GEANY_API_SYMBOL +gchar **utils_strv_shorten_file_list(gchar **file_names, gssize file_names_len) +{ + gsize num; + gsize i; + gchar *prefix, *substring, *lcs; + gchar *start, *end; + gchar **names; + gsize prefix_len, lcs_len; + + /* We don't dereference file_names if file_names_len == 0. */ + g_return_val_if_fail(file_names_len == 0 || file_names != NULL, NULL); + + /* The return value shall have exactly the same size as the input. If the input is a + * GStrv (last element is NULL), the output will follow suit. */ + num = (file_names_len == -1) ? g_strv_length(file_names) : (gsize) file_names_len; + /* Always include a terminating NULL, enables easy freeing with g_strfreev() */ + names = g_new0(gchar *, num + 1); + + prefix = utils_strv_find_common_prefix(file_names, num); + /* First: determine the common prefix, that will be stripped. + * Don't strip single-letter prefixes, such as '/' */ + prefix_len = 0; + if (NZV(prefix) && prefix[1]) + { + /* Only strip directory components, include trailing '/' */ + start = strrchr(prefix, G_DIR_SEPARATOR); + if (start) + prefix_len = start - prefix + 1; + } + + /* Second: determine the longest common substring (lcs), that will be ellipsized. Look + * only at the directory parts since we don't want the file name to be detected as the lcs. + * Also, the first component cannot be common (since it would be part of the common prefix), + * so ignore that as well. + * Surround with dir separators so that we can easily determine the boundaries for ellipsizing. */ + for (i = 0; i < num; i++) { + start = strchr(file_names[i] + prefix_len, G_DIR_SEPARATOR); + end = start ? strrchr(start+1, G_DIR_SEPARATOR) : NULL; + /* Breaking out early will also skip looking for lcs (wouldn't succeed anyway). */ + if (!start || !end) + break; + names[i] = g_strndup(start, end-start+1); + } + + lcs_len = 0; + substring = (i == num) ? utils_strv_find_lcs(names, num) : NULL; + if (NZV(substring)) + { + /* Strip leading component. */ + start = strchr(substring, G_DIR_SEPARATOR); + if (start) + { + lcs = start + 1; + /* Strip trailing components (perhaps empty). */ + end = strrchr(lcs, G_DIR_SEPARATOR); + if (end) + { + *end = '\0'; + lcs_len = strlen(lcs); + /* Don't bother for tiny common parts (which are often just "." or "/"). */ + if (lcs_len < 5) + lcs_len = 0; + } + } + } + + /* Finally build the shortened list of unique file names */ + for (i = 0; i < num; i++) + { + start = file_names[i] + prefix_len; + if (lcs_len == 0) + { /* no lcs, copy without prefix */ + SETPTR(names[i], g_strdup(start)); + } + else + { + const gchar *lcs_start = strstr(start, lcs); + const gchar *lcs_end = lcs_start + lcs_len; + /* Maybe replace with unicode's "…" ? */ + SETPTR(names[i], g_strdup_printf("%.*s...%s", (int)(lcs_start - start), start, lcs_end)); + } + } + + g_free(substring); + g_free(prefix); + + return names; +} +
/* Try to parse a date using g_date_set_parse(). It doesn't take any format hint, * obviously g_date_set_parse() uses some magic.
Modified: src/utils.h 2 lines changed, 2 insertions(+), 0 deletions(-) =================================================================== @@ -301,6 +301,8 @@ gchar **utils_strv_new(const gchar *first, ...) G_GNUC_NULL_TERMINATED;
gchar **utils_strv_join(gchar **first, gchar **second) G_GNUC_WARN_UNUSED_RESULT;
+gchar **utils_strv_shorten_file_list(gchar **file_names, gssize file_names_len); + GSList *utils_get_config_files(const gchar *subdir);
gchar *utils_get_help_url(const gchar *suffix);
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).