Refactoring and review comments
- Fix lots of compiler warnings - Fix a bug where a long base name would prevent ellipsizing the longest common substring - rewrite utils_strv_shorten_file_list to be more clear (hopefully) - use g_strlcpy - optimize case where the longest common substring need not be searched for
This commit is contained in:
parent
fb9673eb61
commit
e0a2c6277a
109
src/utils.c
109
src/utils.c
@ -2054,9 +2054,9 @@ gchar **utils_strv_join(gchar **first, gchar **second)
|
||||
* @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, size_t num)
|
||||
static gchar *utils_strv_find_common_prefix(gchar **strv, gsize num)
|
||||
{
|
||||
gchar *prefix, **ptr;
|
||||
gchar *prefix;
|
||||
|
||||
if (!NZV(strv))
|
||||
return NULL;
|
||||
@ -2068,7 +2068,7 @@ static gchar *utils_strv_find_common_prefix(gchar **strv, size_t num)
|
||||
|
||||
for (gint i = 0; prefix[i]; i++)
|
||||
{
|
||||
for (gint j = 1; j < num; j++)
|
||||
for (gsize j = 1; j < num; j++)
|
||||
{
|
||||
gchar *s = strv[j];
|
||||
if (s[i] != prefix[i])
|
||||
@ -2093,14 +2093,14 @@ static gchar *utils_strv_find_common_prefix(gchar **strv, size_t num)
|
||||
*
|
||||
* @return The common prefix that is part of all strings.
|
||||
*/
|
||||
gchar *utils_strv_find_lcs(gchar **strv, size_t num)
|
||||
static gchar *utils_strv_find_lcs(gchar **strv, gsize num)
|
||||
{
|
||||
gchar *first, *other, *_sub, *sub;
|
||||
gchar *first, *_sub, *sub;
|
||||
gsize n_chars;
|
||||
gsize len;
|
||||
gsize max = 0;
|
||||
char *lcs;
|
||||
gint found;
|
||||
gsize found;
|
||||
|
||||
if (strv == NULL)
|
||||
return NULL;
|
||||
@ -2122,11 +2122,9 @@ gchar *utils_strv_find_lcs(gchar **strv, size_t num)
|
||||
break;
|
||||
for (n_chars = 1; n_chars <= chars_left; n_chars++)
|
||||
{
|
||||
/* strlcpy() ftw! */
|
||||
memcpy(sub, _sub, n_chars);
|
||||
sub[n_chars] = '\0';
|
||||
g_strlcpy(sub, _sub, n_chars+1);
|
||||
found = 1;
|
||||
for (gint i = 1; i < num; i++)
|
||||
for (gsize i = 1; i < num; i++)
|
||||
{
|
||||
if (strstr(strv[i], sub) == NULL)
|
||||
break;
|
||||
@ -2147,7 +2145,7 @@ gchar *utils_strv_find_lcs(gchar **strv, size_t num)
|
||||
|
||||
/** Transform file names in a list to be shorter.
|
||||
*
|
||||
* This function takes a list of file names (porbably with absolute paths), and
|
||||
* 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).
|
||||
@ -2163,70 +2161,83 @@ gchar *utils_strv_find_lcs(gchar **strv, size_t num)
|
||||
* @since 1.34 (API 239)
|
||||
*/
|
||||
GEANY_API_SYMBOL
|
||||
gchar **utils_strv_shorten_file_list(gchar **file_names, size_t num)
|
||||
gchar **utils_strv_shorten_file_list(gchar **file_names, gsize num)
|
||||
{
|
||||
gint i, j;
|
||||
gchar *prefix, *substring, *name, *sep, **s;
|
||||
TMTag *tmtag;
|
||||
gsize i;
|
||||
gchar *prefix, *substring, *lcs;
|
||||
gchar *start, *end;
|
||||
gchar **names;
|
||||
gsize len;
|
||||
gsize prefix_len, lcs_len;
|
||||
|
||||
/* 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. */
|
||||
if (!num)
|
||||
num = g_strv_length(file_names);
|
||||
/* Always include a terminating NULL, enables easy freeing with g_strfreev() */
|
||||
names = g_new(gchar *, num + 1);
|
||||
names[num] = 0;
|
||||
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 '/' */
|
||||
len = 0;
|
||||
prefix_len = 0;
|
||||
if (NZV(prefix) && prefix[1])
|
||||
{
|
||||
/* Only strip directory components, include trailing '/' */
|
||||
sep = strrchr(prefix, G_DIR_SEPARATOR);
|
||||
if (sep)
|
||||
len = sep - prefix + 1;
|
||||
start = strrchr(prefix, G_DIR_SEPARATOR);
|
||||
if (start)
|
||||
prefix_len = start - prefix + 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
names[i] = g_strdup(file_names[i] + len);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Second: determine the longest common substring, that will be ellipsized */
|
||||
substring = utils_strv_find_lcs(names, num);
|
||||
lcs_len = 0;
|
||||
substring = (i == num) ? utils_strv_find_lcs(names, num) : NULL;
|
||||
if (NZV(substring))
|
||||
{
|
||||
/* Only ellipsize directory components. Directory delimiters ought
|
||||
* to be part of the substring. If it doesn't contain at least two
|
||||
* separators, then there isn't even a single directory to ellipsize
|
||||
* (also take care to not ellipsize the base file name). */
|
||||
gchar *start;
|
||||
sep = strchr(substring, G_DIR_SEPARATOR);
|
||||
|
||||
if (sep)
|
||||
/* Strip leading component. */
|
||||
start = strchr(substring, G_DIR_SEPARATOR);
|
||||
if (start)
|
||||
{
|
||||
len = 0;
|
||||
start = sep + 1;
|
||||
sep = strrchr(start, G_DIR_SEPARATOR);
|
||||
if (sep)
|
||||
lcs = start + 1;
|
||||
/* Strip trailing components (perhaps empty). */
|
||||
end = strrchr(lcs, G_DIR_SEPARATOR);
|
||||
if (end)
|
||||
{
|
||||
*sep = '\0';
|
||||
len = strlen(start);
|
||||
*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;
|
||||
}
|
||||
/* Don't bother for tiny substrings. */
|
||||
if (len >= 5)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally build the shortened list of unique file names */
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
gchar *s = strstr(names[i], start);
|
||||
gchar *rem = s + len; /* +1 skips over the leading '/' */
|
||||
gsize copy_n = strlen(rem) + 1; /* include NUL */
|
||||
memcpy(s, "...", 3); /* Maybe replace with unicode's "…" ? */
|
||||
memmove(s+3, rem, copy_n);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user