File indexing completed on 2024-04-28 11:44:56

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