File indexing completed on 2024-04-28 15:30:20
0001 /* 0002 SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org> 0003 SPDX-FileCopyrightText: 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #ifndef KATECOMPLETIONMODEL_H 0009 #define KATECOMPLETIONMODEL_H 0010 0011 #include <QAbstractProxyModel> 0012 #include <QList> 0013 #include <QPair> 0014 0015 #include <ktexteditor/codecompletionmodel.h> 0016 0017 #include "expandingtree/expandingwidgetmodel.h" 0018 #include <ktexteditor_export.h> 0019 0020 #include <set> 0021 0022 class KateCompletionWidget; 0023 class KateArgumentHintModel; 0024 namespace KTextEditor 0025 { 0026 class ViewPrivate; 0027 } 0028 class QWidget; 0029 class QTextEdit; 0030 class QTimer; 0031 class HierarchicalModelHandler; 0032 0033 /** 0034 * This class has the responsibility for filtering, sorting, and manipulating 0035 * code completion data provided by a CodeCompletionModel. 0036 * 0037 * @author Hamish Rodda <rodda@kde.org> 0038 */ 0039 class KTEXTEDITOR_EXPORT KateCompletionModel : public ExpandingWidgetModel 0040 { 0041 Q_OBJECT 0042 0043 public: 0044 enum InternalRole { 0045 IsNonEmptyGroup = KTextEditor::CodeCompletionModel::LastExtraItemDataRole + 1, 0046 }; 0047 0048 explicit KateCompletionModel(KateCompletionWidget *parent = nullptr); 0049 ~KateCompletionModel() override; 0050 0051 QList<KTextEditor::CodeCompletionModel *> completionModels() const; 0052 void clearCompletionModels(); 0053 void addCompletionModel(KTextEditor::CodeCompletionModel *model); 0054 void setCompletionModel(KTextEditor::CodeCompletionModel *model); 0055 void setCompletionModels(const QList<KTextEditor::CodeCompletionModel *> &models); 0056 void removeCompletionModel(KTextEditor::CodeCompletionModel *model); 0057 0058 KTextEditor::ViewPrivate *view() const; 0059 KateCompletionWidget *widget() const; 0060 0061 QString currentCompletion(KTextEditor::CodeCompletionModel *model) const; 0062 void setCurrentCompletion(QMap<KTextEditor::CodeCompletionModel *, QString> currentMatch); 0063 0064 int translateColumn(int sourceColumn) const; 0065 0066 /// Returns a common prefix for all current visible completion entries 0067 /// If there is no common prefix, extracts the next useful prefix for the selected index 0068 QString commonPrefix(QModelIndex selectedIndex) const; 0069 0070 void rowSelected(const QModelIndex &row) const; 0071 0072 bool indexIsItem(const QModelIndex &index) const override; 0073 0074 int columnCount(const QModelIndex &parent = QModelIndex()) const override; 0075 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 0076 Qt::ItemFlags flags(const QModelIndex &index) const override; 0077 bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; 0078 virtual bool hasIndex(int row, int column, const QModelIndex &parent = QModelIndex()) const; 0079 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 0080 0081 // Disabled in case of bugs, reenable once fully debugged. 0082 // virtual QMap<int, QVariant> itemData ( const QModelIndex & index ) const; 0083 QModelIndex parent(const QModelIndex &index) const override; 0084 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0085 0086 /// Maps from this display-model into the appropriate source code-completion model 0087 virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; 0088 0089 /// Maps from an index in a source-model to the index of the item in this display-model 0090 virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; 0091 0092 enum gm { ScopeType = 0x1, Scope = 0x2, AccessType = 0x4, ItemType = 0x8 }; 0093 0094 enum { // An own property that will be used to mark the best-matches group internally 0095 BestMatchesProperty = 2 * KTextEditor::CodeCompletionModel::LastProperty 0096 }; 0097 0098 Q_DECLARE_FLAGS(GroupingMethods, gm) 0099 0100 static const int ScopeTypeMask = 0x380000; 0101 static const int AccessTypeMask = 0x7; 0102 static const int ItemTypeMask = 0xfe0; 0103 0104 void debugStats(); 0105 0106 /// Returns whether one of the filtered items exactly matches its completion string 0107 bool shouldMatchHideCompletionList() const; 0108 0109 uint filteredItemCount() const; 0110 0111 protected: 0112 int contextMatchQuality(const QModelIndex &index) const override; 0113 0114 Q_SIGNALS: 0115 void expandIndex(const QModelIndex &index); 0116 // Emitted whenever something has changed about the group of argument-hints 0117 void argumentHintsChanged(); 0118 0119 private Q_SLOTS: 0120 void slotRowsInserted(const QModelIndex &parent, int start, int end); 0121 void slotRowsRemoved(const QModelIndex &parent, int start, int end); 0122 void slotModelReset(); 0123 0124 // Updates the best-matches group 0125 void updateBestMatches(); 0126 // Makes sure that the ungrouped group contains each item only once 0127 // Must only be called right after the group was created 0128 void makeGroupItemsUnique(bool onlyFiltered = false); 0129 0130 private: 0131 typedef QPair<KTextEditor::CodeCompletionModel *, QModelIndex> ModelRow; 0132 virtual int contextMatchQuality(const ModelRow &sourceRow) const; 0133 0134 QTreeView *treeView() const override; 0135 0136 friend class KateArgumentHintModel; 0137 KTEXTEDITOR_NO_EXPORT 0138 static ModelRow modelRowPair(const QModelIndex &index); 0139 0140 // Represents a source row; provides sorting method 0141 class KTEXTEDITOR_NO_EXPORT Item 0142 { 0143 public: 0144 Item(bool doInitialMatch, KateCompletionModel *model, const HierarchicalModelHandler &handler, ModelRow sourceRow); 0145 0146 bool isValid() const; 0147 // Returns true if the item is not filtered and matches the current completion string 0148 bool isVisible() const; 0149 0150 enum MatchType { NoMatch = 0, PerfectMatch, StartsWithMatch, AbbreviationMatch, ContainsMatch }; 0151 MatchType match(); 0152 0153 const ModelRow &sourceRow() const; 0154 0155 // Sorting operator 0156 bool operator<(const Item &rhs) const; 0157 0158 bool haveExactMatch() const 0159 { 0160 return m_haveExactMatch; 0161 } 0162 0163 void clearExactMatch() 0164 { 0165 m_haveExactMatch = false; 0166 } 0167 0168 QString name() const 0169 { 0170 return m_nameColumn; 0171 } 0172 0173 private: 0174 KateCompletionModel *model; 0175 ModelRow m_sourceRow; 0176 0177 QString m_nameColumn; 0178 0179 int inheritanceDepth; 0180 0181 // True when currently matching completion string 0182 MatchType matchCompletion; 0183 bool m_haveExactMatch; 0184 bool m_unimportant; 0185 }; 0186 0187 public: 0188 // Grouping and sorting of rows 0189 class Group 0190 { 0191 public: 0192 explicit Group(const QString &title, int attribute, KateCompletionModel *model); 0193 0194 void addItem(const Item &i, bool notifyModel = false); 0195 /// Removes the item specified by \a row. Returns true if a change was made to rows. 0196 bool removeItem(const ModelRow &row); 0197 void resort(); 0198 void clear(); 0199 // Returns whether this group should be ordered before other 0200 bool orderBefore(Group *other) const; 0201 // Returns a number that can be used for ordering 0202 int orderNumber() const; 0203 0204 /// Returns the row in the this group's filtered list of the given model-row in a source-model 0205 ///-1 if the item is not in the filtered list 0206 ///@todo Implement an efficient way of doing this map, that does _not_ iterate over all items! 0207 int rowOf(const ModelRow &item) 0208 { 0209 for (int a = 0; a < (int)filtered.size(); ++a) { 0210 if (filtered[a].sourceRow() == item) { 0211 return a; 0212 } 0213 } 0214 return -1; 0215 } 0216 0217 KateCompletionModel *model; 0218 int attribute; 0219 QString title, scope; 0220 std::vector<Item> filtered; 0221 std::vector<Item> prefilter; 0222 bool isEmpty; 0223 //-1 if none was set 0224 int customSortingKey; 0225 }; 0226 0227 typedef std::set<Group *> GroupSet; 0228 0229 bool hasGroups() const; 0230 0231 private: 0232 KTEXTEDITOR_NO_EXPORT 0233 QString commonPrefixInternal(const QString &forcePrefix) const; 0234 /// @note performs model reset 0235 KTEXTEDITOR_NO_EXPORT 0236 void createGroups(); 0237 /// Creates all sub-items of index i, or the item corresponding to index i. Returns the affected groups. 0238 /// i must be an index in the source model 0239 KTEXTEDITOR_NO_EXPORT 0240 GroupSet createItems(const HierarchicalModelHandler &, const QModelIndex &i, bool notifyModel = false); 0241 /// Deletes all sub-items of index i, or the item corresponding to index i. Returns the affected groups. 0242 /// i must be an index in the source model 0243 KTEXTEDITOR_NO_EXPORT 0244 GroupSet deleteItems(const QModelIndex &i); 0245 KTEXTEDITOR_NO_EXPORT 0246 Group *createItem(const HierarchicalModelHandler &, const QModelIndex &i, bool notifyModel = false); 0247 /// @note Make sure you're in a {begin,end}ResetModel block when calling this! 0248 KTEXTEDITOR_NO_EXPORT 0249 void clearGroups(); 0250 KTEXTEDITOR_NO_EXPORT 0251 void hideOrShowGroup(Group *g, bool notifyModel = false); 0252 /// When forceGrouping is enabled, all given attributes will be used for grouping, regardless of the completion settings. 0253 KTEXTEDITOR_NO_EXPORT 0254 Group *fetchGroup(int attribute, bool forceGrouping = false); 0255 // If this returns nonzero on an index, the index is the header of the returned group 0256 KTEXTEDITOR_NO_EXPORT 0257 Group *groupForIndex(const QModelIndex &index) const; 0258 inline Group *groupOfParent(const QModelIndex &child) const 0259 { 0260 return static_cast<Group *>(child.internalPointer()); 0261 } 0262 KTEXTEDITOR_NO_EXPORT 0263 QModelIndex indexForRow(Group *g, int row) const; 0264 KTEXTEDITOR_NO_EXPORT 0265 QModelIndex indexForGroup(Group *g) const; 0266 0267 enum changeTypes { Broaden, Narrow, Change }; 0268 0269 // Returns whether the model needs to be reset 0270 KTEXTEDITOR_NO_EXPORT 0271 void changeCompletions(Group *g); 0272 0273 KTEXTEDITOR_NO_EXPORT 0274 bool hasCompletionModel() const; 0275 0276 /// Removes attributes not used in grouping from the input \a attribute 0277 KTEXTEDITOR_NO_EXPORT 0278 int groupingAttributes(int attribute) const; 0279 KTEXTEDITOR_NO_EXPORT 0280 static int countBits(int value); 0281 0282 KTEXTEDITOR_NO_EXPORT 0283 void resort(); 0284 0285 static bool matchesAbbreviation(const QString &word, const QString &typed, int &score); 0286 // exported for completion_test 0287 0288 bool m_hasGroups = false; 0289 0290 // ### Runtime state 0291 // General 0292 QList<KTextEditor::CodeCompletionModel *> m_completionModels; 0293 QMap<KTextEditor::CodeCompletionModel *, QString> m_currentMatch; 0294 0295 // Column merging 0296 QList<QList<int>> m_columnMerges; 0297 0298 QTimer *m_updateBestMatchesTimer; 0299 0300 Group *m_ungrouped; 0301 Group *m_argumentHints; // The argument-hints will be passed on to another model, to be shown in another widget 0302 Group *m_bestMatches; // A temporary group used for holding the best matches of all visible items 0303 0304 // Storing the sorted order 0305 QList<Group *> m_rowTable; 0306 QList<Group *> m_emptyGroups; 0307 // Quick access to each specific group (if it exists) 0308 QMultiHash<int, Group *> m_groupHash; 0309 // Maps custom group-names to their specific groups 0310 QHash<QString, Group *> m_customGroupHash; 0311 0312 friend class CompletionTest; 0313 }; 0314 0315 Q_DECLARE_OPERATORS_FOR_FLAGS(KateCompletionModel::GroupingMethods) 0316 0317 #endif