1 | /* |
2 | SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | #ifndef KATE_TEXTBUFFER_H |
7 | #define KATE_TEXTBUFFER_H |
8 | |
9 | #include <QList> |
10 | #include <QObject> |
11 | #include <QSet> |
12 | #include <QString> |
13 | |
14 | #include "katetextblock.h" |
15 | #include "katetexthistory.h" |
16 | #include <ktexteditor_export.h> |
17 | |
18 | // encoding prober |
19 | #include <KEncodingProber> |
20 | |
21 | namespace KTextEditor |
22 | { |
23 | class DocumentPrivate; |
24 | } |
25 | |
26 | class KCompressionDevice; |
27 | |
28 | namespace Kate |
29 | { |
30 | class ; |
31 | class TextCursor; |
32 | class TextBlock; |
33 | |
34 | constexpr int BufferBlockSize = 64; |
35 | |
36 | /** |
37 | * Class representing a text buffer. |
38 | * The interface is line based, internally the text will be stored in blocks of text lines. |
39 | */ |
40 | class KTEXTEDITOR_EXPORT TextBuffer : public QObject |
41 | { |
42 | friend class TextCursor; |
43 | friend class TextRange; |
44 | friend class TextBlock; |
45 | |
46 | Q_OBJECT |
47 | |
48 | public: |
49 | /** |
50 | * End of line mode |
51 | */ |
52 | enum EndOfLineMode { |
53 | eolUnknown = -1, |
54 | eolUnix = 0, |
55 | eolDos = 1, |
56 | eolMac = 2 |
57 | }; |
58 | |
59 | /** |
60 | * Construct an empty text buffer. |
61 | * Empty means one empty line in one block. |
62 | * @param parent parent qobject |
63 | */ |
64 | explicit TextBuffer(KTextEditor::DocumentPrivate *parent, bool alwaysUseKAuth = false); |
65 | |
66 | /** |
67 | * Destruct the text buffer |
68 | * Virtual, we allow inheritance |
69 | */ |
70 | ~TextBuffer() override; |
71 | |
72 | /** |
73 | * Clears the buffer, reverts to initial empty state. |
74 | * Empty means one empty line in one block. |
75 | * Virtual, can be overwritten. |
76 | */ |
77 | virtual void clear(); |
78 | |
79 | /** |
80 | * Set encoding prober type for this buffer to use for load. |
81 | * @param proberType prober type to use for encoding |
82 | */ |
83 | void setEncodingProberType(KEncodingProber::ProberType proberType) |
84 | { |
85 | m_encodingProberType = proberType; |
86 | } |
87 | |
88 | /** |
89 | * Get encoding prober type for this buffer |
90 | * @return currently in use prober type of this buffer |
91 | */ |
92 | KEncodingProber::ProberType encodingProberType() const |
93 | { |
94 | return m_encodingProberType; |
95 | } |
96 | |
97 | /** |
98 | * Set fallback codec for this buffer to use for load. |
99 | * @param codec fallback to use for encoding |
100 | */ |
101 | void setFallbackTextCodec(const QString &codec) |
102 | { |
103 | m_fallbackTextCodec = codec; |
104 | } |
105 | |
106 | /** |
107 | * Get fallback codec for this buffer |
108 | * @return currently in use fallback codec of this buffer |
109 | */ |
110 | QString fallbackTextCodec() const |
111 | { |
112 | return m_fallbackTextCodec; |
113 | } |
114 | |
115 | /** |
116 | * Set codec for this buffer to use for load/save. |
117 | * Loading might overwrite this, if it encounters problems and finds a better codec. |
118 | * Might change BOM setting. |
119 | * @param codec to use for encoding |
120 | */ |
121 | void setTextCodec(const QString &codec); |
122 | |
123 | /** |
124 | * Get codec for this buffer |
125 | * @return currently in use codec of this buffer |
126 | */ |
127 | QString textCodec() const |
128 | { |
129 | return m_textCodec; |
130 | } |
131 | |
132 | /** |
133 | * Generate byte order mark on save. |
134 | * Loading might overwrite this setting, if there is a BOM found inside the file. |
135 | * @param generateByteOrderMark should BOM be generated? |
136 | */ |
137 | void setGenerateByteOrderMark(bool generateByteOrderMark) |
138 | { |
139 | m_generateByteOrderMark = generateByteOrderMark; |
140 | } |
141 | |
142 | /** |
143 | * Generate byte order mark on save? |
144 | * @return should BOM be generated? |
145 | */ |
146 | bool generateByteOrderMark() const |
147 | { |
148 | return m_generateByteOrderMark; |
149 | } |
150 | |
151 | /** |
152 | * Set end of line mode for this buffer, not allowed to be set to unknown. |
153 | * Loading might overwrite this setting, if there is a eol found inside the file. |
154 | * @param endOfLineMode new eol mode |
155 | */ |
156 | void setEndOfLineMode(EndOfLineMode endOfLineMode) |
157 | { |
158 | Q_ASSERT(endOfLineMode != eolUnknown); |
159 | m_endOfLineMode = endOfLineMode; |
160 | } |
161 | |
162 | /** |
163 | * Get end of line mode |
164 | * @return end of line mode |
165 | */ |
166 | EndOfLineMode endOfLineMode() const |
167 | { |
168 | return m_endOfLineMode; |
169 | } |
170 | |
171 | /** |
172 | * Set line length limit |
173 | * @param lineLengthLimit new line length limit |
174 | */ |
175 | void setLineLengthLimit(int lineLengthLimit) |
176 | { |
177 | m_lineLengthLimit = lineLengthLimit; |
178 | } |
179 | |
180 | /** |
181 | * Load the given file. This will first clear the buffer and then load the file. |
182 | * Even on error during loading the buffer will still be cleared. |
183 | * Before calling this, setTextCodec must have been used to set codec! |
184 | * @param filename file to open |
185 | * @param encodingErrors were there problems occurred while decoding the file? |
186 | * @param tooLongLinesWrapped were too long lines found and wrapped? |
187 | * @param longestLineLoaded the longest line in the file (before wrapping) |
188 | * @param enforceTextCodec enforce to use only the set text codec |
189 | * @return success, the file got loaded, perhaps with encoding errors |
190 | * Virtual, can be overwritten. |
191 | */ |
192 | virtual bool load(const QString &filename, bool &encodingErrors, bool &tooLongLinesWrapped, int &longestLineLoaded, bool enforceTextCodec); |
193 | |
194 | /** |
195 | * Save the current buffer content to the given file. |
196 | * Before calling this, setTextCodec and setFallbackTextCodec must have been used to set codec! |
197 | * @param filename file to save |
198 | * @return success |
199 | * Virtual, can be overwritten. |
200 | */ |
201 | virtual bool save(const QString &filename); |
202 | |
203 | /** |
204 | * Lines currently stored in this buffer. |
205 | * This is never 0, even clear will let one empty line remain. |
206 | */ |
207 | int lines() const |
208 | { |
209 | Q_ASSERT(m_lines > 0); |
210 | return m_lines; |
211 | } |
212 | |
213 | /** |
214 | * Revision of this buffer. Is set to 0 on construction, clear() (load will trigger clear()). |
215 | * Is incremented on each change to the buffer. |
216 | * @return current revision |
217 | */ |
218 | qint64 revision() const |
219 | { |
220 | return m_revision; |
221 | } |
222 | |
223 | /** |
224 | * Retrieve a text line. |
225 | * @param line wanted line number |
226 | * @return text line |
227 | */ |
228 | TextLine line(int line) const; |
229 | |
230 | /** |
231 | * Transfer all non text attributes for the given line from the given text line to the one in the buffer. |
232 | * @param line line number to set attributes |
233 | * @param textLine line reference to get attributes from |
234 | */ |
235 | void setLineMetaData(int line, const TextLine &textLine); |
236 | |
237 | /** |
238 | * Retrieve length for @p line |
239 | * @param line wanted line number |
240 | * @return length of the line |
241 | */ |
242 | int lineLength(int line) const |
243 | { |
244 | // get block, this will assert on invalid line |
245 | int blockIndex = blockForLine(line); |
246 | |
247 | // get line length |
248 | return m_blocks.at(n: blockIndex)->lineLength(line); |
249 | } |
250 | |
251 | /** |
252 | * Retrieve offset in text for the given cursor position |
253 | */ |
254 | int cursorToOffset(KTextEditor::Cursor c) const; |
255 | |
256 | /** |
257 | * Retrieve cursor in text for the given offset |
258 | */ |
259 | KTextEditor::Cursor offsetToCursor(int offset) const; |
260 | |
261 | /** |
262 | * Retrieve text of complete buffer. |
263 | * @return text for this buffer, lines separated by '\n' |
264 | */ |
265 | QString text() const; |
266 | |
267 | /** |
268 | * Start an editing transaction, the wrapLine/unwrapLine/insertText and removeText functions |
269 | * are only allowed to be called inside a editing transaction. |
270 | * Editing transactions can stack. The number of startEdit and endEdit calls must match. |
271 | * @return returns true, if no transaction was already running |
272 | * Virtual, can be overwritten. |
273 | */ |
274 | virtual bool startEditing(); |
275 | |
276 | /** |
277 | * Finish an editing transaction. Only allowed to be called if editing transaction is started. |
278 | * @return returns true, if this finished last running transaction |
279 | * Virtual, can be overwritten. |
280 | */ |
281 | virtual bool finishEditing(); |
282 | |
283 | /** |
284 | * Query the number of editing transactions running atm. |
285 | * @return number of running transactions |
286 | */ |
287 | int editingTransactions() const |
288 | { |
289 | return m_editingTransactions; |
290 | } |
291 | |
292 | /** |
293 | * Query the revision of this buffer before the ongoing editing transactions. |
294 | * @return revision of buffer before current editing transaction altered it |
295 | */ |
296 | qint64 editingLastRevision() const |
297 | { |
298 | return m_editingLastRevision; |
299 | } |
300 | |
301 | /** |
302 | * Query the number of lines of this buffer before the ongoing editing transactions. |
303 | * @return number of lines of buffer before current editing transaction altered it |
304 | */ |
305 | int editingLastLines() const |
306 | { |
307 | return m_editingLastLines; |
308 | } |
309 | |
310 | /** |
311 | * Query information from the last editing transaction: was the content of the buffer changed? |
312 | * This is checked by comparing the editingLastRevision() with the current revision(). |
313 | * @return content of buffer was changed in last transaction? |
314 | */ |
315 | bool editingChangedBuffer() const |
316 | { |
317 | return editingLastRevision() != revision(); |
318 | } |
319 | |
320 | /** |
321 | * Query information from the last editing transaction: was the number of lines of the buffer changed? |
322 | * This is checked by comparing the editingLastLines() with the current lines(). |
323 | * @return content of buffer was changed in last transaction? |
324 | */ |
325 | bool editingChangedNumberOfLines() const |
326 | { |
327 | return editingLastLines() != lines(); |
328 | } |
329 | |
330 | /** |
331 | * Get minimal line number changed by last editing transaction |
332 | * @return maximal line number changed by last editing transaction, or -1, if none changed |
333 | */ |
334 | int editingMinimalLineChanged() const |
335 | { |
336 | return m_editingMinimalLineChanged; |
337 | } |
338 | |
339 | /** |
340 | * Get maximal line number changed by last editing transaction |
341 | * @return maximal line number changed by last editing transaction, or -1, if none changed |
342 | */ |
343 | int editingMaximalLineChanged() const |
344 | { |
345 | return m_editingMaximalLineChanged; |
346 | } |
347 | |
348 | /** |
349 | * Wrap line at given cursor position. |
350 | * @param position line/column as cursor where to wrap |
351 | * Virtual, can be overwritten. |
352 | */ |
353 | virtual void wrapLine(const KTextEditor::Cursor position); |
354 | |
355 | /** |
356 | * Unwrap given line. |
357 | * @param line line to unwrap |
358 | * Virtual, can be overwritten. |
359 | */ |
360 | virtual void unwrapLine(int line); |
361 | |
362 | /** |
363 | * Insert text at given cursor position. Does nothing if text is empty, beside some consistency checks. |
364 | * @param position position where to insert text |
365 | * @param text text to insert |
366 | * Virtual, can be overwritten. |
367 | */ |
368 | virtual void insertText(const KTextEditor::Cursor position, const QString &text); |
369 | |
370 | /** |
371 | * Remove text at given range. Does nothing if range is empty, beside some consistency checks. |
372 | * @param range range of text to remove, must be on one line only. |
373 | * Virtual, can be overwritten. |
374 | */ |
375 | virtual void removeText(KTextEditor::Range range); |
376 | |
377 | /** |
378 | * TextHistory of this buffer |
379 | * @return text history for this buffer |
380 | */ |
381 | TextHistory &history() |
382 | { |
383 | return m_history; |
384 | } |
385 | |
386 | Q_SIGNALS: |
387 | /** |
388 | * Buffer got cleared. This is emitted when constructor or load have called clear() internally, |
389 | * or when the user of the buffer has called clear() itself. |
390 | */ |
391 | void cleared(); |
392 | |
393 | /** |
394 | * Buffer loaded successfully a file |
395 | * @param filename file which was loaded |
396 | * @param encodingErrors were there problems occurred while decoding the file? |
397 | */ |
398 | void loaded(const QString &filename, bool encodingErrors); |
399 | |
400 | /** |
401 | * Buffer saved successfully a file |
402 | * @param filename file which was saved |
403 | */ |
404 | void saved(const QString &filename); |
405 | |
406 | private: |
407 | /** |
408 | * Save result which indicates an abstract reason why the operation has |
409 | * failed |
410 | */ |
411 | enum class SaveResult { |
412 | Failed = 0, |
413 | MissingPermissions, |
414 | Success |
415 | }; |
416 | |
417 | /** |
418 | * Find block containing given line. |
419 | * @param line we want to find block for this line |
420 | * @return index of found block |
421 | */ |
422 | int blockForLine(int line) const; |
423 | // exported for movingrange_test |
424 | |
425 | /** |
426 | * Fix start lines of all blocks after the given one |
427 | * @param startBlock index of block from which we start to fix |
428 | */ |
429 | KTEXTEDITOR_NO_EXPORT |
430 | void fixStartLines(int startBlock, int value); |
431 | |
432 | /** |
433 | * Balance the given block. Look if it is too small or too large. |
434 | * @param index block to balance |
435 | */ |
436 | KTEXTEDITOR_NO_EXPORT |
437 | void balanceBlock(int index); |
438 | |
439 | /** |
440 | * A range changed, notify the views, in case of attributes or feedback. |
441 | * @param view which view is affected? nullptr for all views |
442 | * @param lineRange line range that the change spans |
443 | * @param needsRepaint do we need to trigger repaints? e.g. if ranges with attributes change |
444 | * @param deletedRange if set, points to the range that is deleted |
445 | */ |
446 | KTEXTEDITOR_NO_EXPORT |
447 | void (KTextEditor::View *view, KTextEditor::LineRange lineRange, bool needsRepaint, TextRange *deletedRange = nullptr); |
448 | |
449 | /** |
450 | * Mark all modified lines as lines saved on disk (modified line system). |
451 | */ |
452 | KTEXTEDITOR_NO_EXPORT |
453 | void markModifiedLinesAsSaved(); |
454 | |
455 | /** |
456 | * Save the current buffer content to the given already opened device |
457 | * |
458 | * @param filename path name for display/debugging purposes |
459 | * @param saveFile open device to write the buffer to |
460 | */ |
461 | KTEXTEDITOR_NO_EXPORT |
462 | bool saveBuffer(const QString &filename, KCompressionDevice &saveFile); |
463 | |
464 | /** |
465 | * Attempt to save the buffer content in the given filename location using |
466 | * current privileges. |
467 | */ |
468 | KTEXTEDITOR_NO_EXPORT |
469 | SaveResult saveBufferUnprivileged(const QString &filename); |
470 | |
471 | /** |
472 | * Attempt to save the buffer content in the given filename location using |
473 | * escalated privileges. |
474 | */ |
475 | KTEXTEDITOR_NO_EXPORT |
476 | bool saveBufferEscalated(const QString &filename); |
477 | |
478 | public: |
479 | /** |
480 | * Gets the document to which this buffer is bound. |
481 | * \return a pointer to the document |
482 | */ |
483 | KTextEditor::DocumentPrivate *document() const |
484 | { |
485 | return m_document; |
486 | } |
487 | |
488 | /** |
489 | * Debug output, print whole buffer content with line numbers and line length |
490 | * @param title title for this output |
491 | */ |
492 | void debugPrint(const QString &title) const; |
493 | |
494 | /** |
495 | * Return the ranges which affect the given line. |
496 | * @param line line to look at |
497 | * @param view only return ranges associated with given view |
498 | * @param rangesWithAttributeOnly only return ranges which have a attribute set |
499 | * @return list of ranges affecting this line |
500 | */ |
501 | QList<TextRange *> rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const |
502 | { |
503 | // get block, this will assert on invalid line |
504 | QList<TextRange *> outRanges; |
505 | rangesForLine(line, view, rangesWithAttributeOnly, outRanges); |
506 | return outRanges; |
507 | } |
508 | |
509 | void (int line, KTextEditor::View *view, bool rangesWithAttributeOnly, QList<TextRange *> &outRanges) const; |
510 | |
511 | /** |
512 | * Invalidate all ranges in this buffer. |
513 | */ |
514 | void invalidateRanges(); |
515 | |
516 | /** |
517 | * Add/Remove a multiline range that spans multiple blocks |
518 | */ |
519 | KTEXTEDITOR_NO_EXPORT void (TextRange *range); |
520 | KTEXTEDITOR_NO_EXPORT void (TextRange *range); |
521 | bool hasMultlineRange(KTextEditor::MovingRange *range) const; |
522 | |
523 | // |
524 | // checksum handling |
525 | // |
526 | public: |
527 | /** |
528 | * Checksum of the document on disk, set either through file loading |
529 | * in openFile() or in KTextEditor::DocumentPrivate::saveFile() |
530 | * @return git compatible sha1 checksum for this document |
531 | */ |
532 | const QByteArray &digest() const; |
533 | |
534 | /** |
535 | * Set the checksum of this buffer. Make sure this checksum is up-to-date |
536 | * when reading digest(). |
537 | * @param checksum git compatible sha1 digest for the document on disk |
538 | */ |
539 | void setDigest(const QByteArray &checksum); |
540 | |
541 | private: |
542 | QByteArray m_digest; |
543 | |
544 | private: |
545 | /** |
546 | * parent document |
547 | */ |
548 | KTextEditor::DocumentPrivate *m_document; |
549 | |
550 | /** |
551 | * text history |
552 | */ |
553 | TextHistory m_history; |
554 | |
555 | /** |
556 | * List of starting lines of the blocks in m_blocks |
557 | */ |
558 | std::vector<int> m_startLines; |
559 | |
560 | /** |
561 | * List of blocks which contain the lines of this buffer |
562 | */ |
563 | std::vector<TextBlock *> m_blocks; |
564 | |
565 | /** |
566 | * List of blocksizes of each block in m_blocks |
567 | */ |
568 | std::vector<int> m_blockSizes; |
569 | |
570 | /** |
571 | * Number of lines in buffer |
572 | */ |
573 | int m_lines; |
574 | |
575 | /** |
576 | * Revision of the buffer. |
577 | */ |
578 | qint64 m_revision; |
579 | |
580 | /** |
581 | * Current number of running edit transactions |
582 | */ |
583 | int m_editingTransactions; |
584 | |
585 | /** |
586 | * Revision remembered at start of current editing transaction |
587 | */ |
588 | qint64 m_editingLastRevision; |
589 | |
590 | /** |
591 | * Number of lines remembered at start of current editing transaction |
592 | */ |
593 | int m_editingLastLines; |
594 | |
595 | /** |
596 | * minimal line number changed by last editing transaction |
597 | */ |
598 | int m_editingMinimalLineChanged; |
599 | |
600 | /** |
601 | * maximal line number changed by last editing transaction |
602 | */ |
603 | int m_editingMaximalLineChanged; |
604 | |
605 | /** |
606 | * Multiline ranges that span multiple blocks |
607 | */ |
608 | std::vector<TextRange *> m_multilineRanges; |
609 | |
610 | /** |
611 | * Encoding prober type to use |
612 | */ |
613 | KEncodingProber::ProberType m_encodingProberType; |
614 | |
615 | /** |
616 | * Fallback text codec to use |
617 | */ |
618 | QString m_fallbackTextCodec; |
619 | |
620 | /** |
621 | * Text codec to use |
622 | */ |
623 | QString m_textCodec; |
624 | |
625 | /** |
626 | * Mime-Type used for transparent compression/decompression support |
627 | * Set by load(), reset by clear() |
628 | */ |
629 | QString m_mimeTypeForFilterDev; |
630 | |
631 | /** |
632 | * Should byte order mark be created? |
633 | */ |
634 | bool m_generateByteOrderMark; |
635 | |
636 | /** |
637 | * End of line mode, default is Unix |
638 | */ |
639 | EndOfLineMode m_endOfLineMode; |
640 | |
641 | /** |
642 | * Limit for line length, longer lines will be wrapped on load |
643 | */ |
644 | int m_lineLengthLimit; |
645 | |
646 | /** |
647 | * For unit-testing purposes only. |
648 | */ |
649 | bool m_alwaysUseKAuthForSave; |
650 | |
651 | /** |
652 | * For copying QBuffer -> QTemporaryFile while saving document in privileged mode |
653 | */ |
654 | static const qint64 bufferLength = 4096; |
655 | }; |
656 | |
657 | } |
658 | |
659 | #endif |
660 | |