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 | |
20 | class KateUndoManager; |
21 | |
22 | namespace KTextEditor |
23 | { |
24 | class DocumentPrivate; |
25 | class ViewPrivate; |
26 | class MovingCursor; |
27 | class MovingRange; |
28 | class 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 | |
53 | class KateTemplateHandler : public QObject |
54 | { |
55 | Q_OBJECT |
56 | |
57 | public: |
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 | |
72 | protected: |
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 | |
90 | private: |
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 | |
168 | private 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 | |
186 | public: |
187 | KTextEditor::ViewPrivate *view() const; |
188 | KTextEditor::DocumentPrivate *doc() const; |
189 | |
190 | private: |
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 | |