File indexing completed on 2024-04-28 15:30:56

0001 /*
0002     SPDX-FileCopyrightText: 2004, 2010 Joseph Wenninger <jowenn@kde.org>
0003     SPDX-FileCopyrightText: 2009 Milian Wolff <mail@milianw.de>
0004     SPDX-FileCopyrightText: 2014 Sven Brauch <svenbrauch@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef _KATE_TEMPLATE_HANDLER_H_
0010 #define _KATE_TEMPLATE_HANDLER_H_
0011 
0012 #include <QMap>
0013 #include <QObject>
0014 #include <QPointer>
0015 #include <QString>
0016 #include <QVector>
0017 
0018 #include <katescript.h>
0019 #include <ktexteditor/cursor.h>
0020 
0021 class KateUndoManager;
0022 
0023 namespace KTextEditor
0024 {
0025 class DocumentPrivate;
0026 class ViewPrivate;
0027 class MovingCursor;
0028 class MovingRange;
0029 class View;
0030 }
0031 
0032 /**
0033  * \brief Inserts a template and offers advanced snippet features, like navigation and mirroring.
0034  *
0035  * For each template inserted a new KateTemplateHandler will be created.
0036  *
0037  * The handler has the following features:
0038  *
0039  * \li It inserts the template string into the document at the requested position.
0040  * \li When the template contains at least one variable, the cursor will be placed
0041  *     at the start of the first variable and its range gets selected.
0042  * \li When more than one variable exists,TAB and SHIFT TAB can be used to navigate
0043  *     to the next/previous variable.
0044  * \li When a variable occurs more than once in the template, edits to any of the
0045  *     occurrences will be mirroed to the other ones.
0046  * \li When ESC is pressed, the template handler closes.
0047  * \li When ALT + RETURN is pressed and a \c ${cursor} variable
0048  *     exists in the template,the cursor will be placed there. Else the cursor will
0049  *     be placed at the end of the template.
0050  *
0051  * \author Milian Wolff <mail@milianw.de>
0052  */
0053 
0054 class KateTemplateHandler : public QObject
0055 {
0056     Q_OBJECT
0057 
0058 public:
0059     /**
0060      * Setup the template handler, insert the template string.
0061      *
0062      * NOTE: The handler deletes itself when required, you do not need to
0063      *       keep track of it.
0064      */
0065     KateTemplateHandler(KTextEditor::ViewPrivate *view,
0066                         KTextEditor::Cursor position,
0067                         const QString &templateString,
0068                         const QString &script,
0069                         KateUndoManager *undoManager);
0070 
0071     ~KateTemplateHandler() override;
0072 
0073 protected:
0074     /**
0075      * \brief Provide keyboard interaction for the template handler.
0076      *
0077      * The event filter handles the following shortcuts:
0078      *
0079      * TAB: jump to next editable (i.e. not mirrored) range.
0080      *      NOTE: this prevents indenting via TAB.
0081      * SHIFT + TAB: jump to previous editable (i.e. not mirrored) range.
0082      *      NOTE: this prevents un-indenting via SHIFT + TAB.
0083      * ESC: terminate template handler (only when no completion is active).
0084      * ALT + RETURN: accept template and jump to the end-cursor.
0085      *               if %{cursor} was given in the template, that will be the
0086      *               end-cursor.
0087      *               else just jump to the end of the inserted text.
0088      */
0089     bool eventFilter(QObject *object, QEvent *event) override;
0090 
0091 private:
0092     /**
0093      * Inserts the @p text template at @p position and performs
0094      * all necessary initializations, such as populating default values
0095      * and placing the cursor.
0096      */
0097     void initializeTemplate();
0098 
0099     /**
0100      * Parse @p templateText and populate m_fields.
0101      */
0102     void parseFields(const QString &templateText);
0103 
0104     /**
0105      * Set necessary attributes (esp. background colour) on all moving
0106      * ranges for the fields in m_fields.
0107      */
0108     void setupFieldRanges();
0109 
0110     /**
0111      * Evaluate default values for all fields in m_fields and
0112      * store them in the fields. This updates the @property defaultValue property
0113      * of the TemplateField instances in m_fields from the raw, user-entered
0114      * default value to its evaluated equivalent (e.g. "func()" -> result of function call)
0115      *
0116      * @sa TemplateField
0117      */
0118     void setupDefaultValues();
0119 
0120     /**
0121      * Install an event filter on the filter proxy of \p view for
0122      * navigation between the ranges and terminating the KateTemplateHandler.
0123      *
0124      * \see eventFilter()
0125      */
0126     void setupEventHandler(KTextEditor::View *view);
0127 
0128     /**
0129      * Jumps to the previous editable range. If there is none, wrap and jump to the first range.
0130      *
0131      * \see jumpToNextRange()
0132      */
0133     void jumpToPreviousRange();
0134 
0135     /**
0136      * Jumps to the next editable range. If there is none, wrap and jump to the last range.
0137      *
0138      * \see jumpToPreviousRange()
0139      */
0140     void jumpToNextRange();
0141 
0142     /**
0143      * Helper function for jumpTo{Next,Previous}
0144      * if initial is set to true, assumes the cursor is before the snippet
0145      * and selects the first field
0146      */
0147     void jump(int by, bool initial = false);
0148 
0149     /**
0150      * Jumps to the final cursor position. This is either \p m_finalCursorPosition, or
0151      * if that is not set, the end of \p m_templateRange.
0152      */
0153     void jumpToFinalCursorPosition();
0154 
0155     /**
0156      * Go through all template fields and decide if their moving ranges expand
0157      * when edited at the corners. Expansion is turned off if two fields are
0158      * directly adjacent to avoid overlaps when characters are inserted between
0159      * them.
0160      */
0161     void updateRangeBehaviours();
0162 
0163     /**
0164      * Sort all template fields in m_fields by tab order, which means,
0165      * by range; except for ${cursor} which is always sorted last.
0166      */
0167     void sortFields();
0168 
0169 private Q_SLOTS:
0170     /**
0171      * Saves the range of the inserted template. This is required since
0172      * tabs could get expanded on insert. While we are at it, we can
0173      * use it to auto-indent the code after insert.
0174      */
0175     void slotTemplateInserted(KTextEditor::Document *document, KTextEditor::Range range);
0176 
0177     /**
0178      * Install event filter on new views.
0179      */
0180     void slotViewCreated(KTextEditor::Document *document, KTextEditor::View *view);
0181 
0182     /**
0183      * Update content of all dependent fields, i.e. mirror or script fields.
0184      */
0185     void updateDependentFields(KTextEditor::Document *document, KTextEditor::Range oldRange);
0186 
0187 public:
0188     KTextEditor::ViewPrivate *view() const;
0189     KTextEditor::DocumentPrivate *doc() const;
0190 
0191 private:
0192     /// The view we operate on
0193     KTextEditor::ViewPrivate *m_view;
0194     /// The undo manager associated with our document
0195     KateUndoManager *const m_undoManager;
0196 
0197     // Describes a single template field, e.g. ${foo}.
0198     struct TemplateField {
0199         // up-to-date range for the field
0200         QSharedPointer<KTextEditor::MovingRange> range;
0201         // contents of the field, i.e. identifier or function to call
0202         QString identifier;
0203         // default value, if applicable; else empty
0204         QString defaultValue;
0205         enum Kind {
0206             Invalid, // not an actual field
0207             Editable, // normal, user-editable field (green by default) [non-dependent field]
0208             Mirror, // field mirroring contents of another field [dependent field]
0209             FunctionCall, // field containing the up-to-date result of a function call [dependent field]
0210             FinalCursorPosition // field marking the final cursor position
0211         };
0212         Kind kind = Invalid;
0213         // true if this field was edited by the user before
0214         bool touched = false;
0215         bool operator==(const TemplateField &other) const
0216         {
0217             return range == other.range;
0218         }
0219     };
0220     // List of all template fields in the inserted snippet. @see sortFields()
0221     QVector<TemplateField> m_fields;
0222 
0223     // Get the template field which contains @p range.
0224     const TemplateField fieldForRange(KTextEditor::Range range) const;
0225 
0226     /// Construct a map of master fields and their current value, for use in scripts.
0227     KateScript::FieldMap fieldMap() const;
0228 
0229     /// A range that occupies the whole range of the inserted template.
0230     /// When the an edit happens outside it, the template handler gets closed.
0231     QSharedPointer<KTextEditor::MovingRange> m_wholeTemplateRange;
0232 
0233     /// Set to true when currently updating dependent fields, to prevent recursion.
0234     bool m_internalEdit;
0235 
0236     /// template script (i.e. javascript stuff), which can be used by the current template
0237     KateScript m_templateScript;
0238 };
0239 
0240 #endif