matrixion/src/CompletionProxyModel.h

259 lines
8.9 KiB
C
Raw Normal View History

#pragma once
// Class for showing a limited amount of completions at a time
#include <QAbstractProxyModel>
2020-11-24 15:40:23 +03:00
#include <QRegularExpression>
2020-11-20 04:38:08 +03:00
#include "CompletionModelRoles.h"
#include "Logging.h"
2020-11-20 06:05:31 +03:00
#include "Utils.h"
2020-11-20 04:38:08 +03:00
template<typename Key, typename Value>
struct trie
{
std::vector<Value> values;
std::map<Key, trie> next;
void insert(const QVector<Key> &keys, const Value &v)
{
auto t = this;
for (const auto k : keys) {
t = &t->next[k];
}
t->values.push_back(v);
}
std::vector<Value> valuesAndSubvalues(size_t limit = -1) const
{
std::vector<Value> ret;
if (limit < 200)
ret.reserve(limit);
for (const auto &v : values) {
if (ret.size() >= limit)
return ret;
else
ret.push_back(v);
}
for (const auto &[k, t] : next) {
(void)k;
if (ret.size() >= limit)
return ret;
else {
auto temp = t.valuesAndSubvalues(limit - ret.size());
2020-11-24 04:35:38 +03:00
for (auto &&v : temp) {
if (ret.size() >= limit)
return ret;
if (std::find(ret.begin(), ret.end(), v) == ret.end()) {
ret.push_back(std::move(v));
}
}
}
}
return ret;
}
std::vector<Value> search(const QVector<Key> &keys, //< TODO(Nico): replace this with a span
2020-11-24 04:35:38 +03:00
size_t limit,
size_t max_distance = 2) const
{
std::vector<Value> ret;
2020-11-24 04:35:38 +03:00
if (!limit)
return ret;
if (keys.isEmpty())
return valuesAndSubvalues(limit);
2020-11-24 04:35:38 +03:00
auto append = [&ret, limit](std::vector<Value> &&in) {
for (auto &&v : in) {
if (ret.size() >= limit)
return;
if (std::find(ret.begin(), ret.end(), v) == ret.end()) {
ret.push_back(std::move(v));
}
}
};
if (auto e = this->next.find(keys[0]); e != this->next.end()) {
append(e->second.search(keys.mid(1), limit, max_distance));
}
if (max_distance && ret.size() < limit) {
2020-11-24 04:35:38 +03:00
max_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;
}
}
if (t) {
append(t->search(
keys.mid(2), (limit - ret.size()) * 2, max_distance));
}
}
// delete character case
append(this->search(keys.mid(1), (limit - ret.size()) * 2, max_distance));
// substitute and insert cases
for (const auto &[k, t] : this->next) {
if (k == keys[0] || ret.size() >= limit)
break;
// substitute
append(t.search(keys.mid(1), limit - ret.size(), max_distance));
2020-11-24 04:35:38 +03:00
if (ret.size() >= limit)
break;
// insert
append(t.search(keys, limit - ret.size(), max_distance));
2020-11-24 04:35:38 +03:00
}
}
return ret;
}
};
class CompletionProxyModel : public QAbstractProxyModel
{
2020-11-20 04:38:08 +03:00
Q_OBJECT
public:
CompletionProxyModel(QAbstractItemModel *model, QObject *parent = nullptr)
: QAbstractProxyModel(parent)
{
setSourceModel(model);
2020-11-24 15:40:23 +03:00
QRegularExpression splitPoints("\\s+|-");
for (int i = 0; i < sourceModel()->rowCount(); i++) {
if (i < 7)
mapping.push_back(i);
auto string1 =
sourceModel()
->data(sourceModel()->index(i, 0), CompletionModel::SearchRole)
.toString()
.toLower();
trie_.insert(string1.toUcs4(), i);
2020-11-24 15:40:23 +03:00
for (const auto &e : string1.split(splitPoints, Qt::SkipEmptyParts)) {
trie_.insert(e.toUcs4(), i);
}
auto string2 =
sourceModel()
->data(sourceModel()->index(i, 0), CompletionModel::SearchRole2)
.toString()
.toLower();
2020-11-24 15:40:23 +03:00
if (!string2.isEmpty()) {
trie_.insert(string2.toUcs4(), i);
2020-11-24 15:40:23 +03:00
for (const auto &e :
string2.split(splitPoints, Qt::SkipEmptyParts)) {
trie_.insert(e.toUcs4(), i);
}
}
}
2020-11-20 06:05:31 +03:00
connect(
this,
&CompletionProxyModel::newSearchString,
this,
[this](QString s) {
s.remove(":");
s.remove("@");
searchString = s.toLower();
invalidate();
},
Qt::QueuedConnection);
}
2020-11-20 04:38:08 +03:00
void invalidate()
{
auto key = searchString.toUcs4();
beginResetModel();
mapping = trie_.search(key, 7);
endResetModel();
std::string temp;
for (auto v : mapping) {
temp += std::to_string(v) + ", ";
}
nhlog::ui()->debug("mapping: {}", temp);
};
2020-11-20 04:38:08 +03:00
QHash<int, QByteArray> roleNames() const override
{
return this->sourceModel()->roleNames();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
(void)parent;
return (int)mapping.size();
}
2020-11-20 04:38:08 +03:00
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
2020-11-20 06:05:31 +03:00
{
for (int i = 0; i < (int)mapping.size(); i++) {
if (mapping[i] == sourceIndex.row()) {
return index(i, 0);
}
}
return QModelIndex();
2020-11-20 06:05:31 +03:00
}
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
2020-11-20 06:05:31 +03:00
{
auto row = proxyIndex.row();
if (row < 0 || row >= (int)mapping.size())
return QModelIndex();
2020-11-20 06:05:31 +03:00
return sourceModel()->index(mapping[row], 0);
}
2020-11-20 06:05:31 +03:00
QModelIndex index(int row,
int column,
const QModelIndex &parent = QModelIndex()) const override
{
(void)parent;
return createIndex(row, column);
2020-11-20 06:05:31 +03:00
}
QModelIndex parent(const QModelIndex &) const override { return QModelIndex{}; }
int columnCount(const QModelIndex &) const override { return sourceModel()->columnCount(); }
2020-11-20 04:38:08 +03:00
public slots:
QVariant completionAt(int i) const
{
if (i >= 0 && i < rowCount())
return data(index(i, 0), CompletionModel::CompletionRole);
else
return {};
}
2020-11-20 06:05:31 +03:00
void setSearchString(QString s) { emit newSearchString(s); }
signals:
void newSearchString(QString);
private:
QString searchString;
trie<uint, int> trie_;
std::vector<int> mapping;
};