[devhelp] KeywordModel: delegate DhLink matching to DhSearchContext
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [devhelp] KeywordModel: delegate DhLink matching to DhSearchContext
- Date: Thu, 18 Jan 2018 15:49:10 +0000 (UTC)
commit 30075bd6e78189c1863c2cc71c7c541753c9b486
Author: Sébastien Wilmet <swilmet gnome org>
Date: Thu Jan 18 14:45:43 2018 +0100
KeywordModel: delegate DhLink matching to DhSearchContext
The implementation is different.
Now only two GPatternSpec's are created (prefix and nonprefix), not for
each keyword separately. All keywords are concatenated with the '*' glob
in-between.
For example searching:
"gtk_source_region region" is now equivalent to:
"gtk_source_region*region".
It's at least the behavior that I expect when I do a search with several
keywords.
https://bugzilla.gnome.org/show_bug.cgi?id=782257
src/dh-keyword-model.c | 163 +++--------------------------------------------
src/dh-search-context.c | 95 +++++++++++++++++++++++++++
src/dh-search-context.h | 6 ++
3 files changed, 110 insertions(+), 154 deletions(-)
---
diff --git a/src/dh-keyword-model.c b/src/dh-keyword-model.c
index 9b5bed9..30d079d 100644
--- a/src/dh-keyword-model.c
+++ b/src/dh-keyword-model.c
@@ -51,21 +51,10 @@ typedef struct {
gint stamp;
} DhKeywordModelPrivate;
-/* Store a keyword as well as glob
- * patterns that match at the start of a word
- * and one that matches in any position of a
- * word */
-typedef struct {
- gchar *keyword;
- GPatternSpec *glob_pattern_start;
- GPatternSpec *glob_pattern_any;
- guint has_glob : 1;
-} DhKeywordGlobPattern;
-
typedef struct {
+ DhSearchContext *search_context;
const gchar *search_string;
GStrv keywords;
- GList *keyword_globs;
const gchar *book_id;
const gchar *skip_book_id;
const gchar *page_id;
@@ -369,73 +358,6 @@ dh_keyword_model_new (void)
return g_object_new (DH_TYPE_KEYWORD_MODEL, NULL);
}
-/* For each keyword, creates a DhKeywordGlobPattern with GPatternSpec's
- * allocated if there are any special glob characters ('*', '?') in the keyword.
- */
-static GList *
-dh_globbed_keywords_new (GStrv keywords)
-{
- GList *list = NULL;
- gint i;
-
- if (keywords == NULL) {
- return NULL;
- }
-
- for (i = 0; keywords[i] != NULL; i++) {
- DhKeywordGlobPattern *data;
-
- data = g_slice_new (DhKeywordGlobPattern);
- data->keyword = keywords[i];
-
- if (strchr (keywords[i], '*') != NULL ||
- strchr (keywords[i], '?') != NULL) {
- gchar *glob;
-
- data->has_glob = TRUE;
-
- /* g_pattern_match matches whole strings only, so
- * for globs, we need to end with a star for partial matches */
- glob = g_strdup_printf ("%s*", keywords[i]);
- data->glob_pattern_start = g_pattern_spec_new (glob);
- g_free (glob);
-
- glob = g_strdup_printf ("*%s*", keywords[i]);
- data->glob_pattern_any = g_pattern_spec_new (glob);
- g_free (glob);
- } else {
- data->has_glob = FALSE;
- }
-
- list = g_list_prepend (list, data);
- }
-
- return g_list_reverse (list);
-}
-
-/* Frees all the datastructures and patterns associated with
- * keyword_globs as well as keyword_globs itself. It does not free
- * DhKeywordGlobPattern->keyword however (only the pattern specs).
- */
-static void
-dh_globbed_keywords_free (GList *keyword_globs)
-{
- GList *l;
-
- for (l = keyword_globs; l != NULL; l = l->next) {
- DhKeywordGlobPattern *data = l->data;
-
- if (data->has_glob) {
- g_pattern_spec_free (data->glob_pattern_start);
- g_pattern_spec_free (data->glob_pattern_any);
- }
-
- g_slice_free (DhKeywordGlobPattern, data);
- }
-
- g_list_free (keyword_globs);
-}
-
static GQueue *
search_single_book (DhBook *book,
SearchSettings *settings,
@@ -449,77 +371,12 @@ search_single_book (DhBook *book,
for (l = dh_book_get_links (book);
l != NULL && ret->length < max_hits;
- l = g_list_next (l)) {
- DhLink *link;
- gboolean found;
-
- link = l->data;
- found = FALSE;
-
- /* Filter by page? */
- if (settings->page_id != NULL) {
- if (!dh_link_belongs_to_page (link,
- settings->page_id,
- settings->case_sensitive)) {
- /* No need of this keyword. */
- continue;
- }
-
- /* This means we got no keywords to look for. */
- if (settings->keywords == NULL) {
- /* Show all in the page */
- found = TRUE;
- }
- }
-
- if (!found && settings->keywords != NULL) {
- gboolean all_found;
- gboolean prefix_found;
- gchar *name;
- GList *list;
-
- name = (settings->case_sensitive ?
- g_strdup (dh_link_get_name (link)) :
- g_ascii_strdown (dh_link_get_name (link), -1));
-
- all_found = TRUE;
- prefix_found = FALSE;
- for (list = settings->keyword_globs; list != NULL; list = g_list_next (list)) {
- DhKeywordGlobPattern *data = list->data;
-
- /* If our keyword is a glob pattern, use
- * it. Otherwise, do more efficient string searching */
- if (data->has_glob) {
- if (g_pattern_match_string (data->glob_pattern_start, name)) {
- prefix_found = TRUE;
- /* If we get a prefix match and we're not
- * looking for prefix, stop. */
- if (!settings->prefix)
- break;
- } else if (!g_pattern_match_string (data->glob_pattern_any, name)) {
- all_found = FALSE;
- break;
- }
- } else {
- if (g_str_has_prefix (name, data->keyword)) {
- prefix_found = TRUE;
- if (!settings->prefix)
- break;
- } else if (g_strrstr (name, data->keyword) == NULL) {
- all_found = FALSE;
- break;
- }
- }
- }
-
- g_free (name);
-
- found = all_found &&
- ((settings->prefix && prefix_found) ||
- (!settings->prefix && !prefix_found));
- }
+ l = l->next) {
+ DhLink *link = l->data;
- if (found) {
+ if (_dh_search_context_match_link (settings->search_context,
+ link,
+ settings->prefix)) {
g_queue_push_tail (ret, link);
if (exact_link == NULL || dh_link_get_name (link) == NULL)
@@ -641,9 +498,9 @@ keyword_model_search (DhKeywordModel *model,
DhLink *other_books_exact_link = NULL;
GQueue *out = g_queue_new ();
+ settings.search_context = search_context;
settings.search_string = search_string;
settings.keywords = _dh_search_context_get_keywords (search_context);
- settings.keyword_globs = dh_globbed_keywords_new (settings.keywords);
settings.book_id = priv->current_book_id;
settings.skip_book_id = NULL;
settings.page_id = _dh_search_context_get_page_id (search_context);
@@ -696,7 +553,7 @@ keyword_model_search (DhKeywordModel *model,
}
if (out->length >= max_hits)
- goto out;
+ return out;
/* Look for non-prefixed matches in current book. */
settings.prefix = FALSE;
@@ -712,7 +569,7 @@ keyword_model_search (DhKeywordModel *model,
dh_util_queue_concat (out, in_book);
if (out->length >= max_hits)
- goto out;
+ return out;
}
/* If still room for more items, look for non-prefixed items in other
@@ -726,8 +583,6 @@ keyword_model_search (DhKeywordModel *model,
NULL);
dh_util_queue_concat (out, other_books);
-out:
- dh_globbed_keywords_free (settings.keyword_globs);
return out;
}
diff --git a/src/dh-search-context.c b/src/dh-search-context.c
index 671a727..192d952 100644
--- a/src/dh-search-context.c
+++ b/src/dh-search-context.c
@@ -20,9 +20,19 @@
#include <string.h>
struct _DhSearchContext {
+ /* The content of the search string */
gchar *book_id;
gchar *page_id;
+
+ // If non-NULL, contains at least one non-empty string.
GStrv keywords;
+
+ /* Derived data */
+
+ // The GPatternSpec's are NULL if @keywords is NULL.
+ GPatternSpec *pattern_spec_prefix;
+ GPatternSpec *pattern_spec_nonprefix;
+
guint case_sensitive : 1;
};
@@ -176,6 +186,39 @@ set_case_sensitive (DhSearchContext *search)
}
}
+static void
+create_pattern_specs (DhSearchContext *search)
+{
+ GString *pattern_prefix_string;
+ gchar *pattern_nonprefix_str;
+ gint i;
+
+ g_assert (search->pattern_spec_prefix == NULL);
+ g_assert (search->pattern_spec_nonprefix == NULL);
+
+ if (search->keywords == NULL)
+ return;
+
+ /* Prefix */
+ pattern_prefix_string = g_string_new (NULL);
+
+ for (i = 0; search->keywords[i] != NULL; i++) {
+ const gchar *cur_keyword = search->keywords[i];
+
+ g_string_append (pattern_prefix_string, cur_keyword);
+ g_string_append_c (pattern_prefix_string, '*');
+ }
+
+ search->pattern_spec_prefix = g_pattern_spec_new (pattern_prefix_string->str);
+
+ /* Non-prefix */
+ pattern_nonprefix_str = g_strconcat ("?*", pattern_prefix_string->str, NULL);
+ search->pattern_spec_nonprefix = g_pattern_spec_new (pattern_nonprefix_str);
+
+ g_string_free (pattern_prefix_string, TRUE);
+ g_free (pattern_nonprefix_str);
+}
+
/* Returns: (transfer full) (nullable): a new #DhSearchContext, or %NULL if
* @search_string is invalid.
*/
@@ -194,6 +237,7 @@ _dh_search_context_new (const gchar *search_string)
}
set_case_sensitive (search);
+ create_pattern_specs (search);
return search;
}
@@ -207,6 +251,13 @@ _dh_search_context_free (DhSearchContext *search)
g_free (search->book_id);
g_free (search->page_id);
g_strfreev (search->keywords);
+
+ if (search->pattern_spec_prefix != NULL)
+ g_pattern_spec_free (search->pattern_spec_prefix);
+
+ if (search->pattern_spec_nonprefix != NULL)
+ g_pattern_spec_free (search->pattern_spec_nonprefix);
+
g_free (search);
}
@@ -241,3 +292,47 @@ _dh_search_context_get_case_sensitive (DhSearchContext *search)
return search->case_sensitive;
}
+
+/* This function assumes that checking that the DhBook (book_id) matches has
+ * already been done (to not check the book_id for each DhLink).
+ */
+gboolean
+_dh_search_context_match_link (DhSearchContext *search,
+ DhLink *link,
+ gboolean prefix)
+{
+ gchar *str_to_free = NULL;
+ const gchar *name;
+ gboolean match = FALSE;
+
+ g_return_val_if_fail (search != NULL, FALSE);
+ g_return_val_if_fail (link != NULL, FALSE);
+
+ /* Filter by page? */
+ if (search->page_id != NULL) {
+ if (!dh_link_belongs_to_page (link, search->page_id, TRUE))
+ return FALSE;
+
+ if (search->keywords == NULL)
+ /* Show all in the page. */
+ return TRUE;
+ }
+
+ if (search->keywords == NULL)
+ return FALSE;
+
+ if (search->case_sensitive) {
+ name = dh_link_get_name (link);
+ } else {
+ str_to_free = g_ascii_strdown (dh_link_get_name (link), -1);
+ name = str_to_free;
+ }
+
+ if (prefix)
+ match = g_pattern_match_string (search->pattern_spec_prefix, name);
+ else
+ match = g_pattern_match_string (search->pattern_spec_nonprefix, name);
+
+ g_free (str_to_free);
+ return match;
+}
diff --git a/src/dh-search-context.h b/src/dh-search-context.h
index 2f748a1..ea00d92 100644
--- a/src/dh-search-context.h
+++ b/src/dh-search-context.h
@@ -20,6 +20,7 @@
#define DH_SEARCH_CONTEXT_H
#include <glib.h>
+#include "dh-link.h"
G_BEGIN_DECLS
@@ -43,6 +44,11 @@ GStrv _dh_search_context_get_keywords (DhSearchContext
G_GNUC_INTERNAL
gboolean _dh_search_context_get_case_sensitive (DhSearchContext *search);
+G_GNUC_INTERNAL
+gboolean _dh_search_context_match_link (DhSearchContext *search,
+ DhLink *link,
+ gboolean prefix);
+
G_END_DECLS
#endif /* DH_SEARCH_CONTEXT_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]