1/*
2 SPDX-FileCopyrightText: 2004, 2010 Joseph Wenninger <jowenn@kde.org>
3 SPDX-FileCopyrightText: 2009 Milian Wolff <mail@milianw.de>
4 SPDX-FileCopyrightText: 2014 Sven Brauch <svenbrauch@gmail.com>
5 SPDX-FileCopyrightText: 2025 Mirian Margiani <mixosaurus+kde@pm.me>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#ifndef _KATE_TEMPLATE_HANDLER_H_
11#define _KATE_TEMPLATE_HANDLER_H_
12
13#include <QList>
14#include <QObject>
15#include <QPointer>
16#include <QString>
17
18#include <katescript.h>
19#include <ktexteditor/cursor.h>
20#include <ktexteditor/range.h>
21
22class QDebug;
23class KateUndoManager;
24
25namespace KTextEditor
26{
27class DocumentPrivate;
28class ViewPrivate;
29class MovingCursor;
30class MovingRange;
31class View;
32}
33
34/**
35 * \brief Inserts a template and offers advanced snippet features, like navigation and mirroring.
36 *
37 * For each template inserted a new KateTemplateHandler will be created.
38 *
39 * The handler has the following features:
40 *
41 * \li It inserts the template string into the document at the requested position.
42 * \li When the template contains at least one variable, the cursor will be placed
43 * at the start of the first variable and its range gets selected.
44 * \li When more than one variable exists,TAB and SHIFT TAB can be used to navigate
45 * to the next/previous variable.
46 * \li When a variable occurs more than once in the template, edits to any of the
47 * occurrences will be mirroed to the other ones.
48 * \li When ESC is pressed, the template handler closes.
49 * \li When ALT + RETURN is pressed and a \c ${cursor} variable
50 * exists in the template,the cursor will be placed there. Else the cursor will
51 * be placed at the end of the template.
52 *
53 * \author Milian Wolff <mail@milianw.de>
54 */
55
56class KateTemplateHandler : public QObject
57{
58 Q_OBJECT
59
60public:
61 /**
62 * Setup the template handler, insert the template string.
63 *
64 * NOTE: The handler deletes itself when required, you do not need to
65 * keep track of it.
66 */
67 KateTemplateHandler(KTextEditor::ViewPrivate *view,
68 KTextEditor::Cursor position,
69 const QString &templateString,
70 const QString &script,
71 KateUndoManager *undoManager);
72
73 ~KateTemplateHandler() override;
74
75protected:
76 /**
77 * \brief Provide keyboard interaction for the template handler.
78 *
79 * The event filter handles the following shortcuts:
80 *
81 * TAB: jump to next editable (i.e. not mirrored) range.
82 * NOTE: this prevents indenting via TAB.
83 * SHIFT + TAB: jump to previous editable (i.e. not mirrored) range.
84 * NOTE: this prevents un-indenting via SHIFT + TAB.
85 * ESC: terminate template handler (only when no completion is active).
86 * ALT + RETURN: accept template and jump to the end-cursor.
87 * if %{cursor} was given in the template, that will be the
88 * end-cursor.
89 * else just jump to the end of the inserted text.
90 */
91 bool eventFilter(QObject *object, QEvent *event) override;
92
93private:
94 /**
95 * Inserts the @p text template at @p position and performs
96 * all necessary initializations, such as populating default values
97 * and placing the cursor.
98 */
99 void initializeTemplate();
100
101 /**
102 * Parse @p templateText and populate m_fields.
103 */
104 void parseFields(const QString &templateText);
105
106 /**
107 * Set necessary attributes (esp. background colour) on all moving
108 * ranges for the fields in m_fields.
109 */
110 void setupFieldRanges();
111
112 /**
113 * Evaluate default values for all fields in m_fields and
114 * store them in the fields. This updates the @property defaultValue property
115 * of the TemplateField instances in m_fields from the raw, user-entered
116 * default value to its evaluated equivalent (e.g. "func()" -> result of function call)
117 *
118 * Default values are subsequently written into the document.
119 *
120 * @sa TemplateField
121 */
122 void setupDefaultValues();
123
124 /**
125 * Install an event filter on the filter proxy of \p view for
126 * navigation between the ranges and terminating the KateTemplateHandler.
127 *
128 * \see eventFilter()
129 */
130 void setupEventHandler(KTextEditor::View *view);
131
132 /**
133 * Jumps to the previous editable range. If there is none, wrap and jump to the first range.
134 *
135 * \see jumpToNextRange()
136 */
137 void jumpToPreviousRange();
138
139 /**
140 * Jumps to the next editable range. If there is none, wrap and jump to the last range.
141 *
142 * \see jumpToPreviousRange()
143 */
144 void jumpToNextRange();
145
146 /**
147 * Helper function for jumpTo{Next,Previous}
148 * if initial is set to true, assumes the cursor is before the snippet
149 * and selects the first field
150 */
151 void jump(int by, bool initial = false);
152
153 /**
154 * Jumps to the final cursor position. This is either \p m_finalCursorPosition, or
155 * if that is not set, the end of \p m_templateRange.
156 */
157 void jumpToFinalCursorPosition();
158
159 /**
160 * Go through all template fields and decide if their moving ranges expand
161 * when edited at the corners. Expansion is turned off if two fields are
162 * directly adjacent to avoid overlaps when characters are inserted between
163 * them.
164 */
165 void updateRangeBehaviours();
166
167private Q_SLOTS:
168 /**
169 * Saves the range of the inserted template. This is required since
170 * tabs could get expanded on insert. While we are at it, we can
171 * use it to auto-indent the code after insert.
172 */
173 void slotTemplateInserted(KTextEditor::Document *document, KTextEditor::Range range);
174
175 /**
176 * Install event filter on new views.
177 */
178 void slotViewCreated(KTextEditor::Document *document, KTextEditor::View *view);
179
180 /**
181 * Update content of all dependent fields, i.e. mirror or script fields.
182 */
183 void updateDependentFields(KTextEditor::Document *document, KTextEditor::Range oldRange, bool textRemoved = false);
184
185public:
186 KTextEditor::ViewPrivate *view() const;
187 KTextEditor::DocumentPrivate *doc() const;
188
189private:
190 /// The view we operate on
191 KTextEditor::ViewPrivate *m_view;
192 /// The undo manager associated with our document
193 KateUndoManager *const m_undoManager;
194
195 // Describes a single template field, e.g. ${foo}.
196 struct TemplateField {
197 // unique field ID to identify and order fields
198 qsizetype id{-1};
199
200 // up-to-date range for the field
201 std::shared_ptr<KTextEditor::MovingRange> range;
202
203 // static range for identifying changes
204 KTextEditor::Range staticRange{KTextEditor::Range::invalid()};
205
206 // contents of the field, i.e. identifier or function to call
207 QString identifier;
208 // default value, if applicable; else empty
209 QString defaultValue;
210 enum Kind {
211 Invalid, // not an actual field
212 Editable, // normal, user-editable field (green by default) [non-dependent field]
213 Mirror, // field mirroring contents of another field [dependent field]
214 FunctionCall, // field containing the up-to-date result of a function call [dependent field]
215 FinalCursorPosition // field marking the final cursor position
216 };
217 Kind kind = Invalid;
218 // true if this field was edited by the user before
219 bool touched = false;
220 // true if this field was removed e.g., because the line that contained it was removed
221 bool removed = false;
222
223 bool operator==(const TemplateField &other) const
224 {
225 return id == other.id;
226 }
227
228 friend bool operator<(const TemplateField &l, const TemplateField &r)
229 {
230 return l.id < r.id;
231 }
232 };
233
234 // List of all template fields in the inserted snippet. @see sortFields()
235 QList<TemplateField> m_fields;
236
237 // Get all template fields which contain or border on @p range.
238 // If @p compareStaticRanges is @c true, compare with last saved static field
239 // ranges instead of moving field ranges.
240 const QList<TemplateField> fieldsForRange(KTextEditor::Range range, bool compareStaticRanges) const;
241
242 // Restore order of empty adjacent fields after inserting into one.
243 void reorderEmptyAdjacentFields(const QList<TemplateField> &changedFields);
244
245 /// Construct a map of master fields and their current value, for use in scripts.
246 KateScript::FieldMap fieldMap() const;
247
248 /// A range that occupies the whole range of the inserted template.
249 /// When the an edit happens outside it, the template handler gets closed.
250 std::shared_ptr<KTextEditor::MovingRange> m_wholeTemplateRange;
251
252 /// Set to true when currently updating dependent fields, to prevent recursion.
253 bool m_internalEdit;
254
255 /// template script (i.e. javascript stuff), which can be used by the current template
256 KateScript m_templateScript;
257};
258
259#endif
260

source code of ktexteditor/src/utils/katetemplatehandler.h