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

source code of ktexteditor/src/buffer/katetextbuffer.h