File indexing completed on 2024-04-28 15:30:18

0001 /*
0002     SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kateargumenthintmodel.h"
0008 
0009 #include "kateargumenthinttree.h"
0010 #include "katecompletiontree.h"
0011 #include "katecompletionwidget.h"
0012 #include "katepartdebug.h"
0013 
0014 #include <ktexteditor/codecompletionmodel.h>
0015 
0016 #include <QApplication>
0017 #include <QGridLayout>
0018 #include <QTextFormat>
0019 
0020 using namespace KTextEditor;
0021 
0022 void KateArgumentHintModel::clear()
0023 {
0024     m_rows.clear();
0025     clearExpanding();
0026 }
0027 
0028 QModelIndex KateArgumentHintModel::mapToSource(const QModelIndex &index) const
0029 {
0030     if (index.row() < 0 || index.row() >= m_rows.count()) {
0031         return QModelIndex();
0032     }
0033 
0034     if (m_rows[index.row()] < 0 || m_rows[index.row()] >= (int)group()->filtered.size()) {
0035         return QModelIndex();
0036     }
0037 
0038     KateCompletionModel::ModelRow source = group()->filtered[m_rows[index.row()]].sourceRow();
0039     if (!source.first) {
0040         qCDebug(LOG_KTE) << "KateArgumentHintModel::data: Row does not exist in source";
0041         return QModelIndex();
0042     }
0043 
0044     QModelIndex sourceIndex = source.second.sibling(source.second.row(), index.column());
0045 
0046     return sourceIndex;
0047 }
0048 
0049 void KateArgumentHintModel::parentModelReset()
0050 {
0051     clear();
0052     buildRows();
0053 }
0054 
0055 void KateArgumentHintModel::buildRows()
0056 {
0057     beginResetModel();
0058 
0059     m_rows.clear();
0060     QMap<int, QList<int>> m_depths; // Map each hint-depth to a list of functions of that depth
0061     for (int a = 0; a < (int)group()->filtered.size(); a++) {
0062         KateCompletionModel::ModelRow source = group()->filtered[a].sourceRow();
0063         QModelIndex sourceIndex = source.second.sibling(source.second.row(), 0);
0064         QVariant v = sourceIndex.data(CodeCompletionModel::ArgumentHintDepth);
0065         if (v.type() == QVariant::Int) {
0066             QList<int> &lst(m_depths[v.toInt()]);
0067             lst << a;
0068         }
0069     }
0070 
0071     for (QMap<int, QList<int>>::const_iterator it = m_depths.constBegin(); it != m_depths.constEnd(); ++it) {
0072         for (int row : *it) {
0073             m_rows.push_front(row); // Insert filtered in reversed order
0074         }
0075         m_rows.push_front(-it.key());
0076     }
0077 
0078     endResetModel();
0079 
0080     Q_EMIT contentStateChanged(!m_rows.isEmpty());
0081 }
0082 
0083 KateArgumentHintModel::KateArgumentHintModel(KateCompletionWidget *parent)
0084     : ExpandingWidgetModel(parent)
0085     , m_parent(parent)
0086 {
0087     connect(parent->model(), &KateCompletionModel::modelReset, this, &KateArgumentHintModel::parentModelReset);
0088     connect(parent->model(), &KateCompletionModel::argumentHintsChanged, this, &KateArgumentHintModel::parentModelReset);
0089 }
0090 
0091 QVariant KateArgumentHintModel::data(const QModelIndex &index, int role) const
0092 {
0093     if (index.row() < 0 || index.row() >= m_rows.count()) {
0094         // qCDebug(LOG_KTE) << "KateArgumentHintModel::data: index out of bound: " << index.row() << " total filtered: " << m_rows.count();
0095         return QVariant();
0096     }
0097 
0098     if (m_rows[index.row()] < 0) {
0099         // Show labels
0100         if (role == Qt::DisplayRole && index.column() == 0) {
0101             return QString(); // QString("Depth %1").arg(-m_rows[index.row()]);
0102         } else if (role == Qt::BackgroundRole) {
0103             return QApplication::palette().toolTipBase().color();
0104         } else if (role == Qt::ForegroundRole) {
0105             return QApplication::palette().toolTipText().color();
0106         } else {
0107             return QVariant();
0108         }
0109     }
0110 
0111     if (m_rows[index.row()] < 0 || m_rows[index.row()] >= (int)group()->filtered.size()) {
0112         qCDebug(LOG_KTE) << "KateArgumentHintModel::data: index out of bound: " << m_rows[index.row()] << " total filtered: " << (int)group()->filtered.size();
0113         return QVariant();
0114     }
0115 
0116     KateCompletionModel::ModelRow source = group()->filtered[m_rows[index.row()]].sourceRow();
0117     if (!source.first) {
0118         qCDebug(LOG_KTE) << "KateArgumentHintModel::data: Row does not exist in source";
0119         return QVariant();
0120     }
0121 
0122     if (index.column() == 0) {
0123         switch (role) {
0124         case Qt::DecorationRole: {
0125             // Show the expand-handle
0126             model()->cacheIcons();
0127 
0128             if (!isExpanded(index)) {
0129                 return QVariant(model()->m_collapsedIcon);
0130             } else {
0131                 return QVariant(model()->m_expandedIcon);
0132             }
0133         }
0134         case Qt::DisplayRole:
0135             // Ignore text in the first column(we create our own compound text in the second)
0136             return QVariant();
0137         }
0138     }
0139 
0140     QModelIndex sourceIndex = source.second.sibling(source.second.row(), index.column());
0141 
0142     if (!sourceIndex.isValid()) {
0143         qCDebug(LOG_KTE) << "KateArgumentHintModel::data: Source-index is not valid";
0144         return QVariant();
0145     }
0146 
0147     switch (role) {
0148     case Qt::DisplayRole: {
0149         // Construct the text
0150         QString totalText;
0151         for (int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++) {
0152             if (a != CodeCompletionModel::Scope) { // Skip the scope
0153                 totalText += source.second.sibling(source.second.row(), a).data(Qt::DisplayRole).toString() + QLatin1Char(' ');
0154             }
0155         }
0156 
0157         return QVariant(totalText);
0158     }
0159     case CodeCompletionModel::HighlightingMethod: {
0160         // Return that we are doing custom-highlighting of one of the sub-strings does it
0161         for (int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++) {
0162             QVariant method = source.second.sibling(source.second.row(), a).data(CodeCompletionModel::HighlightingMethod);
0163             if (method.type() == QVariant::Int && method.toInt() == CodeCompletionModel::CustomHighlighting) {
0164                 return QVariant(CodeCompletionModel::CustomHighlighting);
0165             }
0166         }
0167 
0168         return QVariant();
0169     }
0170     case CodeCompletionModel::CustomHighlight: {
0171         QStringList strings;
0172 
0173         // Collect strings
0174         for (int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++) {
0175             strings << source.second.sibling(source.second.row(), a).data(Qt::DisplayRole).toString();
0176         }
0177 
0178         QList<QVariantList> highlights;
0179 
0180         // Collect custom-highlightings
0181         for (int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++) {
0182             highlights << source.second.sibling(source.second.row(), a).data(CodeCompletionModel::CustomHighlight).toList();
0183         }
0184 
0185         // Replace invalid QTextFormats with match-quality color or yellow.
0186         for (QList<QVariantList>::iterator it = highlights.begin(); it != highlights.end(); ++it) {
0187             QVariantList &list(*it);
0188 
0189             for (int a = 2; a < list.count(); a += 3) {
0190                 if (list[a].canConvert<QTextFormat>()) {
0191                     QTextFormat f = list[a].value<QTextFormat>();
0192 
0193                     if (!f.isValid()) {
0194                         f = QTextFormat(QTextFormat::CharFormat);
0195                         uint color = matchColor(index);
0196 
0197                         if (color) {
0198                             f.setBackground(QBrush(color));
0199                         } else {
0200                             f.setBackground(Qt::yellow);
0201                         }
0202 
0203                         list[a] = QVariant(f);
0204                     }
0205                 }
0206             }
0207         }
0208 
0209         return mergeCustomHighlighting(strings, highlights, 1);
0210     }
0211     case Qt::DecorationRole: {
0212         // Redirect the decoration to the decoration of the item-column
0213         return source.second.sibling(source.second.row(), CodeCompletionModel::Icon).data(role);
0214     }
0215     }
0216 
0217     QVariant v = ExpandingWidgetModel::data(index, role);
0218     if (v.isValid()) {
0219         return v;
0220     } else {
0221         return sourceIndex.data(role);
0222     }
0223 }
0224 
0225 int KateArgumentHintModel::rowCount(const QModelIndex &parent) const
0226 {
0227     if (!parent.isValid()) {
0228         return m_rows.count();
0229     } else {
0230         return 0;
0231     }
0232 }
0233 
0234 int KateArgumentHintModel::columnCount(const QModelIndex & /*parent*/) const
0235 {
0236     return 2; // 2 Columns, one for the expand-handle, one for the signature
0237 }
0238 
0239 KateCompletionModel::Group *KateArgumentHintModel::group() const
0240 {
0241     return model()->m_argumentHints;
0242 }
0243 
0244 QModelIndex KateArgumentHintModel::index(int row, int column, const QModelIndex &parent) const
0245 {
0246     if (row < 0 || row > rowCount() || column < 0 || column > columnCount() || parent.isValid()) {
0247         return {};
0248     }
0249     return createIndex(row, column);
0250 }
0251 
0252 QModelIndex KateArgumentHintModel::parent(const QModelIndex & /*parent*/) const
0253 {
0254     return {};
0255 }
0256 
0257 KateCompletionModel *KateArgumentHintModel::model() const
0258 {
0259     return m_parent->model();
0260 }
0261 
0262 QTreeView *KateArgumentHintModel::treeView() const
0263 {
0264     return m_parent->argumentHintTree();
0265 }
0266 
0267 void KateArgumentHintModel::emitDataChanged(const QModelIndex &start, const QModelIndex &end)
0268 {
0269     Q_EMIT dataChanged(start, end);
0270 }
0271 
0272 bool KateArgumentHintModel::indexIsItem(const QModelIndex &index) const
0273 {
0274     return index.row() >= 0 && index.row() < m_rows.count() && m_rows[index.row()] >= 0;
0275 }
0276 
0277 int KateArgumentHintModel::contextMatchQuality(const QModelIndex &index) const
0278 {
0279     int row = index.row();
0280     if (row < 0 || row >= m_rows.count()) {
0281         return -1;
0282     }
0283 
0284     if (m_rows[row] < 0 || m_rows[row] >= (int)group()->filtered.size()) {
0285         return -1; // Probably a label
0286     }
0287 
0288     KateCompletionModel::ModelRow source = group()->filtered[m_rows[row]].sourceRow();
0289     if (!source.first) {
0290         return -1;
0291     }
0292 
0293     QModelIndex sourceIndex = source.second.sibling(source.second.row(), 0);
0294 
0295     if (!sourceIndex.isValid()) {
0296         return -1;
0297     }
0298 
0299     int depth = sourceIndex.data(CodeCompletionModel::ArgumentHintDepth).toInt();
0300 
0301     switch (depth) {
0302     case 1: {
0303         // This argument-hint is on the lowest level, match it with the currently selected item in the completion-widget
0304         QModelIndex row = m_parent->treeView()->currentIndex();
0305         if (!row.isValid()) {
0306             return -1;
0307         }
0308 
0309         QModelIndex selectedIndex = m_parent->model()->mapToSource(row);
0310         if (!selectedIndex.isValid()) {
0311             return -1;
0312         }
0313 
0314         if (selectedIndex.model() != sourceIndex.model()) {
0315             return -1; // We can only match items from the same source-model
0316         }
0317 
0318         sourceIndex.data(CodeCompletionModel::SetMatchContext);
0319 
0320         QVariant v = selectedIndex.data(CodeCompletionModel::MatchQuality);
0321         if (v.type() == QVariant::Int) {
0322             return v.toInt();
0323         }
0324     } break;
0325     default:
0326         // Do some other nice matching here in future
0327         break;
0328     }
0329 
0330     return -1;
0331 }
0332 
0333 #include "moc_kateargumenthintmodel.cpp"