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

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