File indexing completed on 2024-05-05 05:52:18

0001 /*  This file is part of the Kate project.
0002  *  Based on the snippet plugin from KDevelop 4.
0003  *
0004  *  SPDX-FileCopyrightText: 2008 Andreas Pakulat <apaku@gmx.de>
0005  *  SPDX-FileCopyrightText: 2012 Christoph Cullmann <cullmann@kde.org>
0006  *
0007  *  SPDX-License-Identifier: LGPL-2.0-or-later
0008  */
0009 
0010 #include "snippetcompletionmodel.h"
0011 
0012 #include <ktexteditor/document.h>
0013 #include <ktexteditor/view.h>
0014 
0015 #include "snippet.h"
0016 #include "snippetcompletionitem.h"
0017 #include "snippetrepository.h"
0018 #include "snippetstore.h"
0019 
0020 #include <KLocalizedString>
0021 
0022 SnippetCompletionModel::SnippetCompletionModel()
0023     : KTextEditor::CodeCompletionModel(nullptr)
0024 {
0025     setHasGroups(false);
0026 }
0027 
0028 SnippetCompletionModel::~SnippetCompletionModel()
0029 {
0030     qDeleteAll(m_snippets);
0031     m_snippets.clear();
0032 }
0033 
0034 QVariant SnippetCompletionModel::data(const QModelIndex &idx, int role) const
0035 {
0036     if (role == KTextEditor::CodeCompletionModel::InheritanceDepth) {
0037         return 11000;
0038     }
0039 
0040     // grouping of snippets
0041     if (!idx.parent().isValid()) {
0042         if (role == Qt::DisplayRole) {
0043             return i18n("Snippets");
0044         }
0045         if (role == KTextEditor::CodeCompletionModel::GroupRole) {
0046             return Qt::DisplayRole;
0047         }
0048         return QVariant();
0049     }
0050     // snippets
0051     if (!idx.isValid() || idx.row() < 0 || idx.row() >= m_snippets.count()) {
0052         return QVariant();
0053     } else {
0054         return m_snippets.at(idx.row())->data(idx, role, nullptr);
0055     }
0056 }
0057 
0058 void SnippetCompletionModel::executeCompletionItem(KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const
0059 {
0060     if (index.parent().isValid()) {
0061         m_snippets[index.row()]->execute(view, word);
0062     }
0063 }
0064 
0065 void SnippetCompletionModel::completionInvoked(KTextEditor::View *view, const KTextEditor::Range &range, InvocationType invocationType)
0066 {
0067     Q_UNUSED(range);
0068     Q_UNUSED(invocationType);
0069     initData(view);
0070 }
0071 
0072 void SnippetCompletionModel::initData(KTextEditor::View *view)
0073 {
0074     QString posMode = view->document()->highlightingModeAt(view->cursorPosition());
0075     QString docMode = view->document()->highlightingMode();
0076     if (docMode.isEmpty() && posMode.isEmpty()) {
0077         qWarning() << Q_FUNC_INFO << "Unexpected empty modes";
0078         return;
0079     }
0080 
0081     beginResetModel();
0082 
0083     qDeleteAll(m_snippets);
0084     m_snippets.clear();
0085     SnippetStore *store = SnippetStore::self();
0086     for (int i = 0; i < store->rowCount(); i++) {
0087         if (store->item(i, 0)->checkState() != Qt::Checked) {
0088             continue;
0089         }
0090         SnippetRepository *repo = SnippetRepository::fromItem(store->item(i, 0));
0091         if (!repo) {
0092             continue;
0093         }
0094         const QStringList fileTypes = repo->fileTypes();
0095         if (fileTypes.isEmpty() || fileTypes.contains(docMode) || fileTypes.contains(posMode)) {
0096             for (int j = 0; j < repo->rowCount(); ++j) {
0097                 if (Snippet *snippet = Snippet::fromItem(repo->child(j))) {
0098                     m_snippets << new SnippetCompletionItem(snippet, repo);
0099                 }
0100             }
0101         }
0102     }
0103 
0104     endResetModel();
0105 }
0106 
0107 QModelIndex SnippetCompletionModel::parent(const QModelIndex &index) const
0108 {
0109     if (index.internalId()) {
0110         return createIndex(0, 0, quintptr(0));
0111     } else {
0112         return QModelIndex();
0113     }
0114 }
0115 
0116 QModelIndex SnippetCompletionModel::index(int row, int column, const QModelIndex &parent) const
0117 {
0118     if (!parent.isValid()) {
0119         if (row == 0) {
0120             return createIndex(row, column, quintptr(0)); // header  index
0121         } else {
0122             return QModelIndex();
0123         }
0124     } else if (parent.parent().isValid()) { // we only have header and children, no subheaders
0125         return QModelIndex();
0126     }
0127 
0128     if (row < 0 || row >= m_snippets.count() || column < 0 || column >= ColumnCount) {
0129         return QModelIndex();
0130     }
0131 
0132     return createIndex(row, column, 1); // normal item index
0133 }
0134 
0135 int SnippetCompletionModel::rowCount(const QModelIndex &parent) const
0136 {
0137     if (!parent.isValid() && !m_snippets.isEmpty()) {
0138         return 1; // one toplevel node (group header)
0139     } else if (parent.parent().isValid()) {
0140         return 0; // we don't have sub children
0141     } else {
0142         return m_snippets.count(); // only the children
0143     }
0144 }
0145 
0146 bool SnippetCompletionModel::shouldStartCompletion(KTextEditor::View *view,
0147                                                    const QString &insertedText,
0148                                                    bool userInsertion,
0149                                                    const KTextEditor::Cursor &position)
0150 {
0151     Q_UNUSED(view);
0152     Q_UNUSED(insertedText);
0153     Q_UNUSED(userInsertion);
0154     Q_UNUSED(position);
0155     return false;
0156 }
0157 
0158 bool SnippetCompletionModel::shouldAbortCompletion(KTextEditor::View *view, const KTextEditor::Range &range, const QString &currentCompletion)
0159 {
0160     if (view->cursorPosition() < range.start() || view->cursorPosition() > range.end()) {
0161         return true; // Always abort when the completion-range has been left
0162     }
0163 
0164     for (const auto token : currentCompletion) {
0165         if (token.isSpace()) {
0166             return true;
0167         }
0168     }
0169     // else it's valid
0170     return false;
0171 }
0172 
0173 #include "moc_snippetcompletionmodel.cpp"