matrixion/src/CompletionProxyModel.h

199 lines
5.9 KiB
C
Raw Normal View History

2021-03-05 02:35:15 +03:00
// SPDX-FileCopyrightText: 2021 Nheko Contributors
// SPDX-FileCopyrightText: 2022 Nheko Contributors
// SPDX-FileCopyrightText: 2023 Nheko Contributors
2021-03-05 02:35:15 +03:00
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
// Class for showing a limited amount of completions at a time
#include <QAbstractProxyModel>
2020-11-20 04:38:08 +03:00
enum class ElementRank
{
first,
second
};
template<typename Key, typename Value>
struct trie
{
2021-09-18 01:22:33 +03:00
std::vector<Value> values;
std::map<Key, trie> next;
template<ElementRank r>
2021-09-18 01:22:33 +03:00
void insert(const QVector<Key> &keys, const Value &v)
{
auto t = this;
for (const auto k : keys) {
t = &t->next[k];
}
if constexpr (r == ElementRank::first) {
t->values.insert(t->values.begin(), v);
} else if constexpr (r == ElementRank::second) {
t->values.push_back(v);
}
2021-09-18 01:22:33 +03:00
}
2021-09-18 01:22:33 +03:00
std::vector<Value> valuesAndSubvalues(size_t limit = -1) const
{
std::vector<Value> ret;
if (limit < 200)
ret.reserve(limit);
2021-09-18 01:22:33 +03:00
for (const auto &v : values) {
if (ret.size() >= limit)
return ret;
2021-09-18 01:22:33 +03:00
else
ret.push_back(v);
}
2021-09-18 01:22:33 +03:00
for (const auto &[k, t] : next) {
(void)k;
if (ret.size() >= limit)
return ret;
else {
auto temp = t.valuesAndSubvalues(limit - ret.size());
for (auto &&v : temp) {
if (ret.size() >= limit)
2020-11-24 04:35:38 +03:00
return ret;
2021-09-18 01:22:33 +03:00
if (std::find(ret.begin(), ret.end(), v) == ret.end()) {
ret.push_back(std::move(v));
}
}
}
}
2021-09-18 01:22:33 +03:00
return ret;
}
2020-11-24 04:35:38 +03:00
2021-09-18 01:22:33 +03:00
std::vector<Value> search(const QVector<Key> &keys, //< TODO(Nico): replace this with a span
size_t result_count_limit,
size_t max_edit_distance_ = 2) const
{
std::vector<Value> ret;
if (!result_count_limit)
return ret;
if (keys.isEmpty())
return valuesAndSubvalues(result_count_limit);
auto append = [&ret, result_count_limit](std::vector<Value> &&in) {
for (auto &&v : in) {
if (ret.size() >= result_count_limit)
return;
2020-11-24 04:35:38 +03:00
2021-09-18 01:22:33 +03:00
if (std::find(ret.begin(), ret.end(), v) == ret.end()) {
ret.push_back(std::move(v));
}
}
};
auto limit = [&ret, result_count_limit] {
return std::min(result_count_limit, (result_count_limit - ret.size()) * 2);
};
// Try first exact matches, then with maximum errors
for (size_t max_edit_distance = 0;
max_edit_distance <= max_edit_distance_ && ret.size() < result_count_limit;
max_edit_distance += 1) {
if (max_edit_distance && ret.size() < result_count_limit) {
max_edit_distance -= 1;
// swap chars case
if (keys.size() >= 2) {
auto t = this;
for (int i = 1; i >= 0; i--) {
if (auto e = t->next.find(keys[i]); e != t->next.end()) {
t = &e->second;
} else {
t = nullptr;
break;
2020-11-24 04:35:38 +03:00
}
2021-09-18 01:22:33 +03:00
}
if (t) {
append(t->search(keys.mid(2), limit(), max_edit_distance));
}
}
2021-09-18 01:22:33 +03:00
// insert case
for (const auto &[k, t] : this->next) {
if (k == keys[0])
continue;
if (ret.size() >= limit())
break;
// insert
append(t.search(keys, limit(), max_edit_distance));
}
// delete character case
append(this->search(keys.mid(1), limit(), max_edit_distance));
// substitute case
for (const auto &[k, t] : this->next) {
if (k == keys[0])
continue;
if (ret.size() >= limit())
break;
// substitute
append(t.search(keys.mid(1), limit(), max_edit_distance));
}
max_edit_distance += 1;
}
if (auto e = this->next.find(keys[0]); e != this->next.end()) {
append(e->second.search(keys.mid(1), limit(), max_edit_distance));
}
}
2021-09-18 01:22:33 +03:00
return ret;
}
};
2022-10-10 15:38:29 +03:00
class CompletionProxyModel final : public QAbstractProxyModel
{
2021-09-18 01:22:33 +03:00
Q_OBJECT
Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY newSearchString)
public:
2021-09-18 01:22:33 +03:00
CompletionProxyModel(QAbstractItemModel *model,
int max_mistakes = 2,
size_t max_completions = 30,
2021-09-18 01:22:33 +03:00
QObject *parent = nullptr);
2020-11-24 15:40:23 +03:00
2021-09-18 01:22:33 +03:00
void invalidate();
2020-11-20 06:05:31 +03:00
2021-09-18 01:22:33 +03:00
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &) const override;
2020-11-20 04:38:08 +03:00
2021-09-18 01:22:33 +03:00
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
2020-11-20 06:05:31 +03:00
QModelIndex
index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
2021-09-18 01:22:33 +03:00
QModelIndex parent(const QModelIndex &) const override;
2020-11-20 04:38:08 +03:00
public slots:
2021-09-18 01:22:33 +03:00
QVariant completionAt(int i) const;
2020-11-20 06:05:31 +03:00
void setSearchString(const QString &s);
2021-09-18 01:22:33 +03:00
QString searchString() const { return searchString_; }
2020-11-20 06:05:31 +03:00
signals:
2021-09-18 01:22:33 +03:00
void newSearchString(QString);
2020-11-20 06:05:31 +03:00
private:
2021-09-18 01:22:33 +03:00
QString searchString_;
trie<uint, int> trie_;
std::vector<int> mapping;
int maxMistakes_;
size_t max_completions_;
};