File indexing completed on 2024-04-14 03:55:02

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 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     KTEXTEDITOR_EXPORT void addCompletionModel(KTextEditor::CodeCompletionModel *model);
0054     KTEXTEDITOR_EXPORT void setCompletionModel(KTextEditor::CodeCompletionModel *model);
0055     void setCompletionModels(const QList<KTextEditor::CodeCompletionModel *> &models);
0056     KTEXTEDITOR_EXPORT void removeCompletionModel(KTextEditor::CodeCompletionModel *model);
0057 
0058     KTextEditor::ViewPrivate *view() const;
0059     KateCompletionWidget *widget() const;
0060 
0061     KTEXTEDITOR_EXPORT 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     KTEXTEDITOR_EXPORT 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     static ModelRow modelRowPair(const QModelIndex &index);
0138 
0139     // Represents a source row; provides sorting method
0140     class Item
0141     {
0142     public:
0143         Item(bool doInitialMatch, KateCompletionModel *model, const HierarchicalModelHandler &handler, ModelRow sourceRow);
0144 
0145         // Returns true if the item is not filtered and matches the current completion string
0146         bool isVisible() const;
0147 
0148         enum MatchType { NoMatch = 0, PerfectMatch, StartsWithMatch, AbbreviationMatch, ContainsMatch };
0149         MatchType match(KateCompletionModel *model);
0150 
0151         const ModelRow &sourceRow() const;
0152 
0153         // Sorting operator
0154         bool lessThan(KateCompletionModel *model, const Item &rhs) const;
0155 
0156         bool haveExactMatch() const
0157         {
0158             return m_haveExactMatch;
0159         }
0160 
0161         QString name() const
0162         {
0163             return m_nameColumn;
0164         }
0165 
0166     private:
0167         ModelRow m_sourceRow;
0168 
0169         QString m_nameColumn;
0170 
0171         int inheritanceDepth;
0172 
0173         // True when currently matching completion string
0174         MatchType matchCompletion;
0175         bool m_haveExactMatch;
0176         bool m_unimportant;
0177     };
0178 
0179 public:
0180     // Grouping and sorting of rows
0181     class Group
0182     {
0183     public:
0184         explicit Group(const QString &title, int attribute, KateCompletionModel *model);
0185 
0186         void addItem(const Item &i, bool notifyModel = false);
0187         /// Removes the item specified by \a row.  Returns true if a change was made to rows.
0188         bool removeItem(const ModelRow &row);
0189         void resort();
0190         void clear();
0191         // Returns whether this group should be ordered before other
0192         bool orderBefore(Group *other) const;
0193         // Returns a number that can be used for ordering
0194         int orderNumber() const;
0195 
0196         /// Returns the row in the this group's filtered list of the given model-row in a source-model
0197         ///-1 if the item is not in the filtered list
0198         ///@todo Implement an efficient way of doing this map, that does _not_ iterate over all items!
0199         int rowOf(const ModelRow &item)
0200         {
0201             for (int a = 0; a < (int)filtered.size(); ++a) {
0202                 if (filtered[a].sourceRow() == item) {
0203                     return a;
0204                 }
0205             }
0206             return -1;
0207         }
0208 
0209         KateCompletionModel *model;
0210         int attribute;
0211         QString title, scope;
0212         std::vector<Item> filtered;
0213         std::vector<Item> prefilter;
0214         bool isEmpty;
0215         //-1 if none was set
0216         int customSortingKey;
0217     };
0218 
0219     typedef std::set<Group *> GroupSet;
0220 
0221     bool hasGroups() const
0222     {
0223         // qCDebug(LOG_KTE) << "m_groupHash.size()"<<m_groupHash.size();
0224         // qCDebug(LOG_KTE) << "m_rowTable.count()"<<m_rowTable.count();
0225         return m_hasGroups;
0226     }
0227 
0228 private:
0229     QString commonPrefixInternal(const QString &forcePrefix) const;
0230     /// @note performs model reset
0231     void createGroups();
0232     /// Creates all sub-items of index i, or the item corresponding to index i. Returns the affected groups.
0233     /// i must be an index in the source model
0234     GroupSet createItems(const HierarchicalModelHandler &, const QModelIndex &i, bool notifyModel = false);
0235     /// Deletes all sub-items of index i, or the item corresponding to index i. Returns the affected groups.
0236     /// i must be an index in the source model
0237     GroupSet deleteItems(const QModelIndex &i);
0238     Group *createItem(const HierarchicalModelHandler &, const QModelIndex &i, bool notifyModel = false);
0239     /// @note Make sure you're in a {begin,end}ResetModel block when calling this!
0240     void clearGroups();
0241     void hideOrShowGroup(Group *g, bool notifyModel = false);
0242     /// When forceGrouping is enabled, all given attributes will be used for grouping, regardless of the completion settings.
0243     Group *fetchGroup(int attribute, bool forceGrouping = false);
0244     // If this returns nonzero on an index, the index is the header of the returned group
0245     Group *groupForIndex(const QModelIndex &index) const;
0246     inline Group *groupOfParent(const QModelIndex &child) const
0247     {
0248         return static_cast<Group *>(child.internalPointer());
0249     }
0250     QModelIndex indexForRow(Group *g, int row) const;
0251     QModelIndex indexForGroup(Group *g) const;
0252 
0253     enum changeTypes { Broaden, Narrow, Change };
0254 
0255     // Returns whether the model needs to be reset
0256     void changeCompletions(Group *g);
0257 
0258     bool hasCompletionModel() const;
0259 
0260     /// Removes attributes not used in grouping from the input \a attribute
0261     int groupingAttributes(int attribute) const;
0262     static int countBits(int value);
0263 
0264     void resort();
0265 
0266     KTEXTEDITOR_EXPORT static bool matchesAbbreviation(const QString &word, const QString &typed, int &score);
0267     // exported for completion_test
0268 
0269     bool m_hasGroups = false;
0270 
0271     // ### Runtime state
0272     // General
0273     QList<KTextEditor::CodeCompletionModel *> m_completionModels;
0274     QMap<KTextEditor::CodeCompletionModel *, QString> m_currentMatch;
0275 
0276     // Column merging
0277     const std::array<std::vector<int>, 3> m_columnMerges = {{
0278         {0},
0279         {1, 2, 3, 4},
0280         {5},
0281     }};
0282 
0283     QTimer *m_updateBestMatchesTimer;
0284 
0285     Group *m_ungrouped;
0286     Group *m_argumentHints; // The argument-hints will be passed on to another model, to be shown in another widget
0287     Group *m_bestMatches; // A temporary group used for holding the best matches of all visible items
0288 
0289     // Storing the sorted order
0290     std::vector<Group *> m_rowTable;
0291     std::vector<Group *> m_emptyGroups;
0292     // Quick access to each specific group (if it exists)
0293     QMultiHash<int, Group *> m_groupHash;
0294     // Maps custom group-names to their specific groups
0295     QHash<QString, Group *> m_customGroupHash;
0296 
0297     friend class CompletionTest;
0298 };
0299 
0300 Q_DECLARE_OPERATORS_FOR_FLAGS(KateCompletionModel::GroupingMethods)
0301 
0302 #endif