File indexing completed on 2024-04-28 11:44:55
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"