File indexing completed on 2024-04-21 03:57:22

0001 /*
0002     SPDX-FileCopyrightText: 2014 Sven Brauch <svenbrauch@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "katekeywordcompletion.h"
0008 
0009 #include "katedocument.h"
0010 #include "katehighlight.h"
0011 #include "katetextline.h"
0012 
0013 #include <ktexteditor/view.h>
0014 
0015 #include <KLocalizedString>
0016 #include <QString>
0017 
0018 KateKeywordCompletionModel::KateKeywordCompletionModel(QObject *parent)
0019     : CodeCompletionModel(parent)
0020 {
0021     setHasGroups(false);
0022 }
0023 
0024 void KateKeywordCompletionModel::completionInvoked(KTextEditor::View *view,
0025                                                    const KTextEditor::Range &range,
0026                                                    KTextEditor::CodeCompletionModel::InvocationType /*invocationType*/)
0027 {
0028     KTextEditor::DocumentPrivate *doc = static_cast<KTextEditor::DocumentPrivate *>(view->document());
0029     if (!doc->highlight() || doc->highlight()->noHighlighting()) {
0030         return;
0031     }
0032     m_items = doc->highlight()->keywordsForLocation(doc, range.end());
0033     std::sort(m_items.begin(), m_items.end());
0034 }
0035 
0036 QModelIndex KateKeywordCompletionModel::parent(const QModelIndex &index) const
0037 {
0038     if (index.internalId()) {
0039         return createIndex(0, 0);
0040     } else {
0041         return QModelIndex();
0042     }
0043 }
0044 
0045 QModelIndex KateKeywordCompletionModel::index(int row, int column, const QModelIndex &parent) const
0046 {
0047     if (!parent.isValid()) {
0048         if (row == 0) {
0049             return createIndex(row, column);
0050         } else {
0051             return QModelIndex();
0052         }
0053     } else if (parent.parent().isValid()) {
0054         return QModelIndex();
0055     }
0056 
0057     if (row < 0 || row >= m_items.count() || column < 0 || column >= ColumnCount) {
0058         return QModelIndex();
0059     }
0060 
0061     return createIndex(row, column, 1);
0062 }
0063 
0064 int KateKeywordCompletionModel::rowCount(const QModelIndex &parent) const
0065 {
0066     if (!parent.isValid() && !m_items.isEmpty()) {
0067         return 1; // One root node to define the custom group
0068     } else if (parent.parent().isValid()) {
0069         return 0; // Completion-items have no children
0070     } else {
0071         return m_items.count();
0072     }
0073 }
0074 
0075 static bool isInWord(const KTextEditor::View *view, const KTextEditor::Cursor &position, QChar c)
0076 {
0077     KTextEditor::DocumentPrivate *document = static_cast<KTextEditor::DocumentPrivate *>(view->document());
0078     KateHighlighting *highlight = document->highlight();
0079     Kate::TextLine line = document->kateTextLine(position.line());
0080     return highlight->isInWord(c, line.attribute(position.column() - 1));
0081 }
0082 
0083 KTextEditor::Range KateKeywordCompletionModel::completionRange(KTextEditor::View *view, const KTextEditor::Cursor &position)
0084 {
0085     const QString &text = view->document()->text(KTextEditor::Range(position, KTextEditor::Cursor(position.line(), 0)));
0086     int pos;
0087     for (pos = text.size() - 1; pos >= 0; pos--) {
0088         if (isInWord(view, position, text.at(pos))) {
0089             // This needs to be aware of what characters are word-characters in the
0090             // active language, so that languages which prefix commands with e.g. @
0091             // or \ have properly working completion.
0092             continue;
0093         }
0094         break;
0095     }
0096     return KTextEditor::Range(KTextEditor::Cursor(position.line(), pos + 1), position);
0097 }
0098 
0099 bool KateKeywordCompletionModel::shouldAbortCompletion(KTextEditor::View *view, const KTextEditor::Range &range, const QString &currentCompletion)
0100 {
0101     if (view->cursorPosition() < range.start() || view->cursorPosition() > range.end()) {
0102         return true; // Always abort when the completion-range has been left
0103     }
0104     // Do not abort completions when the text has been empty already before and a newline has been entered
0105 
0106     for (QChar c : currentCompletion) {
0107         if (!isInWord(view, range.start(), c)) {
0108             return true;
0109         }
0110     }
0111     return false;
0112 }
0113 
0114 bool KateKeywordCompletionModel::shouldStartCompletion(KTextEditor::View * /*view*/,
0115                                                        const QString &insertedText,
0116                                                        bool userInsertion,
0117                                                        const KTextEditor::Cursor & /*position*/)
0118 {
0119     if (userInsertion && insertedText.size() > 3 && !insertedText.contains(QLatin1Char(' ')) && insertedText.at(insertedText.size() - 1).isLetter()) {
0120         return true;
0121     }
0122     return false;
0123 }
0124 
0125 bool KateKeywordCompletionModel::shouldHideItemsWithEqualNames() const
0126 {
0127     return true;
0128 }
0129 
0130 QVariant KateKeywordCompletionModel::data(const QModelIndex &index, int role) const
0131 {
0132     if (role == UnimportantItemRole) {
0133         return QVariant(true);
0134     }
0135     if (role == InheritanceDepth) {
0136         return 9000;
0137     }
0138 
0139     if (!index.parent().isValid()) {
0140         // group header
0141         switch (role) {
0142         case Qt::DisplayRole:
0143             return i18n("Language keywords");
0144         case GroupRole:
0145             return Qt::DisplayRole;
0146         }
0147     }
0148 
0149     if (index.column() == KTextEditor::CodeCompletionModel::Name && role == Qt::DisplayRole) {
0150         return m_items.at(index.row());
0151     }
0152 
0153     if (index.column() == KTextEditor::CodeCompletionModel::Icon && role == Qt::DecorationRole) {
0154         static const QIcon icon(QIcon::fromTheme(QStringLiteral("code-variable")).pixmap(QSize(16, 16)));
0155         return icon;
0156     }
0157 
0158     return QVariant();
0159 }
0160 
0161 KTextEditor::CodeCompletionModelControllerInterface::MatchReaction KateKeywordCompletionModel::matchingItem(const QModelIndex & /*matched*/)
0162 {
0163     return KTextEditor::CodeCompletionModelControllerInterface::HideListIfAutomaticInvocation;
0164 }
0165 
0166 #include "moc_katekeywordcompletion.cpp"
0167 
0168 // kate: indent-width 4; replace-tabs on