1 | /* |
2 | SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #ifndef KATE_TEXTBLOCK_H |
8 | #define KATE_TEXTBLOCK_H |
9 | |
10 | #include "katetextline.h" |
11 | |
12 | #include <QList> |
13 | #include <QSet> |
14 | #include <QVarLengthArray> |
15 | |
16 | #include <ktexteditor/cursor.h> |
17 | #include <ktexteditor_export.h> |
18 | |
19 | namespace KTextEditor |
20 | { |
21 | class View; |
22 | } |
23 | |
24 | namespace Kate |
25 | { |
26 | class TextBuffer; |
27 | class TextCursor; |
28 | class ; |
29 | |
30 | /** |
31 | * Class representing a text block. |
32 | * This is used to build up a Kate::TextBuffer. |
33 | * This class should only be used by TextBuffer/Cursor/Range. |
34 | */ |
35 | class TextBlock |
36 | { |
37 | public: |
38 | /** |
39 | * Construct an empty text block. |
40 | * @param buffer parent text buffer |
41 | * @param startLine start line of this block |
42 | */ |
43 | TextBlock(TextBuffer *buffer, int startLine); |
44 | |
45 | /** |
46 | * Destruct the text block |
47 | */ |
48 | ~TextBlock(); |
49 | |
50 | /** |
51 | * Start line of this block. |
52 | * @return start line of this block |
53 | */ |
54 | int startLine() const |
55 | { |
56 | return m_startLine; |
57 | } |
58 | |
59 | /** |
60 | * Set start line of this block. |
61 | * @param startLine new start line of this block |
62 | */ |
63 | void setStartLine(int startLine); |
64 | |
65 | /** |
66 | * Retrieve a text line. |
67 | * @param line wanted line number |
68 | * @return text line |
69 | */ |
70 | TextLine line(int line) const; |
71 | |
72 | /** |
73 | * Transfer all non text attributes for the given line from the given text line to the one in the block. |
74 | * @param line line number to set attributes |
75 | * @param textLine line reference to get attributes from |
76 | */ |
77 | void setLineMetaData(int line, const TextLine &textLine); |
78 | |
79 | /** |
80 | * Retrieve length for @p line. |
81 | * @param line wanted line number |
82 | * @return length of line |
83 | */ |
84 | int lineLength(int line) const |
85 | { |
86 | Q_ASSERT(line >= startLine() && (line - startLine()) < lines()); |
87 | return m_lines[line - startLine()].length(); |
88 | } |
89 | |
90 | /** |
91 | * Append a new line with given text. |
92 | * @param textOfLine text of the line to append |
93 | */ |
94 | void appendLine(const QString &textOfLine); |
95 | |
96 | /** |
97 | * Clear the lines. |
98 | */ |
99 | void clearLines(); |
100 | |
101 | /** |
102 | * Number of lines in this block. |
103 | * @return number of lines |
104 | */ |
105 | int lines() const |
106 | { |
107 | return static_cast<int>(m_lines.size()); |
108 | } |
109 | |
110 | /** |
111 | * Retrieve text of block. |
112 | * @param text for this block, lines separated by '\n' |
113 | */ |
114 | void text(QString &text) const; |
115 | |
116 | /** |
117 | * Wrap line at given cursor position. |
118 | * @param position line/column as cursor where to wrap |
119 | * @param fixStartLinesStartIndex start index to fix start lines, normally this is this block |
120 | */ |
121 | void wrapLine(const KTextEditor::Cursor position, int fixStartLinesStartIndex); |
122 | |
123 | /** |
124 | * Unwrap given line. |
125 | * @param line line to unwrap |
126 | * @param previousBlock previous block, if any, if we unwrap first line in block, we need to have this |
127 | * @param fixStartLinesStartIndex start index to fix start lines, normally this is this block or the previous one |
128 | */ |
129 | void unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex); |
130 | |
131 | /** |
132 | * Insert text at given cursor position. |
133 | * @param position position where to insert text |
134 | * @param text text to insert |
135 | */ |
136 | void insertText(const KTextEditor::Cursor position, const QString &text); |
137 | |
138 | /** |
139 | * Remove text at given range. |
140 | * @param range range of text to remove, must be on one line only. |
141 | * @param removedText will be filled with removed text |
142 | */ |
143 | void removeText(KTextEditor::Range range, QString &removedText); |
144 | |
145 | /** |
146 | * Debug output, print whole block content with line numbers and line length |
147 | * @param blockIndex index of this block in buffer |
148 | */ |
149 | void debugPrint(int blockIndex) const; |
150 | |
151 | /** |
152 | * Split given block. A new block will be created and all lines starting from the given index will |
153 | * be moved to it, together with the cursors belonging to it. |
154 | * @param fromLine line from which to split |
155 | * @return new block containing the lines + cursors removed from this one |
156 | */ |
157 | TextBlock *splitBlock(int fromLine); |
158 | |
159 | /** |
160 | * Merge this block with given one, the given one must be a direct predecessor. |
161 | * @param targetBlock block to merge with |
162 | */ |
163 | void mergeBlock(TextBlock *targetBlock); |
164 | |
165 | /** |
166 | * Delete the block content, delete all lines and delete all cursors not bound to ranges. |
167 | * This is used in destructor of TextBuffer, for fast cleanup. Only stuff remaining afterwards are cursors which are |
168 | * part of a range, TextBuffer will delete them itself... |
169 | */ |
170 | void deleteBlockContent(); |
171 | |
172 | /** |
173 | * Clear the block content, delete all lines, move all cursors not bound to range to given block at 0,0. |
174 | * This is used by clear() of TextBuffer. |
175 | * @param targetBlock empty target block for cursors |
176 | */ |
177 | void clearBlockContent(TextBlock *targetBlock); |
178 | |
179 | /** |
180 | * Return all ranges in this block which might intersect the given line. |
181 | * @param line line to check intersection |
182 | * @param view only return ranges associated with given view |
183 | * @param rangesWithAttributeOnly ranges with attributes only? |
184 | * @return list of possible candidate ranges |
185 | */ |
186 | KTEXTEDITOR_EXPORT QList<TextRange *> rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const; |
187 | |
188 | KTEXTEDITOR_NO_EXPORT void (int line, KTextEditor::View *view, bool rangesWithAttributeOnly, QList<TextRange *> &outRanges) const; |
189 | |
190 | /** |
191 | * Is the given range contained in this block? |
192 | * @param range range to check for |
193 | * @return contained in this blocks mapping? |
194 | */ |
195 | bool (TextRange *range) const |
196 | { |
197 | return m_cachedLineForRanges.find(key: range) != m_cachedLineForRanges.end() || m_uncachedRanges.contains(t: range); |
198 | } |
199 | |
200 | /** |
201 | * Flag all modified text lines as saved on disk. |
202 | */ |
203 | void markModifiedLinesAsSaved(); |
204 | |
205 | /** |
206 | * Insert cursor into this block. |
207 | * @param cursor cursor to insert |
208 | */ |
209 | void insertCursor(Kate::TextCursor *cursor) |
210 | { |
211 | m_cursors.insert(value: cursor); |
212 | } |
213 | |
214 | /** |
215 | * Remove cursor from this block. |
216 | * @param cursor cursor to remove |
217 | */ |
218 | void removeCursor(Kate::TextCursor *cursor) |
219 | { |
220 | m_cursors.remove(value: cursor); |
221 | } |
222 | |
223 | /** |
224 | * Update a range from this block. |
225 | * Will move the range to right set, either cached for one-line ranges or not. |
226 | * @param range range to update |
227 | */ |
228 | void (TextRange *range); |
229 | |
230 | /** |
231 | * Remove a range from this block. |
232 | * @param range range to remove |
233 | */ |
234 | void (TextRange *range); |
235 | |
236 | /** |
237 | * Returns the size of this block i.e., |
238 | * the count of QChars it has + number of new lines |
239 | */ |
240 | int blockSize() const |
241 | { |
242 | return m_blockSize + m_lines.size(); |
243 | } |
244 | |
245 | private: |
246 | /** |
247 | * Return all ranges in this block which might intersect the given line and only span one line. |
248 | * For them an internal fast lookup cache is hold. |
249 | * @param line line to check intersection |
250 | * @return set of ranges |
251 | */ |
252 | const QVarLengthArray<TextRange *, 6> *cachedRangesForLine(int line) const |
253 | { |
254 | line -= m_startLine; |
255 | if (line >= 0 && (size_t)line < m_cachedRangesForLine.size()) { |
256 | return &m_cachedRangesForLine[line]; |
257 | } else { |
258 | return nullptr; |
259 | } |
260 | } |
261 | |
262 | private: |
263 | /** |
264 | * parent text buffer |
265 | */ |
266 | TextBuffer *m_buffer; |
267 | |
268 | /** |
269 | * Lines contained in this buffer. |
270 | * We need no sharing, use STL. |
271 | */ |
272 | std::vector<Kate::TextLine> m_lines; |
273 | |
274 | /** |
275 | * Startline of this block |
276 | */ |
277 | int m_startLine; |
278 | |
279 | /** |
280 | * size of block i.e., number of QChars |
281 | */ |
282 | int m_blockSize = 0; |
283 | |
284 | /** |
285 | * Set of cursors for this block. |
286 | * using QSet is better than unordered_set for perf reasons |
287 | */ |
288 | QSet<TextCursor *> m_cursors; |
289 | |
290 | /** |
291 | * Contains for each line-offset the ranges that were cached into it. |
292 | * These ranges are fully contained by the line. |
293 | */ |
294 | std::vector<QVarLengthArray<TextRange *, 6>> m_cachedRangesForLine; |
295 | |
296 | /** |
297 | * Maps for each cached range the line into which the range was cached. |
298 | */ |
299 | QHash<TextRange *, int> m_cachedLineForRanges; |
300 | |
301 | /** |
302 | * This contains all the ranges that are not cached. |
303 | */ |
304 | QVarLengthArray<TextRange *, 1> m_uncachedRanges; |
305 | }; |
306 | |
307 | } |
308 | |
309 | #endif |
310 | |