Mark Geary.Account.open_search as async and throwing an error

This lets us make the DB stemmer lookup async when constructing a
search query async and cancellable. Fix call sites.
This commit is contained in:
Michael Gratton 2019-08-04 21:35:46 +10:00 committed by Michael James Gratton
parent 7cf9825701
commit 983e8ce74e
6 changed files with 87 additions and 42 deletions

View file

@ -27,7 +27,7 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
private Configuration config;
private Gee.Set<Geary.App.Conversation>? selection_while_composing = null;
private GLib.Cancellable? find_cancellable = null;
// Stack pages
[GtkChild]
@ -269,8 +269,8 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
// Highlight matching terms from find if active, otherwise
// from the search folder if that's where we are at
Geary.SearchQuery? query = get_find_search_query(
conversation.base_folder.account
Geary.SearchQuery? query = yield get_find_search_query(
conversation.base_folder.account, null
);
if (query == null) {
Geary.SearchFolder? search_folder =
@ -301,6 +301,11 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
// Remove any existing conversation list, cancelling its loading
private void remove_current_list() {
if (this.find_cancellable != null) {
this.find_cancellable.cancel();
this.find_cancellable = null;
}
if (this.current_list != null) {
this.current_list.cancel_conversation_load();
this.conversation_removed(this.current_list);
@ -357,7 +362,34 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
base.set_visible_child(widget);
}
private Geary.SearchQuery? get_find_search_query(Geary.Account account) {
private async void update_find_results() {
ConversationListBox? list = this.current_list;
if (list != null) {
if (this.find_cancellable != null) {
this.find_cancellable.cancel();
}
GLib.Cancellable cancellable = new GLib.Cancellable();
cancellable.cancelled.connect(() => {
list.search.cancel();
});
this.find_cancellable = cancellable;
try {
Geary.SearchQuery? query = yield get_find_search_query(
list.conversation.base_folder.account,
cancellable
);
if (query != null) {
yield list.search.highlight_matching_email(query);
}
} catch (GLib.Error err) {
warning("Error updating find results: %s", err.message);
}
}
}
private async Geary.SearchQuery? get_find_search_query(Geary.Account account,
GLib.Cancellable? cancellable)
throws GLib.Error {
Geary.SearchQuery? query = null;
if (this.conversation_find_bar.get_search_mode()) {
string text = this.conversation_find_entry.get_text().strip();
@ -365,8 +397,8 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
// opening every message in the conversation as soon as
// the user presses a key
if (text.length >= 2) {
query = account.open_search(
text, this.config.get_search_strategy()
query = yield account.open_search(
text, this.config.get_search_strategy(), cancellable
);
}
}
@ -412,14 +444,7 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
private void on_find_text_changed(Gtk.SearchEntry entry) {
this.conversation_find_next.set_sensitive(false);
this.conversation_find_prev.set_sensitive(false);
if (this.current_list != null) {
Geary.SearchQuery? query = get_find_search_query(
this.current_list.conversation.base_folder.account
);
if (query != null) {
this.current_list.search.highlight_matching_email.begin(query);
}
}
this.update_find_results.begin();
}
[GtkCallback]

View file

@ -457,7 +457,10 @@ public abstract class Geary.Account : BaseObject, Loggable {
*
* Dropping the last reference to the SearchQuery will close it.
*/
public abstract Geary.SearchQuery open_search(string query, Geary.SearchQuery.Strategy strategy);
public abstract async Geary.SearchQuery open_search(string query,
SearchQuery.Strategy strategy,
GLib.Cancellable? cancellable)
throws GLib.Error;
/**
* Performs a search with the given query. Optionally, a list of folders not to search

View file

@ -159,9 +159,13 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
}
}
private async void set_search_query_async(string query, Geary.SearchQuery.Strategy strategy,
Cancellable? cancellable) throws Error {
Geary.SearchQuery search_query = account.open_search(query, strategy);
private async void set_search_query_async(string query,
Geary.SearchQuery.Strategy strategy,
Cancellable? cancellable)
throws GLib.Error {
Geary.SearchQuery search_query = yield account.open_search(
query, strategy, cancellable
);
int result_mutex_token = yield result_mutex.claim_async();

View file

@ -261,11 +261,11 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
// A list of all search terms, regardless of search op field name
private Gee.ArrayList<SearchTerm> all = new Gee.ArrayList<SearchTerm>();
public SearchQuery(ImapDB.Account account,
string query,
Geary.SearchQuery.Strategy strategy) {
base (query, strategy);
public async SearchQuery(ImapDB.Account account,
string query,
Geary.SearchQuery.Strategy strategy,
GLib.Cancellable? cancellable) {
base(query, strategy);
this.account = account;
switch (strategy) {
@ -298,7 +298,7 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
break;
}
prepare();
yield prepare(cancellable);
}
public Gee.Collection<string?> get_fields() {
@ -403,7 +403,7 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
return phrases;
}
private void prepare() {
private async void prepare(GLib.Cancellable? cancellable) {
// A few goals here:
// 1) Append an * after every term so it becomes a prefix search
// (see <https://www.sqlite.org/fts3.html#section_3>)
@ -490,7 +490,7 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
// searching for [archive* OR archiv*] when that's
// the same as [archiv*]), otherwise search for
// both
string? stemmed = stem_search_term(s);
string? stemmed = yield stem_search_term(s, cancellable);
string? sql_stemmed = null;
if (stemmed != null) {
@ -580,7 +580,8 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
*
* Otherwise, the stem for the term is returned.
*/
private string? stem_search_term(string term) {
private async string? stem_search_term(string term,
GLib.Cancellable? cancellable) {
if (!this.allow_stemming)
return null;
@ -590,19 +591,25 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
string? stemmed = null;
try {
Db.Statement stmt = this.account.db.prepare("""
SELECT token
FROM TokenizerTable
WHERE input=?
""");
stmt.bind_string(0, term);
yield this.account.db.exec_transaction_async(RO,
(cx, cancellable) => {
Db.Statement stmt = cx.prepare("""
SELECT token
FROM TokenizerTable
WHERE input=?
""");
stmt.bind_string(0, term);
// get stemmed string; if no result, fall through
Db.Result result = stmt.exec();
if (!result.finished)
stemmed = result.string_at(0);
else
debug("No stemmed term returned for \"%s\"", term);
// get stemmed string; if no result, fall through
Db.Result result = stmt.exec(cancellable);
if (!result.finished) {
stemmed = result.string_at(0);
} else {
debug("No stemmed term returned for \"%s\"", term);
}
return COMMIT;
}, cancellable
);
} catch (Error err) {
debug("Unable to query tokenizer table for stemmed term for \"%s\": %s", term, err.message);

View file

@ -547,8 +547,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
return yield local.fetch_email_async(check_id(email_id), required_fields, cancellable);
}
public override Geary.SearchQuery open_search(string query, SearchQuery.Strategy strategy) {
return new ImapDB.SearchQuery(local, query, strategy);
public override async Geary.SearchQuery open_search(string query,
SearchQuery.Strategy strategy,
GLib.Cancellable? cancellable)
throws GLib.Error {
return yield new ImapDB.SearchQuery(local, query, strategy, cancellable);
}
public override async Gee.Collection<Geary.EmailIdentifier>? local_search_async(Geary.SearchQuery query,

View file

@ -213,7 +213,10 @@ public class Geary.MockAccount : Account, MockObject {
);
}
public override SearchQuery open_search(string query, SearchQuery.Strategy strategy) {
public override async SearchQuery open_search(string query,
SearchQuery.Strategy strategy,
GLib.Cancellable? cancellable)
throws GLib.Error {
return new MockSearchQuery();
}