diff --git a/src/CompletionProxyModel.cpp b/src/CompletionProxyModel.cpp
index 07ecdf72..5712cd30 100644
--- a/src/CompletionProxyModel.cpp
+++ b/src/CompletionProxyModel.cpp
@@ -22,54 +22,53 @@ CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model,
{
setSourceModel(model);
- // insert all the full texts
+ auto insertParts = [this](const QString &str, int id) {
+ QTextBoundaryFinder finder(QTextBoundaryFinder::BoundaryType::Word, str);
+ finder.toStart();
+ do {
+ auto start = finder.position();
+ finder.toNextBoundary();
+ auto end = finder.position();
+
+ auto ref = str.midRef(start, end - start).trimmed();
+ if (!ref.isEmpty())
+ trie_.insert<ElementRank::second>(ref.toUcs4(), id);
+ } while (finder.position() < str.size());
+ };
+
+ const auto start_at = std::chrono::steady_clock::now();
+
+ // insert full texts and partial matches
for (int i = 0; i < sourceModel()->rowCount(); i++) {
- if (static_cast<size_t>(i) < max_completions_)
- mapping.push_back(i);
+ // full texts are ranked first and partial matches second
+ // that way when searching full texts will be first in result list
auto string1 = sourceModel()
->data(sourceModel()->index(i, 0), CompletionModel::SearchRole)
.toString()
.toLower();
- if (!string1.isEmpty())
- trie_.insert(string1.toUcs4(), i);
+ if (!string1.isEmpty()) {
+ trie_.insert<ElementRank::first>(string1.toUcs4(), i);
+ insertParts(string1, i);
+ }
auto string2 = sourceModel()
->data(sourceModel()->index(i, 0), CompletionModel::SearchRole2)
.toString()
.toLower();
- if (!string2.isEmpty())
- trie_.insert(string2.toUcs4(), i);
+ if (!string2.isEmpty()) {
+ trie_.insert<ElementRank::first>(string2.toUcs4(), i);
+ insertParts(string2, i);
+ }
}
- // insert the partial matches
- for (int i = 0; i < sourceModel()->rowCount(); i++) {
- auto insertParts = [i, this](const QString &str) {
- if (str.isEmpty())
- return;
-
- QTextBoundaryFinder finder(QTextBoundaryFinder::BoundaryType::Word, str);
- finder.toStart();
- do {
- auto start = finder.position();
- finder.toNextBoundary();
- auto end = finder.position();
-
- auto ref = str.midRef(start, end - start).trimmed();
- if (!ref.isEmpty())
- trie_.insert(ref.toUcs4(), i);
- } while (finder.position() < str.size());
- };
-
- insertParts(sourceModel()
- ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole)
- .toString()
- .toLower());
- insertParts(sourceModel()
- ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole2)
- .toString()
- .toLower());
- }
+ const auto end_at = std::chrono::steady_clock::now();
+ const auto build_time = std::chrono::duration<double, std::milli>(end_at - start_at);
+ nhlog::ui()->debug("CompletionProxyModel: build trie: {} ms", build_time.count());
+
+ // initialize default mapping
+ mapping.resize(std::min(max_completions_, static_cast<size_t>(model->rowCount())));
+ std::iota(mapping.begin(), mapping.end(), 0);
connect(
this,
diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h
index 78c7ea5f..e4a1b9cd 100644
--- a/src/CompletionProxyModel.h
+++ b/src/CompletionProxyModel.h
@@ -9,12 +9,19 @@
#include <QAbstractProxyModel>
+enum class ElementRank
+{
+ first,
+ second
+};
+
template<typename Key, typename Value>
struct trie
{
std::vector<Value> values;
std::map<Key, trie> next;
+ template<ElementRank r>
void insert(const QVector<Key> &keys, const Value &v)
{
auto t = this;
@@ -22,7 +29,11 @@ struct trie
t = &t->next[k];
}
- t->values.push_back(v);
+ if constexpr (r == ElementRank::first) {
+ t->values.insert(t->values.begin(), v);
+ } else if constexpr (r == ElementRank::second) {
+ t->values.push_back(v);
+ }
}
std::vector<Value> valuesAndSubvalues(size_t limit = -1) const
|