-
-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathkatetextfolding.h
More file actions
403 lines (343 loc) · 12.4 KB
/
katetextfolding.h
File metadata and controls
403 lines (343 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*
SPDX-FileCopyrightText: 2013 Christoph Cullmann <cullmann@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KATE_TEXTFOLDING_H
#define KATE_TEXTFOLDING_H
#include <ktexteditor_export.h>
#include "ktexteditor/range.h"
#include <QFlags>
#include <QJsonArray>
#include <QJsonDocument>
#include <QObject>
#include <functional>
namespace Kate
{
class TextBuffer;
class TextCursor;
/**
* Class representing the folding information for a TextBuffer.
* The interface allows to arbitrary fold given regions of a buffer as long
* as they are well nested.
* Multiple instances of this class can exist for the same buffer.
*/
class KTEXTEDITOR_EXPORT TextFolding : public QObject
{
Q_OBJECT
public:
/**
* Create folding object for given buffer.
* @param buffer text buffer we want to provide folding info for
*/
explicit TextFolding(TextBuffer &buffer);
/**
* Cleanup
*/
~TextFolding() override;
/**
* Folding state of a range
*/
enum FoldingRangeFlag {
/**
* Range is persistent, e.g. it should not auto-delete after unfolding!
*/
Persistent = 0x1,
/**
* Range is folded away
*/
Folded = 0x2
};
Q_DECLARE_FLAGS(FoldingRangeFlags, FoldingRangeFlag)
/**
* Create a new folding range.
* @param range folding range
* @param flags initial flags for the new folding range
* @return on success, id of new range >= 0, else -1, we return no pointer as folding ranges might be auto-deleted internally!
* the ids are stable for one Kate::TextFolding, e.g. you can rely in unit tests that you get 0,1,.... for successfully created ranges!
*/
qint64 newFoldingRange(KTextEditor::Range range, FoldingRangeFlags flags = FoldingRangeFlags());
/**
* Returns the folding range associated with @p id.
* If @p id is not a valid id, the returned range matches KTextEditor::Range::invalid().
* @note This works for either persistent ranges or folded ranges.
* Note, that the highlighting does not add folds unless text is folded.
*
* @return the folding range for @p id
*/
KTextEditor::Range foldingRange(qint64 id) const;
/**
* Fold the given range.
* @param id id of the range to fold
* @return success
*/
bool foldRange(qint64 id);
/**
* Unfold the given range.
* In addition it can be forced to remove the region, even if it is persistent.
* @param id id of the range to unfold
* @param remove should the range be removed from the folding after unfolding? ranges that are not persistent auto-remove themself on unfolding
* @return success
*/
bool unfoldRange(qint64 id, bool remove = false);
/**
* Query if a given line is visible.
* Very fast, if nothing is folded, else does binary search
* log(n) for n == number of folded ranges
* @param line real line to query
* @param foldedRangeId if the line is not visible and that pointer is not 0, will be filled with id of range hiding the line or -1
* @return is that line visible?
*/
bool isLineVisible(int line, qint64 *foldedRangeId = nullptr) const;
/**
* Ensure that a given line will be visible.
* Potentially unfold recursively all folds hiding this line, else just returns.
* @param line line to make visible
*/
void ensureLineIsVisible(int line);
/**
* Query number of visible lines.
* Very fast, if nothing is folded, else walks over all folded regions
* O(n) for n == number of folded ranges
*/
int visibleLines() const;
/**
* Convert a text buffer line to a visible line number.
* Very fast, if nothing is folded, else walks over all folded regions
* O(n) for n == number of folded ranges
* @param line line index in the text buffer
* @return index in visible lines
*/
int lineToVisibleLine(int line) const;
/**
* Convert a visible line number to a line number in the text buffer.
* Very fast, if nothing is folded, else walks over all folded regions
* O(n) for n == number of folded ranges
* @param visibleLine visible line index
* @return index in text buffer lines
*/
int visibleLineToLine(int visibleLine) const;
struct IdAndFlag {
qint64 id;
FoldingRangeFlags flags;
};
/**
* Queries which folding ranges start at the given line and returns the id + flags for all
* of them. Very fast if nothing is folded, else binary search.
* @param line line to query starting folding ranges
* @return vector of id's + flags
*/
QList<IdAndFlag> foldingRangesStartingOnLine(int line) const;
/**
* Query child folding ranges for given range id. To query the toplevel
* ranges pass id -1
* @param parentRangeId id of parent range, pass -1 to query top level ranges
* @return vector of id's + flags for child ranges
*/
QList<IdAndFlag> foldingRangesForParentRange(qint64 parentRangeId = -1) const;
/**
* Return the current known folding ranges a QJsonDocument to store in configs.
* @return current folds as variant list
*/
QJsonDocument exportFoldingRanges() const;
/**
* Import the folding ranges given as a QJsonDocument like read from configs.
* @param folds list of folds to import
*/
void importFoldingRanges(const QJsonDocument &folds);
/**
* Dump folding state as string, for unit testing and debugging
* @return current state as text
*/
QString debugDump() const;
/**
* Print state to stdout for testing
*/
void debugPrint(const QString &title) const;
void editEnd(int startLine, int endLine, std::function<bool(int)> isLineFoldingStart);
public Q_SLOTS:
/**
* Clear the complete folding.
* This is automatically triggered if the buffer is cleared.
*/
void clear();
Q_SIGNALS:
/**
* If the folding state of existing ranges changes or
* ranges are added/removed, this signal is emitted.
*/
void foldingRangesChanged();
private:
/**
* Data holder for text folding range and its nested children
*/
class KTEXTEDITOR_NO_EXPORT FoldingRange
{
public:
/**
* Construct new one
* @param buffer text buffer to use
* @param range folding range
* @param flags flags for the new folding range
*/
FoldingRange(TextBuffer &buffer, KTextEditor::Range range, FoldingRangeFlags flags);
/**
* Cleanup
*/
~FoldingRange();
FoldingRange(const FoldingRange &) = delete;
FoldingRange &operator=(const FoldingRange &) = delete;
/**
* Vector of range pointers
*/
typedef QList<FoldingRange *> Vector;
/**
* start moving cursor
* NO range to be more efficient
*/
Kate::TextCursor *start;
/**
* end moving cursor
* NO range to be more efficient
*/
Kate::TextCursor *end;
/**
* parent range, if any
*/
FoldingRange *parent;
/**
* nested ranges, if any
* this will always be sorted and non-overlapping
* nested ranges are inside these ranges
*/
FoldingRange::Vector nestedRanges;
/**
* Folding range flags
*/
FoldingRangeFlags flags;
/**
* id of this range
*/
qint64 id;
};
/**
* Clear all folding range collections but leave global id counter intact.
*/
KTEXTEDITOR_NO_EXPORT
void clearFoldingRanges();
/**
* Fill known folding ranges in a QVariantList to store in configs.
* @param ranges ranges vector to dump
* @param folds current folds as variant list, will be filled
*/
KTEXTEDITOR_NO_EXPORT
static void exportFoldingRanges(const TextFolding::FoldingRange::Vector &ranges, QJsonArray &folds);
/**
* Dump folding state of given vector as string, for unit testing and debugging.
* Will recurse if wanted.
* @param ranges ranges vector to dump
* @param recurse recurse to nestedRanges?
* @return current state as text
*/
KTEXTEDITOR_NO_EXPORT
static QString debugDump(const TextFolding::FoldingRange::Vector &ranges, bool recurse);
/**
* Helper to insert folding range into existing ones.
* Might fail, if not correctly nested.
* Then the outside must take care of the passed pointer, e.g. delete it.
* Will sanitize the ranges vectors, purge invalid/empty ranges.
* @param parent parent folding range if any
* @param existingRanges ranges into which we want to insert the new one
* @param newRange new folding range
* @return success, if false, newRange should be deleted afterwards, else it is registered internally
*/
KTEXTEDITOR_NO_EXPORT
bool insertNewFoldingRange(FoldingRange *parent, TextFolding::FoldingRange::Vector &existingRanges, TextFolding::FoldingRange *newRange);
/**
* Helper to update the folded ranges if we insert a new range into the tree.
* @param newRange new folding range that was inserted, will already contain its new nested ranges, if any!
* @return any updated done? if yes, the foldingRangesChanged() signal got emitted!
*/
KTEXTEDITOR_NO_EXPORT
bool updateFoldedRangesForNewRange(TextFolding::FoldingRange *newRange);
/**
* Helper to update the folded ranges if we remove a new range from the tree.
* @param oldRange new folding range that is removed, will still contain its new nested ranges, if any!
* @return any updated done? if yes, the foldingRangesChanged() signal got emitted!
*/
KTEXTEDITOR_NO_EXPORT
bool updateFoldedRangesForRemovedRange(TextFolding::FoldingRange *oldRange);
/**
* Helper to append recursively topmost folded ranges from input to output vector.
* @param newFoldedFoldingRanges output vector for folded ranges
* @param ranges input vector to search recursively folded ranges inside
*/
KTEXTEDITOR_NO_EXPORT
void appendFoldedRanges(TextFolding::FoldingRange::Vector &newFoldedFoldingRanges, const TextFolding::FoldingRange::Vector &ranges) const;
/**
* Compare two ranges by their start cursor.
* @param a first range
* @param b second range
*/
KTEXTEDITOR_NO_EXPORT
static bool compareRangeByStart(FoldingRange *a, FoldingRange *b);
/**
* Compare two ranges by their end cursor.
* @param a first range
* @param b second range
*/
KTEXTEDITOR_NO_EXPORT
static bool compareRangeByEnd(FoldingRange *a, FoldingRange *b);
/**
* Compare range start with line
* @param line line
* @param range range
*/
KTEXTEDITOR_NO_EXPORT
static bool compareRangeByStartWithLine(int line, FoldingRange *range);
/**
* Compare range start with line
* @param range range
* @param line line
*/
KTEXTEDITOR_NO_EXPORT
static bool compareRangeByLineWithStart(FoldingRange *range, int line);
/**
* Internal helper that queries which folding ranges start at the given line and returns the id + flags for all
* of them. Will recursively dive down starting with given vector
* @param results vector that is filled with id's + flags
* @param ranges ranges vector to search in
* @param line line to query starting folding ranges
*/
KTEXTEDITOR_NO_EXPORT
void foldingRangesStartingOnLine(QList<IdAndFlag> &results, const TextFolding::FoldingRange::Vector &ranges, int line) const;
private:
/**
* parent text buffer
* is a reference, and no pointer, as this must always exist and can't change
* can't be const, as we create text cursors!
*/
TextBuffer &m_buffer;
/**
* toplevel folding ranges
* this will always be sorted and non-overlapping
* nested ranges are inside these ranges
*/
FoldingRange::Vector m_foldingRanges;
/**
* folded folding ranges
* this is a sorted vector of ranges
* all non-overlapping
*/
FoldingRange::Vector m_foldedFoldingRanges;
/**
* global id counter for the created ranges
*/
qint64 m_idCounter;
/**
* mapping: id => range
*/
QHash<qint64, FoldingRange *> m_idToFoldingRange;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(TextFolding::FoldingRangeFlags)
}
#endif