File indexing completed on 2024-05-12 15:45:39

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
0003     SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KTEXTEDITOR_CODECOMPLETIONMODEL_H
0009 #define KTEXTEDITOR_CODECOMPLETIONMODEL_H
0010 
0011 #include <QModelIndex>
0012 #include <ktexteditor/range.h>
0013 #include <ktexteditor_export.h>
0014 
0015 namespace KTextEditor
0016 {
0017 class Document;
0018 class View;
0019 
0020 /**
0021  * \class CodeCompletionModel codecompletionmodel.h <KTextEditor/CodeCompletionModel>
0022  *
0023  * \short An item model for providing code completion, and meta information for
0024  *        enhanced presentation.
0025  *
0026  * \section compmodel_intro Introduction
0027  *
0028  * The CodeCompletionModel is the actual workhorse to provide code completions
0029  * in a KTextEditor::View. It is meant to be used in conjunction with the
0030  * CodeCompletionInterface. The CodeCompletionModel is not meant to be used as
0031  * is. Rather you need to implement a subclass of CodeCompletionModel to actually
0032  * generate completions appropriate for your type of Document.
0033  *
0034  * \section compmodel_implementing Implementing a CodeCompletionModel
0035  *
0036  * The CodeCompletionModel is a QAbstractItemModel, and can be subclassed in the
0037  * same way. It provides default implementations of several members, however, so
0038  * in most cases (if your completions are essentially a non-hierarchical, flat list
0039  * of matches) you will only need to overload few virtual functions.
0040  *
0041  * \section compmodel_flatlist Implementing a CodeCompletionModel for a flat list
0042  *
0043  * For the simple case of a flat list of completions, you will need to:
0044  *  - Implement completionInvoked() to actually generate/update the list of completion
0045  * matches
0046  *  - implement itemData() (or QAbstractItemModel::data()) to return the information that
0047  * should be displayed for each match.
0048  *  - use setRowCount() to reflect the number of matches.
0049  *
0050  * \section compmodel_roles_columns Columns and roles
0051  *
0052  * \todo document the meaning and usage of the columns and roles used by the
0053  * CodeCompletionInterface
0054  *
0055  * \section compmodel_usage Using the new CodeCompletionModel
0056  *
0057  * To start using your CodeCompletionModel, refer to CodeCompletionInterface.
0058  *
0059  * \section compmodel_controller ControllerInterface to get more control
0060  *
0061  * To have more control over code completion implement
0062  * CodeCompletionModelControllerInterface in your CodeCompletionModel.
0063  *
0064  * \see CodeCompletionInterface, CodeCompletionModelControllerInterface
0065  * @author Hamish Rodda <rodda@kde.org>
0066  */
0067 class KTEXTEDITOR_EXPORT CodeCompletionModel : public QAbstractItemModel
0068 {
0069     Q_OBJECT
0070 
0071 public:
0072     explicit CodeCompletionModel(QObject *parent);
0073     ~CodeCompletionModel() override;
0074 
0075     enum Columns {
0076         Prefix = 0,
0077         /// Icon representing the type of completion. We have a separate icon field
0078         /// so that names remain aligned where only some completions have icons,
0079         /// and so that they can be rearranged by the user.
0080         Icon,
0081         Scope,
0082         Name,
0083         Arguments,
0084         Postfix
0085     };
0086     static const int ColumnCount = Postfix + 1;
0087 
0088     /// @see CompletionProperties
0089     enum CompletionProperty {
0090         NoProperty = 0x0,
0091         FirstProperty = 0x1,
0092 
0093         // Access specifiers - no more than 1 per item
0094         Public = 0x1,
0095         Protected = 0x2,
0096         Private = 0x4,
0097 
0098         // Extra access specifiers - any number per item
0099         Static = 0x8,
0100         Const = 0x10,
0101 
0102         // Type - no more than 1 per item (except for Template)
0103         Namespace = 0x20,
0104         Class = 0x40,
0105         Struct = 0x80,
0106         Union = 0x100,
0107         Function = 0x200,
0108         Variable = 0x400,
0109         Enum = 0x800,
0110         Template = 0x1000,
0111         TypeAlias = 0x2000,
0112 
0113         // Special attributes - any number per item
0114         Virtual = 0x4000,
0115         Override = 0x8000,
0116         Inline = 0x10000,
0117         Friend = 0x20000,
0118         Signal = 0x40000,
0119         Slot = 0x80000,
0120 
0121         // Scope - no more than 1 per item
0122         LocalScope = 0x100000,
0123         NamespaceScope = 0x200000,
0124         GlobalScope = 0x400000,
0125 
0126         // Keep this in sync so the code knows when to stop
0127         LastProperty = GlobalScope
0128     };
0129     /// Stores a combination of #CompletionProperty values.
0130     Q_DECLARE_FLAGS(CompletionProperties, CompletionProperty)
0131 
0132     /// @see HighlightMethods
0133     enum HighlightMethod { NoHighlighting = 0x0, InternalHighlighting = 0x1, CustomHighlighting = 0x2 };
0134     /// Stores a combination of #HighlightMethod values.
0135     Q_DECLARE_FLAGS(HighlightMethods, HighlightMethod)
0136 
0137     /// Meta information is passed through extra {Qt::ItemDataRole}s.
0138     /// This information should be returned when requested on the Name column.
0139     enum ExtraItemDataRoles {
0140         /// The model should return a set of CompletionProperties.
0141         CompletionRole = Qt::UserRole,
0142 
0143         /// The model should return an index to the scope
0144         /// -1 represents no scope
0145         /// \todo how to sort scope?
0146         ScopeIndex,
0147 
0148         /**
0149          * If requested, your model should try to determine whether the
0150          * completion in question is a suitable match for the context (ie.
0151          * is accessible, exported, + returns the data type required).
0152          *
0153          * The returned data should ideally be matched against the argument-hint context
0154          * set earlier by SetMatchContext.
0155          *
0156          * Return an integer value that should be positive if the completion is suitable,
0157          * and zero if the completion is not suitable. The value should be between 0 an 10, where
0158          * 10 means perfect match.
0159          *
0160          * Return QVariant::Invalid if you are unable to determine this.
0161          */
0162         MatchQuality,
0163 
0164         /**
0165          * Is requested before MatchQuality(..) is requested. The item on which this is requested
0166          * is an argument-hint item(@see ArgumentHintDepth). When this role is requested, the item should
0167          * be noted, and whenever MatchQuality is requested, it should be computed by matching the item given
0168          * with MatchQuality into the context chosen by SetMatchContext.
0169          *
0170          * Feel free to ignore this, but ideally you should return QVariant::Invalid to make clear that your model does not support this.
0171          * */
0172         SetMatchContext,
0173 
0174         /**
0175          * Define which highlighting method will be used:
0176          * - QVariant::Invalid - allows the editor to choose (usually internal highlighting)
0177          * - QVariant::Integer - highlight as specified by HighlightMethods.
0178          */
0179         HighlightingMethod,
0180 
0181         /**
0182          * Allows an item to provide custom highlighting.  Return a
0183          * QList\<QVariant\> in the following format:
0184          * - int startColumn (where 0 = start of the completion entry)
0185          * - int endColumn (note: not length)
0186          * - QTextFormat attribute (note: attribute may be a KTextEditor::Attribute, as it is a child class)
0187          *   If the attribute is invalid, and the item is an argument-hint, the text will be drawn with
0188          *   a background-color depending on match-quality, or yellow.
0189          *   You can use that to mark the actual arguments that are matched in an argument-hint.
0190          *
0191          * Repeat this triplet as many times as required, however each column must be >= the previous,
0192          * and startColumn != endColumn.
0193          */
0194         CustomHighlight,
0195 
0196         /**
0197          * Returns the inheritance depth of the completion.  For example, a completion
0198          * which comes from the base class would have depth 0, one from a parent class
0199          * would have depth 1, one from that class' parent 2, etc. you can use this to
0200          * symbolize the general distance of a completion-item from a user. It will be used
0201          * for sorting.
0202          */
0203         InheritanceDepth,
0204 
0205         /**
0206          * This allows items in the completion-list to be expandable. If a model returns an QVariant bool value
0207          * that evaluates to true, the completion-widget will draw a handle to expand the item, and will also make
0208          * that action accessible through keyboard.
0209          */
0210         IsExpandable,
0211         /**
0212          * After a model returned true for a row on IsExpandable, the row may be expanded by the user.
0213          * When this happens, ExpandingWidget is requested.
0214          *
0215          * The model may return two types of values:
0216          * QWidget*:
0217          *  If the model returns a QVariant of type QWidget*, the code-completion takes over the given widget
0218          *  and embeds it into the completion-list under the completion-item. The widget will be automatically deleted at some point.
0219          *  The completion-widget will use the height of the widget as a hint for its preferred size, but it will
0220          *  resize the widget at will.
0221          * QString:
0222          *  If the mode returns a QVariant of type QString, it will create a small html-widget showing the given html-code,
0223          *  and embed it into the completion-list under the completion-item.
0224          *
0225          * @warning
0226          * @code
0227          *   QWidget* widget;
0228          *   return QVariant(widget);
0229          * @endcode
0230          * Will not work correctly!
0231          * Use the following instead.:
0232          * @code
0233          *   QVariant v;
0234          *   v.setValue<QWidget*>(widget);
0235          *   return v;
0236          * @endcode
0237          *
0238          * */
0239         ExpandingWidget,
0240         /**
0241          * Whenever an item is selected, this will be requested from the underlying model.
0242          * It may be used as a simple notification that the item was selected.
0243          *
0244          * Above that, the model may return a QString, which then should then contain html-code. A html-widget
0245          * will then be displayed as a one- or two-liner under the currently selected item(it will be partially expanded)
0246          * */
0247         ItemSelected,
0248 
0249         /**Is this completion-item an argument-hint?
0250          * The model should return an integral positive number if the item is an argument-hint, and QVariant() or 0 if it is not one.
0251          *
0252          * The returned depth-integer is important for sorting and matching.
0253          * Example:
0254          * "otherFunction(function1(function2("
0255          * all functions named function2 should have ArgumentHintDepth 1, all functions found for function1 should have ArgumentHintDepth 2,
0256          * and all functions named otherFunction should have ArgumentHintDepth 3
0257          *
0258          * Later, a completed item may then be matched with the first argument of function2, the return-type of function2 with the first
0259          * argument-type of function1, and the return-type of function1 with the argument-type of otherFunction.
0260          *
0261          * If the model returns a positive value on this role for a row, the content will be treated specially:
0262          * - It will be shown in a separate argument-hint list
0263          * - It will be sorted by Argument-hint depth
0264          * - Match-qualities will be illustrated by differently highlighting the matched argument if possible
0265          * The argument-hint list strings will be built from all source-model, with a little special behavior:
0266          * Prefix - Should be all text of the function-signature up to left of the matched argument of the function
0267          * Name - Should be the type and name of the function's matched argument. This part will be highlighted differently depending on the match-quality
0268          * Suffix - Should be all the text of the function-signature behind the matched argument
0269          *
0270          * Example: You are matching a function with signature "void test(int param1, int param2)", and you are matching the first argument.
0271          * The model should then return:
0272          * Prefix: "void test("
0273          * Name: "int param1"
0274          * Suffix: ", int param2)"
0275          *
0276          * If you don't use the highlighting, matching, etc. you can also return the columns in the usual way.
0277          * */
0278         ArgumentHintDepth,
0279 
0280         /**
0281          * This will be requested for each item to ask whether it should be included in computing a best-matches list.
0282          * If you return a valid positive integer n here, the n best matches will be listed at top of the completion-list separately.
0283          *
0284          * This is expensive because all items of the whole completion-list will be tested for their matching-quality, with each of the level 1
0285          * argument-hints.
0286          *
0287          * For that reason the end-user should be able to disable this feature.
0288          */
0289         BestMatchesCount,
0290 
0291         /**
0292          * The following three enumeration-values are only used on expanded completion-list items that contain an expanding-widget(@see ExpandingWidget)
0293          *
0294          * You can use them to allow the user to interact with the widget by keyboard.
0295          *
0296          * AccessibilityNext will be requested on an item if it is expanded, contains an expanding-widget, and the user triggers a special navigation
0297          * short-cut to go to navigate to the next position within the expanding-widget(if applicable).
0298          *
0299          * Return QVariant(true) if the input was used.
0300          * */
0301         AccessibilityNext,
0302         /**
0303          * AccessibilityPrevious will be requested on an item if it is expanded, contains an expanding-widget, and the user triggers a special navigation
0304          * short-cut to go to navigate to the previous position within the expanding-widget(if applicable).
0305          *
0306          * Return QVariant(true) if the input was used.
0307          * */
0308         AccessibilityPrevious,
0309         /**
0310          * AccessibilityAccept will be requested on an item if it is expanded, contains an expanding-widget, and the user triggers a special
0311          * shortcut to trigger the action associated with the position within the expanding-widget the user has navigated to using AccessibilityNext and
0312          * AccessibilityPrevious.
0313          *
0314          * This should return QVariant(true) if an action was triggered, else QVariant(false) or QVariant().
0315          * */
0316         AccessibilityAccept,
0317 
0318         /**
0319          * Using this Role, it is possible to greatly optimize the time needed to process very long completion-lists.
0320          *
0321          * In the completion-list, the items are usually ordered by some properties like argument-hint depth,
0322          * inheritance-depth and attributes. Kate usually has to query the completion-models for these values
0323          * for each item in the completion-list in order to extract the argument-hints and correctly sort the
0324          * completion-list. However, with a very long completion-list, only a very small fraction of the items is actually
0325          * visible.
0326          *
0327          * By using a tree structure you can give the items in a grouped order to kate, so it does not need to look at each
0328          * item and query data in order to initially show the completion-list.
0329          *
0330          * This is how it works:
0331          * - You create a tree-structure for your items
0332          * - Every inner node of the tree defines one attribute value that all sub-nodes have in common.
0333          *   - When the inner node is queried for GroupRole, it should return the "ExtraItemDataRoles" that all sub-nodes have in common
0334          *   - When the inner node is then queried for that exact role, it should return that value.
0335          *   - No other queries will be done to inner nodes.
0336          * - Every leaf node stands for an actual item in the completion list.
0337          *
0338          * The recommended grouping order is: Argument-hint depth, inheritance depth, attributes.
0339          *
0340          * This role can also be used to define completely custom groups, bypassing the editors builtin grouping:
0341          *  - Return Qt::DisplayRole when GroupRole is requested
0342          *  - Return the label text of the group when Qt::DisplayRole
0343          *   - Optional: Return an integer sorting-value when InheritanceDepth is  requested. This number will
0344          *               be used to determine the order of the groups. The order of the builtin groups is:
0345          *               1 = Best Matches, 100 = Local Scope, 200 = Public, 300 = Protected, 400 = Private, 500 = Namespace, 600 = Global
0346          *               You can pick any arbitrary number to position your group relative to these builtin groups.
0347          * */
0348         GroupRole,
0349 
0350         /**
0351          * Return a nonzero value here to enforce sorting the item at the end of the list.
0352          */
0353         UnimportantItemRole,
0354 
0355         LastExtraItemDataRole
0356     };
0357 
0358     void setRowCount(int rowCount);
0359 
0360     enum InvocationType { AutomaticInvocation, UserInvocation, ManualInvocation };
0361 
0362     /**
0363      * This function is responsible to generating / updating the list of current
0364      * completions. The default implementation does nothing.
0365      *
0366      * When implementing this function, remember to call setRowCount() (or implement
0367      * rowCount()), and to generate the appropriate change notifications (for instance
0368      * by calling QAbstractItemModel::reset()).
0369      * @param view The view to generate completions for
0370      * @param range The range of text to generate completions for
0371      * @param invocationType How the code completion was triggered
0372      * */
0373     virtual void completionInvoked(KTextEditor::View *view, const KTextEditor::Range &range, InvocationType invocationType);
0374 
0375     /**
0376      * This function is responsible for inserting a selected completion into the
0377      * view. The default implementation replaces the text that the completions
0378      * were based on with the Qt::DisplayRole of the Name column of the given match.
0379      *
0380      * @param view the view to insert the completion into
0381      * @param word the Range that the completions are based on (what the user entered
0382      * so far)
0383      * @param index identifies the completion match to insert
0384      * */
0385     virtual void executeCompletionItem(KTextEditor::View *view, const Range &word, const QModelIndex &index) const;
0386 
0387     // Reimplementations
0388     /**
0389      * Reimplemented from QAbstractItemModel::columnCount(). The default implementation
0390      * returns ColumnCount for all indices.
0391      * */
0392     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
0393     /**
0394      * Reimplemented from QAbstractItemModel::index(). The default implementation
0395      * returns a standard QModelIndex as long as the row and column are valid.
0396      * */
0397     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
0398     /**
0399      * Reimplemented from QAbstractItemModel::itemData(). The default implementation
0400      * returns a map with the QAbstractItemModel::data() for all roles that are used
0401      * by the CodeCompletionInterface. You will need to reimplement either this
0402      * function or QAbstractItemModel::data() in your CodeCompletionModel.
0403      * */
0404     QMap<int, QVariant> itemData(const QModelIndex &index) const override;
0405     /**
0406      * Reimplemented from QAbstractItemModel::parent(). The default implementation
0407      * returns an invalid QModelIndex for all items. This is appropriate for
0408      * non-hierarchical / flat lists of completions.
0409      * */
0410     QModelIndex parent(const QModelIndex &index) const override;
0411     /**
0412      * Reimplemented from QAbstractItemModel::rowCount(). The default implementation
0413      * returns the value set by setRowCount() for invalid (toplevel) indices, and 0
0414      * for all other indices. This is appropriate for non-hierarchical / flat lists
0415      * of completions
0416      * */
0417     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0418 
0419     /**
0420      * This function returns true if the model needs grouping, otherwise false.
0421      * The default is false if not changed via setHasGroups().
0422      */
0423     bool hasGroups() const;
0424 
0425 Q_SIGNALS:
0426 
0427     /**
0428      * Emit this if the code-completion for this model was invoked, some time is needed in order to get the data,
0429      * and the model is reset once the data is available.
0430      *
0431      * This only has an effect if emitted from within completionInvoked(..).
0432      *
0433      * This prevents the code-completion list from showing until this model is reset,
0434      * so there is no annoying flashing in the user-interface resulting from other models
0435      * supplying their data earlier.
0436      *
0437      * @note The implementation may choose to show the completion-list anyway after some timeout
0438      *
0439      * @warning If you emit this, you _must_ also reset the model at some point,
0440      *                  else the code-completion will be completely broken to the user.
0441      *                  Consider that there may always be additional completion-models apart from yours.
0442      *
0443      * @since 4.3
0444      */
0445     void waitForReset();
0446 
0447     /**
0448      * Internal
0449      */
0450     void hasGroupsChanged(KTextEditor::CodeCompletionModel *model, bool hasGroups);
0451 
0452 protected:
0453     void setHasGroups(bool hasGroups);
0454 
0455 private:
0456     class CodeCompletionModelPrivate *const d;
0457 };
0458 
0459 Q_DECLARE_OPERATORS_FOR_FLAGS(CodeCompletionModel::CompletionProperties)
0460 Q_DECLARE_OPERATORS_FOR_FLAGS(CodeCompletionModel::HighlightMethods)
0461 
0462 }
0463 
0464 #endif // KTEXTEDITOR_CODECOMPLETIONMODEL_H