Skip to content
Snippets Groups Projects
CompletionProxyModel.cpp 4.73 KiB
Newer Older
  • Learn to ignore specific revisions
  • Nicolas Werner's avatar
    Nicolas Werner committed
    // SPDX-FileCopyrightText: 2021 Nheko Contributors
    
    // SPDX-FileCopyrightText: 2022 Nheko Contributors
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    //
    // SPDX-License-Identifier: GPL-3.0-or-later
    
    
    #include "CompletionProxyModel.h"
    
    #include <QRegularExpression>
    
    #include <QTextBoundaryFinder>
    
    
    #include "CompletionModelRoles.h"
    #include "Logging.h"
    #include "Utils.h"
    
    
    CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model,
                                               int max_mistakes,
    
      : QAbstractProxyModel(parent)
    
      , max_completions_(max_completions)
    
        setSourceModel(model);
    
        // insert all the full texts
        for (int i = 0; i < sourceModel()->rowCount(); i++) {
            if (static_cast<size_t>(i) < max_completions_)
                mapping.push_back(i);
    
            auto string1 = sourceModel()
                             ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole)
                             .toString()
                             .toLower();
            if (!string1.isEmpty())
                trie_.insert(string1.toUcs4(), i);
    
            auto string2 = sourceModel()
                             ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole2)
                             .toString()
                             .toLower();
            if (!string2.isEmpty())
                trie_.insert(string2.toUcs4(), 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());
    
        }
    
        connect(
          this,
          &CompletionProxyModel::newSearchString,
          this,
          [this](QString s) {
              searchString_ = s.toLower();
              invalidate();
          },
          Qt::QueuedConnection);
    
    }
    
    void
    CompletionProxyModel::invalidate()
    {
    
        auto key = searchString_.toUcs4();
        beginResetModel();
        if (!key.empty()) // return default model data, if no search string
            mapping = trie_.search(key, max_completions_, maxMistakes_);
        endResetModel();
    
    }
    
    QHash<int, QByteArray>
    CompletionProxyModel::roleNames() const
    {
    
        return this->sourceModel()->roleNames();
    
    }
    
    int
    CompletionProxyModel::rowCount(const QModelIndex &) const
    {
    
        if (searchString_.isEmpty())
            return std::min(
              static_cast<int>(std::min<size_t>(max_completions_, std::numeric_limits<int>::max())),
              sourceModel()->rowCount());
        else
            return (int)mapping.size();
    
    }
    
    QModelIndex
    CompletionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
    {
    
        // return default model data, if no search string
        if (searchString_.isEmpty()) {
            return index(sourceIndex.row(), 0);
        }
    
        for (int i = 0; i < (int)mapping.size(); i++) {
            if (mapping[i] == sourceIndex.row()) {
                return index(i, 0);
    
        }
        return QModelIndex();
    
    }
    
    QModelIndex
    CompletionProxyModel::mapToSource(const QModelIndex &proxyIndex) const
    {
    
        auto row = proxyIndex.row();
    
        // return default model data, if no search string
        if (searchString_.isEmpty()) {
            return index(row, 0);
        }
    
        if (row < 0 || row >= (int)mapping.size())
            return QModelIndex();
    
        return sourceModel()->index(mapping[row], 0);
    
    }
    
    QModelIndex
    CompletionProxyModel::index(int row, int column, const QModelIndex &) const
    {
    
        return createIndex(row, column);
    
    }
    
    QModelIndex
    CompletionProxyModel::parent(const QModelIndex &) const
    {
    
        return QModelIndex{};
    
    }
    int
    CompletionProxyModel::columnCount(const QModelIndex &) const
    {
    
        return sourceModel()->columnCount();
    
    }
    
    QVariant
    CompletionProxyModel::completionAt(int i) const
    {
    
        if (i >= 0 && i < rowCount())
            return data(index(i, 0), CompletionModel::CompletionRole);
        else
            return {};
    
    }
    
    void
    CompletionProxyModel::setSearchString(QString s)
    {
    
        emit newSearchString(s);