mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-23 20:46:14 +01:00
Merge branch 'search-app-prefix-strstr' into 'main'
gdesktopappinfo: Group search results by both categories and match types See merge request GNOME/glib!3107
This commit is contained in:
commit
9e2ad88455
@ -456,6 +456,14 @@ const gchar desktop_key_match_category[N_DESKTOP_KEYS] = {
|
|||||||
[DESKTOP_KEY_Comment] = 6
|
[DESKTOP_KEY_Comment] = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* Lower numbers have higher priority.
|
||||||
|
* Prefix match should put before substring match.
|
||||||
|
*/
|
||||||
|
MATCH_TYPE_PREFIX = 1,
|
||||||
|
MATCH_TYPE_SUBSTRING = 2
|
||||||
|
} MatchType;
|
||||||
|
|
||||||
/* Common prefix commands to ignore from Exec= lines */
|
/* Common prefix commands to ignore from Exec= lines */
|
||||||
const char * const exec_key_match_blocklist[] = {
|
const char * const exec_key_match_blocklist[] = {
|
||||||
"bash",
|
"bash",
|
||||||
@ -537,6 +545,7 @@ struct search_result
|
|||||||
{
|
{
|
||||||
const gchar *app_name;
|
const gchar *app_name;
|
||||||
gint category;
|
gint category;
|
||||||
|
gint match_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct search_result *static_token_results;
|
static struct search_result *static_token_results;
|
||||||
@ -558,13 +567,20 @@ compare_results (gconstpointer a,
|
|||||||
const struct search_result *rb = b;
|
const struct search_result *rb = b;
|
||||||
|
|
||||||
if (ra->app_name < rb->app_name)
|
if (ra->app_name < rb->app_name)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
else if (ra->app_name > rb->app_name)
|
else if (ra->app_name > rb->app_name)
|
||||||
|
{
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (ra->category == rb->category)
|
||||||
|
return ra->match_type - rb->match_type;
|
||||||
|
|
||||||
return ra->category - rb->category;
|
return ra->category - rb->category;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
@ -574,12 +590,19 @@ compare_categories (gconstpointer a,
|
|||||||
const struct search_result *ra = a;
|
const struct search_result *ra = a;
|
||||||
const struct search_result *rb = b;
|
const struct search_result *rb = b;
|
||||||
|
|
||||||
|
/* Also compare match types so we can put prefix match in a group while
|
||||||
|
* substring match in another group.
|
||||||
|
*/
|
||||||
|
if (ra->category == rb->category)
|
||||||
|
return ra->match_type - rb->match_type;
|
||||||
|
|
||||||
return ra->category - rb->category;
|
return ra->category - rb->category;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_token_result (const gchar *app_name,
|
add_token_result (const gchar *app_name,
|
||||||
guint16 category)
|
guint16 category,
|
||||||
|
guint16 match_type)
|
||||||
{
|
{
|
||||||
if G_UNLIKELY (static_token_results_size == static_token_results_allocated)
|
if G_UNLIKELY (static_token_results_size == static_token_results_allocated)
|
||||||
{
|
{
|
||||||
@ -589,6 +612,7 @@ add_token_result (const gchar *app_name,
|
|||||||
|
|
||||||
static_token_results[static_token_results_size].app_name = app_name;
|
static_token_results[static_token_results_size].app_name = app_name;
|
||||||
static_token_results[static_token_results_size].category = category;
|
static_token_results[static_token_results_size].category = category;
|
||||||
|
static_token_results[static_token_results_size].match_type = match_type;
|
||||||
static_token_results_size++;
|
static_token_results_size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,10 +696,22 @@ merge_token_results (gboolean first)
|
|||||||
*
|
*
|
||||||
* Category should be the worse of the two (ie:
|
* Category should be the worse of the two (ie:
|
||||||
* numerically larger).
|
* numerically larger).
|
||||||
|
*
|
||||||
|
* Match type should also be the worse, so if an app has two
|
||||||
|
* prefix matches it will has higher priority than one prefix
|
||||||
|
* matches and one substring matches, for example, LibreOffice
|
||||||
|
* Writer should be higher priority than LibreOffice Draw with
|
||||||
|
* `lib w`.
|
||||||
|
*
|
||||||
|
* (This ignores the difference between partly prefix matches and
|
||||||
|
* all substring matches, however most time we just focus on exact
|
||||||
|
* prefix matches, who cares the 10th-20th search results?)
|
||||||
*/
|
*/
|
||||||
static_search_results[j].app_name = static_search_results[k].app_name;
|
static_search_results[j].app_name = static_search_results[k].app_name;
|
||||||
static_search_results[j].category = MAX (static_search_results[k].category,
|
static_search_results[j].category = MAX (static_search_results[k].category,
|
||||||
static_token_results[i].category);
|
static_token_results[i].category);
|
||||||
|
static_search_results[j].match_type = MAX (static_search_results[k].match_type,
|
||||||
|
static_token_results[i].match_type);
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1224,13 +1260,24 @@ desktop_file_dir_unindexed_search (DesktopFileDir *dir,
|
|||||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||||
{
|
{
|
||||||
MemoryIndexEntry *mie = value;
|
MemoryIndexEntry *mie = value;
|
||||||
|
const char *p;
|
||||||
|
MatchType match_type;
|
||||||
|
|
||||||
if (strstr (key, search_token) == NULL)
|
/* strstr(haystack, needle) returns haystack if needle is empty, so if
|
||||||
|
* needle is not empty and return value equals to haystack means a prefix
|
||||||
|
* match.
|
||||||
|
*/
|
||||||
|
p = strstr (key, search_token);
|
||||||
|
if (p == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
else if (p == key && search_token != NULL && *search_token != '\0')
|
||||||
|
match_type = MATCH_TYPE_PREFIX;
|
||||||
|
else
|
||||||
|
match_type = MATCH_TYPE_SUBSTRING;
|
||||||
|
|
||||||
while (mie)
|
while (mie)
|
||||||
{
|
{
|
||||||
add_token_result (mie->app_name, mie->match_category);
|
add_token_result (mie->app_name, mie->match_category, match_type);
|
||||||
mie = mie->next;
|
mie = mie->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4776,9 +4823,10 @@ g_desktop_app_info_search (const gchar *search_string)
|
|||||||
{
|
{
|
||||||
gchar **search_tokens;
|
gchar **search_tokens;
|
||||||
gint last_category = -1;
|
gint last_category = -1;
|
||||||
|
gint last_match_type = -1;
|
||||||
gchar ***results;
|
gchar ***results;
|
||||||
gint n_categories = 0;
|
gint n_groups = 0;
|
||||||
gint start_of_category;
|
gint start_of_group;
|
||||||
gint i, j;
|
gint i, j;
|
||||||
guint k;
|
guint k;
|
||||||
|
|
||||||
@ -4800,36 +4848,41 @@ g_desktop_app_info_search (const gchar *search_string)
|
|||||||
|
|
||||||
sort_total_search_results ();
|
sort_total_search_results ();
|
||||||
|
|
||||||
/* Count the total number of unique categories */
|
/* Count the total number of unique categories and match types */
|
||||||
for (i = 0; i < static_total_results_size; i++)
|
for (i = 0; i < static_total_results_size; i++)
|
||||||
if (static_total_results[i].category != last_category)
|
if (static_total_results[i].category != last_category ||
|
||||||
|
static_total_results[i].match_type != last_match_type)
|
||||||
{
|
{
|
||||||
last_category = static_total_results[i].category;
|
last_category = static_total_results[i].category;
|
||||||
n_categories++;
|
last_match_type = static_total_results[i].match_type;
|
||||||
|
n_groups++;
|
||||||
}
|
}
|
||||||
|
|
||||||
results = g_new (gchar **, n_categories + 1);
|
results = g_new (gchar **, n_groups + 1);
|
||||||
|
|
||||||
/* Start loading into the results list */
|
/* Start loading into the results list */
|
||||||
start_of_category = 0;
|
start_of_group = 0;
|
||||||
for (i = 0; i < n_categories; i++)
|
for (i = 0; i < n_groups; i++)
|
||||||
{
|
{
|
||||||
gint n_items_in_category = 0;
|
gint n_items_in_group = 0;
|
||||||
gint this_category;
|
gint this_category;
|
||||||
|
gint this_match_type;
|
||||||
gint j;
|
gint j;
|
||||||
|
|
||||||
this_category = static_total_results[start_of_category].category;
|
this_category = static_total_results[start_of_group].category;
|
||||||
|
this_match_type = static_total_results[start_of_group].match_type;
|
||||||
|
|
||||||
while (start_of_category + n_items_in_category < static_total_results_size &&
|
while (start_of_group + n_items_in_group < static_total_results_size &&
|
||||||
static_total_results[start_of_category + n_items_in_category].category == this_category)
|
static_total_results[start_of_group + n_items_in_group].category == this_category &&
|
||||||
n_items_in_category++;
|
static_total_results[start_of_group + n_items_in_group].match_type == this_match_type)
|
||||||
|
n_items_in_group++;
|
||||||
|
|
||||||
results[i] = g_new (gchar *, n_items_in_category + 1);
|
results[i] = g_new (gchar *, n_items_in_group + 1);
|
||||||
for (j = 0; j < n_items_in_category; j++)
|
for (j = 0; j < n_items_in_group; j++)
|
||||||
results[i][j] = g_strdup (static_total_results[start_of_category + j].app_name);
|
results[i][j] = g_strdup (static_total_results[start_of_group + j].app_name);
|
||||||
results[i][j] = NULL;
|
results[i][j] = NULL;
|
||||||
|
|
||||||
start_of_category += n_items_in_category;
|
start_of_group += n_items_in_group;
|
||||||
}
|
}
|
||||||
results[i] = NULL;
|
results[i] = NULL;
|
||||||
|
|
||||||
|
@ -867,11 +867,12 @@ test_search (void)
|
|||||||
assert_search ("image viewer", "", TRUE, TRUE, NULL, NULL);
|
assert_search ("image viewer", "", TRUE, TRUE, NULL, NULL);
|
||||||
|
|
||||||
/* There're "flatpak" apps (clocks) installed as well - they should *not*
|
/* There're "flatpak" apps (clocks) installed as well - they should *not*
|
||||||
* match the prefix command ("/bin/sh") in the Exec= line though. However,
|
* match the prefix command ("/bin/sh") in the Exec= line though. Then with
|
||||||
* with substring matching, Image Viewer (eog) should be in result list
|
* substring matching, Image Viewer (eog) should be in next group because it
|
||||||
* because it contains "Slideshow" in its keywords.
|
* contains "Slideshow" in its keywords.
|
||||||
*/
|
*/
|
||||||
assert_search ("sh", "eog.desktop gnome-terminal.desktop\n", TRUE, FALSE, NULL, NULL);
|
assert_search ("sh", "gnome-terminal.desktop\n"
|
||||||
|
"eog.desktop\n", TRUE, FALSE, NULL, NULL);
|
||||||
|
|
||||||
/* "frobnicator.desktop" is ignored by get_all() because the binary is
|
/* "frobnicator.desktop" is ignored by get_all() because the binary is
|
||||||
* missing, but search should still find it (to avoid either stale results
|
* missing, but search should still find it (to avoid either stale results
|
||||||
@ -886,11 +887,12 @@ test_search (void)
|
|||||||
assert_search ("files file fil fi f", "nautilus.desktop\n"
|
assert_search ("files file fil fi f", "nautilus.desktop\n"
|
||||||
"gedit.desktop\n", TRUE, TRUE, NULL, NULL);
|
"gedit.desktop\n", TRUE, TRUE, NULL, NULL);
|
||||||
|
|
||||||
/* With substring match, "con" match dconf, contacts, nautilus-classic (which
|
/* "con" will match "connect" and "contacts" on name with prefix match in
|
||||||
* has a name called "Desktop Icons"), nautilus-connect-server (which has a
|
* first group, then match "Dconf Editor" and "Desktop Icons" with substring
|
||||||
* name called "Connect to Server").
|
* match in next group.
|
||||||
*/
|
*/
|
||||||
assert_search ("con", "dconf-editor.desktop gnome-contacts.desktop nautilus-classic.desktop nautilus-connect-server.desktop\n", TRUE, TRUE, NULL, NULL);
|
assert_search ("con", "gnome-contacts.desktop nautilus-connect-server.desktop\n"
|
||||||
|
"dconf-editor.desktop nautilus-classic.desktop\n", TRUE, TRUE, NULL, NULL);
|
||||||
|
|
||||||
/* "gnome" will match "eye of gnome" from the user's directory, plus
|
/* "gnome" will match "eye of gnome" from the user's directory, plus
|
||||||
* matching "GNOME Clocks" X-GNOME-FullName. It's only a comment on
|
* matching "GNOME Clocks" X-GNOME-FullName. It's only a comment on
|
||||||
|
Loading…
Reference in New Issue
Block a user