8#include "katecompletionmodel.h"
10#include "kateargumenthintmodel.h"
11#include "katecompletiontree.h"
12#include "katecompletionwidget.h"
13#include "katepartdebug.h"
14#include "katerenderer.h"
16#include <ktexteditor/codecompletionmodelcontrollerinterface.h>
18#include <KFuzzyMatcher>
19#include <KLocalizedString>
21#include <QApplication>
24#include <QVarLengthArray>
29class HierarchicalModelHandler
32 explicit HierarchicalModelHandler(CodeCompletionModel *model);
35 void collectRoles(
const QModelIndex &index);
36 void takeRole(
const QModelIndex &index);
38 CodeCompletionModel *model()
const;
43 bool hasHierarchicalRoles()
const;
45 int inheritanceDepth(
const QModelIndex &i)
const;
47 QString customGroup()
const
52 int customGroupingKey()
const
54 return m_groupSortingKey;
58 typedef std::pair<CodeCompletionModel::ExtraItemDataRoles, QVariant> RoleAndValue;
59 typedef std::vector<std::pair<CodeCompletionModel::ExtraItemDataRoles, QVariant>> RoleMap;
61 QString m_customGroup;
62 int m_groupSortingKey;
63 CodeCompletionModel *m_model;
71bool HierarchicalModelHandler::hasHierarchicalRoles()
const
73 return !m_roleValues.empty();
76void HierarchicalModelHandler::collectRoles(
const QModelIndex &index)
79 collectRoles(index.
parent());
81 if (m_model->rowCount(index) != 0) {
86int HierarchicalModelHandler::inheritanceDepth(
const QModelIndex &i)
const
91void HierarchicalModelHandler::takeRole(
const QModelIndex &index)
100 m_groupSortingKey = sortingKey.
toInt();
104 addValue(role, value);
107 qCDebug(LOG_KTE) <<
"Did not return valid GroupRole in hierarchical completion-model";
113 auto it = std::find_if(m_roleValues.begin(), m_roleValues.end(), [role](
const RoleAndValue &v) {
114 return v.first == role;
116 if (it != m_roleValues.end()) {
119 return index.
data(role);
124 : m_groupSortingKey(-1)
131 auto it = std::find_if(m_roleValues.begin(), m_roleValues.end(), [role](
const RoleAndValue &v) {
132 return v.first == role;
134 if (it != m_roleValues.end()) {
137 m_roleValues.push_back({role, value});
143 , m_ungrouped(new Group({}, 0,
this))
144 , m_argumentHints(
new Group(
i18n(
"Argument-hints"), -1,
this))
145 , m_bestMatches(
new Group(
i18n(
"Best matches"), BestMatchesProperty,
this))
146 , m_emptyGroups({m_ungrouped, m_argumentHints, m_bestMatches})
148 m_updateBestMatchesTimer =
new QTimer(
this);
149 m_updateBestMatchesTimer->setSingleShot(
true);
150 connect(m_updateBestMatchesTimer, &
QTimer::timeout,
this, &KateCompletionModel::updateBestMatches);
152 m_groupHash.insert(0, m_ungrouped);
153 m_groupHash.insert(-1, m_argumentHints);
154 m_groupHash.insert(BestMatchesProperty, m_argumentHints);
159KateCompletionModel::~KateCompletionModel()
161 clearCompletionModels();
162 delete m_argumentHints;
164 delete m_bestMatches;
167QTreeView *KateCompletionModel::treeView()
const
169 return view()->completionWidget()->treeView();
174 if (!hasCompletionModel() || !index.isValid()) {
178 if (role == InternalRole::IsNonEmptyGroup) {
179 auto group = groupForIndex(index);
180 return group && !group->isEmpty;
184 if (!hasGroups() || groupOfParent(index)) {
187 for (
const auto &list : m_columnMerges) {
188 if (
size_t(index.column()) < c + list.
size()) {
191 }
else if (list.
size() == 1 && list.
front() == CodeCompletionModel::Scope) {
202 for (
int column : m_columnMerges[index.column()]) {
213 for (
int column : m_columnMerges[index.column()]) {
217 return QVariant(CodeCompletionModel::CustomHighlighting);
227 const auto &columns = m_columnMerges[index.column()];
228 strings.
reserve(columns.size());
229 for (
int column : columns) {
236 highlights.
reserve(columns.size());
237 for (
int column : columns) {
241 return mergeCustomHighlighting(strings, highlights, 0);
253 Group *g = groupForIndex(index);
255 if (g && (!g->isEmpty)) {
258 if (!index.column()) {
264 if (!index.column()) {
265 QFont f = view()->renderer()->currentFont();
283 if (!index.isValid()) {
286 Group *g = groupOfParent(index);
287 if (!g || g->filtered.size() < (
size_t)index.row()) {
300 for (
const Item &item : m_argumentHints->filtered) {
301 const ModelRow &row(item.sourceRow());
302 if (realIndex.
model() != row.first) {
306 QModelIndex hintIndex = row.second;
317 int m = matchQuality.
toInt();
324 if (m_argumentHints->filtered.empty()) {
327 int m = matchQuality.
toInt();
339 if (!hasCompletionModel() || !index.isValid()) {
343 if (!hasGroups() || groupOfParent(index)) {
355KTextEditor::ViewPrivate *KateCompletionModel::view()
const
357 return widget()->view();
360int KateCompletionModel::columnCount(
const QModelIndex &)
const
365KateCompletionModel::ModelRow KateCompletionModel::modelRowPair(
const QModelIndex &index)
367 return qMakePair(
static_cast<CodeCompletionModel *
>(
const_cast<QAbstractItemModel *
>(index.model())), index);
370bool KateCompletionModel::hasChildren(
const QModelIndex &parent)
const
372 if (!hasCompletionModel()) {
376 if (!parent.isValid()) {
381 return !m_ungrouped->filtered.empty();
384 if (parent.column() != 0) {
392 if (Group *g = groupForIndex(parent)) {
393 return !g->filtered.empty();
401 if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) {
402 return QModelIndex();
405 if (parent.isValid() || !hasGroups()) {
406 if (parent.isValid() && parent.column() != 0) {
407 return QModelIndex();
410 Group *g = groupForIndex(parent);
413 return QModelIndex();
416 if (row >= (
int)g->filtered.size()) {
418 return QModelIndex();
425 if (
size_t(row) >= m_rowTable.size()) {
427 return QModelIndex();
434bool KateCompletionModel::hasIndex(
int row,
int column,
const QModelIndex &parent)
const
436 if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) {
440 if (parent.isValid() || !hasGroups()) {
441 if (parent.isValid() && parent.column() != 0) {
445 Group *g = groupForIndex(parent);
447 if (row >= (
int)g->filtered.size()) {
454 if (
size_t(row) >= m_rowTable.size()) {
461QModelIndex KateCompletionModel::indexForRow(Group *g,
int row)
const
463 if (row < 0 || row >= (
int)g->filtered.size()) {
464 return QModelIndex();
470QModelIndex KateCompletionModel::indexForGroup(Group *g)
const
473 return QModelIndex();
476 auto it = std::find(m_rowTable.begin(), m_rowTable.end(), g);
477 if (it == m_rowTable.end()) {
478 return QModelIndex();
480 int row = std::distance(m_rowTable.begin(), it);
484void KateCompletionModel::clearGroups()
486 m_ungrouped->clear();
487 m_argumentHints->clear();
488 m_bestMatches->clear();
491 m_rowTable.erase(std::remove_if(m_rowTable.begin(),
494 return (g == m_ungrouped) || (g == m_argumentHints) || (g == m_bestMatches);
498 m_emptyGroups.erase(std::remove_if(m_emptyGroups.begin(),
501 return (g == m_ungrouped) || (g == m_argumentHints) || (g == m_bestMatches);
503 m_emptyGroups.end());
505 qDeleteAll(m_rowTable);
506 qDeleteAll(m_emptyGroups);
508 m_emptyGroups.clear();
510 m_customGroupHash.clear();
512 m_emptyGroups.insert(m_emptyGroups.end(), {m_ungrouped, m_argumentHints, m_bestMatches});
514 m_groupHash.insert(0, m_ungrouped);
515 m_groupHash.insert(-1, m_argumentHints);
516 m_groupHash.insert(BestMatchesProperty, m_bestMatches);
519KateCompletionModel::GroupSet KateCompletionModel::createItems(
const HierarchicalModelHandler &_handler,
const QModelIndex &i,
bool notifyModel)
521 HierarchicalModelHandler handler(_handler);
527 ret.insert(createItem(handler, i, notifyModel));
531 for (
int a = 0; a < model->
rowCount(i); a++) {
532 ret.merge(createItems(handler, model->
index(a, 0, i), notifyModel));
539KateCompletionModel::GroupSet KateCompletionModel::deleteItems(
const QModelIndex &i)
547 g->removeItem(ModelRow(
const_cast<CodeCompletionModel *
>(
static_cast<const CodeCompletionModel *
>(i.
model())), i));
550 for (
int a = 0; a < i.
model()->rowCount(i); a++) {
551 ret.merge(deleteItems(i.
model()->
index(a, 0, i)));
558void KateCompletionModel::createGroups()
565 bool has_groups =
false;
567 for (CodeCompletionModel *sourceModel : std::as_const(m_completionModels)) {
568 has_groups |= sourceModel->hasGroups();
569 for (
int i = 0; i < sourceModel->rowCount(); ++i) {
570 groups.merge(createItems(HierarchicalModelHandler(sourceModel), sourceModel->index(i, 0)));
576 for (
auto g : groups) {
580 std::sort(g->filtered.begin(), g->filtered.end(), [
this](
const Item &l,
const Item &r) {
581 return l.lessThan(this, r);
585 m_hasGroups = has_groups;
589 for (Group *g : m_rowTable) {
593 for (Group *g : m_emptyGroups) {
597 makeGroupItemsUnique();
603KateCompletionModel::Group *KateCompletionModel::createItem(
const HierarchicalModelHandler &handler,
const QModelIndex &sourceIndex,
bool notifyModel)
612 if (argumentHintDepth) {
615 QString customGroup = handler.customGroup();
616 if (!customGroup.
isNull() && m_hasGroups) {
617 if (m_customGroupHash.contains(customGroup)) {
618 g = m_customGroupHash[customGroup];
620 g =
new Group(customGroup, 0,
this);
621 g->customSortingKey = handler.customGroupingKey();
622 m_emptyGroups.push_back(g);
623 m_customGroupHash.insert(customGroup, g);
626 g = fetchGroup(completionFlags, handler.hasHierarchicalRoles());
630 Item item = Item(g != m_argumentHints,
this, handler, ModelRow(handler.model(), sourceIndex));
632 if (g != m_argumentHints) {
636 g->addItem(item, notifyModel);
641void KateCompletionModel::slotRowsInserted(
const QModelIndex &parent,
int start,
int end)
643 HierarchicalModelHandler handler(
static_cast<CodeCompletionModel *
>(
sender()));
644 if (parent.isValid()) {
645 handler.collectRoles(parent);
648 GroupSet affectedGroups;
649 for (
int i =
start; i <=
end; ++i) {
650 affectedGroups.merge(createItems(handler, handler.model()->
index(i, 0, parent),
true));
653 for (
auto g : affectedGroups) {
654 hideOrShowGroup(g,
true);
658void KateCompletionModel::slotRowsRemoved(
const QModelIndex &parent,
int start,
int end)
660 CodeCompletionModel *source =
static_cast<CodeCompletionModel *
>(
sender());
662 GroupSet affectedGroups;
663 for (
int i =
start; i <=
end; ++i) {
664 QModelIndex index = source->
index(i, 0, parent);
665 affectedGroups.merge(deleteItems(index));
668 for (
auto g : affectedGroups) {
669 hideOrShowGroup(g,
true);
673KateCompletionModel::Group *KateCompletionModel::fetchGroup(
int attribute,
bool forceGrouping)
675 Q_UNUSED(forceGrouping);
682 int groupingAttribute = groupingAttributes(attribute);
685 if (m_groupHash.contains(groupingAttribute)) {
686 return m_groupHash.value(groupingAttribute);
694 if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) {
695 st = QStringLiteral(
"Global");
696 }
else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) {
697 st = QStringLiteral(
"Namespace");
698 }
else if (attribute & KTextEditor::CodeCompletionModel::LocalScope) {
699 st = QStringLiteral(
"Local");
704 if (attribute & KTextEditor::CodeCompletionModel::Public) {
705 at = QStringLiteral(
"Public");
706 }
else if (attribute & KTextEditor::CodeCompletionModel::Protected) {
707 at = QStringLiteral(
"Protected");
708 }
else if (attribute & KTextEditor::CodeCompletionModel::Private) {
709 at = QStringLiteral(
"Private");
714 title.
append(QLatin1String(
", "));
720 Group *ret =
new Group(title, attribute,
this);
721 ret->scope = QString();
723 m_emptyGroups.push_back(ret);
724 m_groupHash.insert(groupingAttribute, ret);
729KateCompletionModel::Group *KateCompletionModel::groupForIndex(
const QModelIndex &index)
const
731 if (!index.isValid()) {
739 if (groupOfParent(index)) {
743 if (
size_t(index.row()) >= m_rowTable.size()) {
747 return m_rowTable[index.row()];
752 if (!index.isValid()) {
753 return QModelIndex();
756 if (Group *g = groupOfParent(index)) {
758 Q_ASSERT(g == m_ungrouped);
759 return QModelIndex();
762 auto it = std::find(m_rowTable.begin(), m_rowTable.end(), g);
763 if (it == m_rowTable.end()) {
764 qCWarning(LOG_KTE) <<
"Couldn't find parent for index" << index;
765 return QModelIndex();
767 int row = std::distance(m_rowTable.begin(), it);
771 return QModelIndex();
774int KateCompletionModel::rowCount(
const QModelIndex &parent)
const
776 if (!parent.isValid()) {
779 return m_rowTable.size();
782 return m_ungrouped->filtered.size();
786 if (parent.column() > 0) {
791 Group *g = groupForIndex(parent);
799 return g->filtered.size();
808 if (Group *g = groupOfParent(proxyIndex)) {
809 if (std::find(m_rowTable.begin(), m_rowTable.end(), g) == m_rowTable.end()) {
810 qWarning() << Q_FUNC_INFO <<
"Stale proxy index for which there is no group";
814 if (proxyIndex.
row() >= 0 && proxyIndex.
row() < (
int)g->filtered.size()) {
815 ModelRow source = g->filtered[proxyIndex.
row()].sourceRow();
816 return source.second.sibling(source.second.row(), proxyIndex.
column());
818 qCDebug(LOG_KTE) <<
"Invalid proxy-index";
832 return index(m_ungrouped->rowOf(modelRowPair(sourceIndex)), sourceIndex.
column(),
QModelIndex());
835 for (Group *g : m_rowTable) {
836 int row = g->rowOf(modelRowPair(sourceIndex));
838 return index(row, sourceIndex.
column(), indexForGroup(g));
843 for (Group *g : m_emptyGroups) {
844 int row = g->rowOf(modelRowPair(sourceIndex));
846 return index(row, sourceIndex.
column(), indexForGroup(g));
857 m_currentMatch = currentMatch;
860 changeCompletions(m_ungrouped);
862 for (Group *g : m_rowTable) {
863 if (g != m_argumentHints) {
864 changeCompletions(g);
867 for (Group *g : m_emptyGroups) {
868 if (g != m_argumentHints) {
869 changeCompletions(g);
880QString KateCompletionModel::commonPrefixInternal(
const QString &forcePrefix)
const
884 std::vector<Group *> groups = m_rowTable;
885 groups.push_back(m_ungrouped);
887 for (Group *g : groups) {
888 for (
const Item &item : g->filtered) {
889 uint startPos = currentCompletion(item.sourceRow().first).length();
890 const QString candidate = item.name().mid(startPos);
924 Group *g = m_ungrouped;
926 g = groupOfParent(selectedIndex);
929 if (g && selectedIndex.
row() < (
int)g->filtered.size()) {
931 Item item = g->filtered[selectedIndex.
row()];
932 int matchLength = currentCompletion(item.sourceRow().first).length();
940void KateCompletionModel::changeCompletions(Group *g)
945 std::remove_copy_if(g->prefilter.begin(), g->prefilter.end(), std::back_inserter(g->filtered), [
this](
Item &item) {
946 return !item.match(this);
949 hideOrShowGroup(g,
false);
952int KateCompletionModel::Group::orderNumber()
const
954 if (
this == model->m_ungrouped) {
958 if (customSortingKey != -1) {
959 return customSortingKey;
962 if (attribute & BestMatchesProperty) {
966 if (attribute & KTextEditor::CodeCompletionModel::LocalScope) {
968 }
else if (attribute & KTextEditor::CodeCompletionModel::Public) {
970 }
else if (attribute & KTextEditor::CodeCompletionModel::Protected) {
972 }
else if (attribute & KTextEditor::CodeCompletionModel::Private) {
974 }
else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) {
976 }
else if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) {
983bool KateCompletionModel::Group::orderBefore(Group *other)
const
985 return orderNumber() < other->orderNumber();
988void KateCompletionModel::hideOrShowGroup(Group *g,
bool notifyModel)
990 if (g == m_argumentHints) {
991 Q_EMIT argumentHintsChanged();
992 m_updateBestMatchesTimer->start(200);
997 if (g->filtered.empty()) {
1000 auto it = std::find(m_rowTable.begin(), m_rowTable.end(), g);
1002 if (it != m_rowTable.end()) {
1003 int row = std::distance(m_rowTable.begin(), it);
1004 if (hasGroups() && notifyModel) {
1007 m_rowTable.erase(it);
1008 if (hasGroups() && notifyModel) {
1011 m_emptyGroups.push_back(g);
1013 qCWarning(LOG_KTE) <<
"Group " << g <<
" not found in row table!!";
1018 if (!g->filtered.empty()) {
1023 for (
size_t a = 0; a < m_rowTable.size(); a++) {
1024 if (g->orderBefore(m_rowTable[a])) {
1038 m_rowTable.insert(m_rowTable.begin() + row, g);
1042 m_emptyGroups.erase(std::remove(m_emptyGroups.begin(), m_emptyGroups.end(), g), m_emptyGroups.end());
1053 if (groupOfParent(index)) {
1060void KateCompletionModel::slotModelReset()
1067void KateCompletionModel::debugStats()
1070 qCDebug(LOG_KTE) <<
"Model groupless, " << m_ungrouped->filtered.size() <<
" items.";
1072 qCDebug(LOG_KTE) <<
"Model grouped (" << m_rowTable.size() <<
" groups):";
1073 for (Group *g : m_rowTable) {
1074 qCDebug(LOG_KTE) <<
"Group" << g <<
"count" << g->filtered.size();
1079bool KateCompletionModel::hasCompletionModel()
const
1081 return !m_completionModels.empty();
1084int KateCompletionModel::translateColumn(
int sourceColumn)
const
1086 if (m_columnMerges.empty()) {
1087 return sourceColumn;
1104 for (
const auto &list : m_columnMerges) {
1105 for (
int column : list) {
1106 if (column == sourceColumn) {
1115int KateCompletionModel::groupingAttributes(
int attribute)
const
1119 if (countBits(attribute & ScopeTypeMask) > 1) {
1120 qCWarning(LOG_KTE) <<
"Invalid completion model metadata: more than one scope type modifier provided.";
1122 if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) {
1123 ret |= KTextEditor::CodeCompletionModel::GlobalScope;
1124 }
else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) {
1125 ret |= KTextEditor::CodeCompletionModel::NamespaceScope;
1126 }
else if (attribute & KTextEditor::CodeCompletionModel::LocalScope) {
1127 ret |= KTextEditor::CodeCompletionModel::LocalScope;
1130 if (countBits(attribute & AccessTypeMask) > 1) {
1131 qCWarning(LOG_KTE) <<
"Invalid completion model metadata: more than one access type modifier provided.";
1133 if (attribute & KTextEditor::CodeCompletionModel::Public) {
1134 ret |= KTextEditor::CodeCompletionModel::Public;
1135 }
else if (attribute & KTextEditor::CodeCompletionModel::Protected) {
1136 ret |= KTextEditor::CodeCompletionModel::Protected;
1137 }
else if (attribute & KTextEditor::CodeCompletionModel::Private) {
1138 ret |= KTextEditor::CodeCompletionModel::Private;
1144int KateCompletionModel::countBits(
int value)
1147 for (
int i = 1; i; i <<= 1) {
1156KateCompletionModel::Item::Item(
bool doInitialMatch, KateCompletionModel *m,
const HierarchicalModelHandler &handler, ModelRow sr)
1158 , matchCompletion(StartsWithMatch)
1159 , m_haveExactMatch(false)
1164 QModelIndex nameSibling = sr.second.sibling(sr.second.row(), CodeCompletionModel::Name);
1167 if (doInitialMatch) {
1178 if (m_unimportant && !rhs.m_unimportant) {
1182 if (!m_unimportant && rhs.m_unimportant) {
1186 if (matchCompletion < rhs.matchCompletion) {
1190 if (matchCompletion > rhs.matchCompletion) {
1194 ret = inheritanceDepth - rhs.inheritanceDepth;
1197 auto it = model->m_currentMatch.
constFind(rhs.m_sourceRow.first);
1198 if (it != model->m_currentMatch.
cend()) {
1199 const QString &
filter = it.value();
1201 bool rhsStartsWithFilter = rhs.m_nameColumn.startsWith(filter,
Qt::CaseSensitive);
1203 if (thisStartWithFilter && !rhsStartsWithFilter) {
1206 if (rhsStartsWithFilter && !thisStartWithFilter) {
1219 ret = m_sourceRow.second.row() - rhs.m_sourceRow.second.row();
1225void KateCompletionModel::Group::addItem(
const Item &i,
bool notifyModel)
1228 notifyModel =
false;
1231 QModelIndex groupIndex;
1233 groupIndex = model->indexForGroup(
this);
1237 auto comp = [
this](
const Item &
left,
const Item &
right) {
1238 return left.lessThan(model, right);
1240 prefilter.insert(std::upper_bound(prefilter.begin(), prefilter.end(), i, comp), i);
1242 prefilter.push_back(i);
1247 auto comp = [
this](
const Item &
left,
const Item &
right) {
1248 return left.lessThan(model, right);
1251 const auto rowNumber = it -
filtered.begin();
1265bool KateCompletionModel::Group::removeItem(
const ModelRow &row)
1267 for (
size_t pi = 0; pi < prefilter.size(); ++pi) {
1268 if (prefilter[pi].sourceRow() == row) {
1269 int index = rowOf(row);
1275 prefilter.erase(prefilter.begin() + pi);
1289KateCompletionModel::Group::Group(
const QString &title,
int attribute, KateCompletionModel *m)
1291 , attribute(attribute)
1293 , title(QLatin1Char(
' ') + title)
1295 , customSortingKey(-1)
1300void KateCompletionModel::Group::resort()
1302 auto comp = [
this](
const Item &
left,
const Item &
right) {
1303 return left.lessThan(model, right);
1305 std::stable_sort(filtered.begin(), filtered.end(), comp);
1306 model->hideOrShowGroup(
this);
1309void KateCompletionModel::resort()
1311 for (Group *g : m_rowTable) {
1315 for (Group *g : m_emptyGroups) {
1320 updateBestMatches();
1323void KateCompletionModel::Group::clear()
1330uint KateCompletionModel::filteredItemCount()
const
1333 for (Group *group : m_rowTable) {
1334 ret += group->filtered.size();
1344 bool doHide =
false;
1347 for (Group *group : m_rowTable) {
1348 for (
const Item &item : group->filtered) {
1349 if (item.haveExactMatch()) {
1357 && iface3->
matchingItem(item.sourceRow().second) == KTextEditor::CodeCompletionModelControllerInterface::HideListIfAutomaticInvocation) {
1362 hideModel = item.sourceRow().first;
1370 for (Group *group : m_rowTable) {
1371 for (
const Item &item : group->filtered) {
1372 if (item.sourceRow().first != hideModel) {
1387bool KateCompletionModel::matchesAbbreviation(
const QString &word,
const QString &typed,
int &score)
1396 const int firstLetter = [&word] {
1397 for (
auto it = word.
cbegin(); it != word.
cend(); ++it) {
1399 return int(it - word.
cbegin());
1404 QStringView wordView = word;
1405 wordView = wordView.
mid(firstLetter);
1407 if (toLower(wordView.
at(0)) != toLower(typed.
at(0))) {
1416static inline bool containsAtWordBeginning(
const QString &word,
const QString &typed)
1422 for (
int i = 1; i < word.
size(); i++) {
1426 const QChar c = word.
at(i);
1427 const QChar prev = word.
at(i - 1);
1428 if (!(prev == QLatin1Char(
'_') || (c.
isUpper() && !prev.
isUpper()))) {
1436 if (word.
size() - i < typed.
size()) {
1443KateCompletionModel::Item::MatchType KateCompletionModel::Item::match(KateCompletionModel *model)
1445 const QString
match = model->currentCompletion(m_sourceRow.first);
1447 m_haveExactMatch =
false;
1450 if (
match.isEmpty()) {
1451 return PerfectMatch;
1453 if (m_nameColumn.isEmpty()) {
1457 matchCompletion = (m_nameColumn.startsWith(
match) ? StartsWithMatch : NoMatch);
1459 if (matchCompletion == NoMatch && !m_nameColumn.isEmpty() && !
match.isEmpty()) {
1462 if (matchesAbbreviation(m_nameColumn,
match, score)) {
1463 inheritanceDepth -= score;
1464 matchCompletion = AbbreviationMatch;
1468 if (matchCompletion == NoMatch) {
1473 if (containsAtWordBeginning(m_nameColumn,
match)) {
1474 matchCompletion = ContainsMatch;
1478 if (matchCompletion &&
match.length() == m_nameColumn.length()) {
1479 matchCompletion = PerfectMatch;
1480 m_haveExactMatch =
true;
1483 return matchCompletion;
1486bool KateCompletionModel::Item::isVisible()
const
1488 return matchCompletion;
1491const KateCompletionModel::ModelRow &KateCompletionModel::Item::sourceRow()
const
1496QString KateCompletionModel::currentCompletion(KTextEditor::CodeCompletionModel *model)
const
1498 return m_currentMatch.value(model);
1501void KateCompletionModel::addCompletionModel(KTextEditor::CodeCompletionModel *model)
1503 if (m_completionModels.contains(model)) {
1507 m_completionModels.push_back(model);
1515 [
this](
const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QList<int> &roles) {
1523void KateCompletionModel::setCompletionModel(KTextEditor::CodeCompletionModel *model)
1525 clearCompletionModels();
1526 addCompletionModel(model);
1529void KateCompletionModel::setCompletionModels(
const QList<KTextEditor::CodeCompletionModel *> &models)
1534 clearCompletionModels();
1536 m_completionModels = models;
1538 for (KTextEditor::CodeCompletionModel *model : models) {
1545 [
this](
const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QList<int> &roles) {
1554QList<KTextEditor::CodeCompletionModel *> KateCompletionModel::completionModels()
const
1556 return m_completionModels;
1559void KateCompletionModel::removeCompletionModel(CodeCompletionModel *model)
1561 if (!model || !m_completionModels.contains(model)) {
1565 bool willCreateGroups = (m_completionModels.size() - 1) > 0;
1567 if (!willCreateGroups) {
1570 m_currentMatch.remove(model);
1576 m_completionModels.removeAll(model);
1577 if (!willCreateGroups) {
1581 if (willCreateGroups) {
1587void KateCompletionModel::makeGroupItemsUnique(
bool onlyFiltered)
1589 struct FilterItems {
1590 FilterItems(KateCompletionModel &model,
const QList<KTextEditor::CodeCompletionModel *> &needShadowing)
1592 , m_needShadowing(needShadowing)
1596 QHash<QString, CodeCompletionModel *> had;
1597 KateCompletionModel &m_model;
1598 const QList<KTextEditor::CodeCompletionModel *> &m_needShadowing;
1600 void filter(std::vector<Item> &items)
1602 std::vector<Item> temp;
1603 temp.reserve(items.size());
1604 for (
const Item &item : items) {
1606 if (it != had.
constEnd() && *it != item.sourceRow().first && m_needShadowing.
contains(item.sourceRow().first)) {
1610 had.
insert(item.name(), item.sourceRow().first);
1611 temp.push_back(item);
1616 void filter(Group *group,
bool onlyFiltered)
1618 if (group->prefilter.size() == group->filtered.size()) {
1621 if (!onlyFiltered) {
1622 group->prefilter = group->filtered;
1627 if (!onlyFiltered) {
1628 filter(group->prefilter);
1632 if (group->filtered.empty()) {
1633 m_model.hideOrShowGroup(group);
1638 QList<KTextEditor::CodeCompletionModel *> needShadowing;
1639 for (KTextEditor::CodeCompletionModel *model : std::as_const(m_completionModels)) {
1646 if (needShadowing.
isEmpty()) {
1650 FilterItems
filter(*
this, needShadowing);
1652 filter.filter(m_ungrouped, onlyFiltered);
1654 for (Group *group : m_rowTable) {
1655 filter.filter(group, onlyFiltered);
1660void KateCompletionModel::updateBestMatches()
1665 int maxMatches = 300;
1667 m_updateBestMatchesTimer->stop();
1669 typedef QMultiMap<int, QPair<int, ModelRow>> BestMatchMap;
1670 BestMatchMap matches;
1674 QMultiMap<int, int> rowsForQuality;
1677 for (
const Item &item : m_ungrouped->filtered) {
1678 ModelRow source = item.sourceRow();
1685 rowsForQuality.
insert(quality, row);
1691 if (maxMatches < 0) {
1696 if (!rowsForQuality.
isEmpty()) {
1698 QSet<int> movedToFront;
1699 std::vector<Item> newFiltered;
1700 newFiltered.reserve(rowsForQuality.
size());
1703 newFiltered.push_back(m_ungrouped->filtered[it.value()]);
1704 movedToFront.
insert(it.value());
1706 std::reverse(newFiltered.begin(), newFiltered.end());
1708 int size = m_ungrouped->filtered.size();
1709 for (
int a = 0; a < size; ++a) {
1711 newFiltered.push_back(m_ungrouped->filtered[a]);
1714 m_ungrouped->filtered.swap(newFiltered);
1720 for (Group *g : m_rowTable) {
1721 if (g == m_bestMatches) {
1724 for (
int a = 0; a < (int)g->filtered.size(); a++) {
1725 ModelRow source = g->filtered[a].sourceRow();
1734 matches.insert(quality, qMakePair(v.
toInt(), g->filtered[a].sourceRow()));
1739 if (maxMatches < 0) {
1743 if (maxMatches < 0) {
1752 BestMatchMap::const_iterator it = matches.
constEnd();
1753 while (it != matches.constBegin()) {
1756 matchesSum += (*it).first;
1757 if (cnt > matchesSum / cnt) {
1762 m_bestMatches->filtered.clear();
1764 it = matches.constEnd();
1766 while (it != matches.constBegin() && cnt > 0) {
1770 m_bestMatches->filtered.push_back(Item(
true,
this, HierarchicalModelHandler((*it).second.first), (*it).second));
1773 hideOrShowGroup(m_bestMatches);
1779 int rc = widget()->argumentHintModel()->rowCount(
QModelIndex());
1786 QModelIndex end = widget()->argumentHintModel()->index(rc - 1, 0);
1788 widget()->argumentHintModel()->emitDataChanged(
start, end);
1791void KateCompletionModel::clearCompletionModels()
1793 if (m_completionModels.
empty()) {
1798 for (CodeCompletionModel *model : std::as_const(m_completionModels)) {
1802 m_completionModels.clear();
1804 m_currentMatch.clear();
1810#include "moc_katecompletionmodel.cpp"
Controller interface for a CodeCompletionModel.
virtual MatchReaction matchingItem(const QModelIndex &matched)
Called whenever an item in the completion-list perfectly matches the current filter text.
virtual bool shouldHideItemsWithEqualNames() const
When multiple completion models are used at the same time, it may happen that multiple models add ite...
An item model for providing code completion, and meta information for enhanced presentation.
ExtraItemDataRoles
Meta information is passed through extra {Qt::ItemDataRole}s.
@ InheritanceDepth
Returns the inheritance depth of the completion.
@ CompletionRole
The model should return a set of CompletionProperties.
@ GroupRole
Using this Role, it is possible to greatly optimize the time needed to process very long completion-l...
@ SetMatchContext
Is requested before MatchQuality(..) is requested.
@ HighlightingMethod
Define which highlighting method will be used:
@ UnimportantItemRole
Return a nonzero value here to enforce sorting the item at the end of the list.
@ BestMatchesCount
This will be requested for each item to ask whether it should be included in computing a best-matches...
@ ArgumentHintDepth
Is this completion-item an argument-hint?
@ MatchQuality
If requested, your model should try to determine whether the completion in question is a suitable mat...
@ CustomHighlight
Allows an item to provide custom highlighting.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Reimplemented from QAbstractItemModel::index().
This class has the responsibility for filtering, sorting, and manipulating code completion data provi...
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Does not request data from index, this only returns local data like highlighting for expanded rows an...
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const
Maps from this display-model into the appropriate source code-completion model.
bool indexIsItem(const QModelIndex &index) const override
Should return true if the given row should be painted like a contained item(as opposed to label-rows ...
bool shouldMatchHideCompletionList() const
Returns whether one of the filtered items exactly matches its completion string.
QString commonPrefix(QModelIndex selectedIndex) const
Returns a common prefix for all current visible completion entries If there is no common prefix,...
int contextMatchQuality(const QModelIndex &index) const override
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
Maps from an index in a source-model to the index of the item in this display-model.
void rowSelected(const QModelIndex &row) const
Q_SCRIPTABLE QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
QAbstractItemModel(QObject *parent)
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
QModelIndex createIndex(int row, int column, const void *ptr) const const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const
virtual int rowCount(const QModelIndex &parent) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
const QColor & color() const const
bool isLower(char32_t ucs4)
bool isUpper(char32_t ucs4)
char32_t toLower(char32_t ucs4)
void setBold(bool enable)
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
iterator insert(const Key &key, const T &value)
bool contains(const AT &value) const const
bool isEmpty() const const
void push_back(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
const_iterator cend() const const
const_iterator constFind(const Key &key) const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
size_type size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
T qobject_cast(QObject *object)
QObject * sender() const const
const QBrush & toolTipText() const const
bool isVisible() const const
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
void reserve(qsizetype size)
QString & append(QChar ch)
const QChar at(qsizetype position) const const
const_iterator cbegin() const const
const_iterator cend() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
const_iterator constEnd() const const
bool isEmpty() const const
bool isNull() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QStringView mid(qsizetype start, qsizetype length) const const
QChar at(qsizetype n) const const
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< typename qValueType< Iterator >::value_type > filtered(Iterator begin, Iterator end, KeepFunctor &&filterFunction)
bool canConvert() const const
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QList< QVariant > toList() const const
QString toString() const const
int userType() const const