[geany/geany] 9cc18f: Merge pull request #1445 from kugel-/fix-1069

Colomban Wendling git-noreply at xxxxx
Tue Dec 4 21:43:18 UTC 2018


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Tue, 04 Dec 2018 21:43:18 UTC
Commit:      9cc18f9143a50bb7402bbbd499ec7081d009f931
             https://github.com/geany/geany/commit/9cc18f9143a50bb7402bbbd499ec7081d009f931

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).


More information about the Commits mailing list