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 {
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
386Q_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
406private:
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 notifyAboutRangeChange(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
478public:
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 rangesForLine(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 addMultilineRange(TextRange *range);
520 KTEXTEDITOR_NO_EXPORT void removeMultilineRange(TextRange *range);
521 bool hasMultlineRange(KTextEditor::MovingRange *range) const;
522
523 //
524 // checksum handling
525 //
526public:
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
541private:
542 QByteArray m_digest;
543
544private:
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

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