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 ¤tCompletion) 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