File indexing completed on 2023-09-24 04:11:27
0001 /* 0002 SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 #ifndef KATE_TEXTBUFFER_H 0007 #define KATE_TEXTBUFFER_H 0008 0009 #include <QObject> 0010 #include <QSet> 0011 #include <QString> 0012 #include <QTextCodec> 0013 #include <QVector> 0014 0015 #include "katetextblock.h" 0016 #include "katetexthistory.h" 0017 #include <ktexteditor_export.h> 0018 0019 // encoding prober 0020 #include <KEncodingProber> 0021 0022 namespace KTextEditor 0023 { 0024 class DocumentPrivate; 0025 } 0026 0027 class KCompressionDevice; 0028 0029 namespace Kate 0030 { 0031 class TextRange; 0032 class TextCursor; 0033 class TextBlock; 0034 class TextLineData; 0035 typedef std::shared_ptr<TextLineData> TextLine; 0036 /** 0037 * Class representing a text buffer. 0038 * The interface is line based, internally the text will be stored in blocks of text lines. 0039 */ 0040 class KTEXTEDITOR_EXPORT TextBuffer : public QObject 0041 { 0042 friend class TextCursor; 0043 friend class TextRange; 0044 friend class TextBlock; 0045 0046 Q_OBJECT 0047 0048 public: 0049 /** 0050 * End of line mode 0051 */ 0052 enum EndOfLineMode { eolUnknown = -1, eolUnix = 0, eolDos = 1, eolMac = 2 }; 0053 0054 /** 0055 * Construct an empty text buffer. 0056 * Empty means one empty line in one block. 0057 * @param parent parent qobject 0058 * @param blockSize block size in lines the buffer should try to hold, default 64 lines 0059 * @param alwaysUseKAuth only set this for unit testing purposes 0060 */ 0061 explicit TextBuffer(KTextEditor::DocumentPrivate *parent, int blockSize = 64, bool alwaysUseKAuth = false); 0062 0063 /** 0064 * Destruct the text buffer 0065 * Virtual, we allow inheritance 0066 */ 0067 ~TextBuffer() override; 0068 0069 /** 0070 * Clears the buffer, reverts to initial empty state. 0071 * Empty means one empty line in one block. 0072 * Virtual, can be overwritten. 0073 */ 0074 virtual void clear(); 0075 0076 /** 0077 * Set encoding prober type for this buffer to use for load. 0078 * @param proberType prober type to use for encoding 0079 */ 0080 void setEncodingProberType(KEncodingProber::ProberType proberType) 0081 { 0082 m_encodingProberType = proberType; 0083 } 0084 0085 /** 0086 * Get encoding prober type for this buffer 0087 * @return currently in use prober type of this buffer 0088 */ 0089 KEncodingProber::ProberType encodingProberType() const 0090 { 0091 return m_encodingProberType; 0092 } 0093 0094 /** 0095 * Set fallback codec for this buffer to use for load. 0096 * @param codec fallback QTextCodec to use for encoding 0097 */ 0098 void setFallbackTextCodec(QTextCodec *codec) 0099 { 0100 m_fallbackTextCodec = codec; 0101 } 0102 0103 /** 0104 * Get fallback codec for this buffer 0105 * @return currently in use fallback codec of this buffer 0106 */ 0107 QTextCodec *fallbackTextCodec() const 0108 { 0109 return m_fallbackTextCodec; 0110 } 0111 0112 /** 0113 * Set codec for this buffer to use for load/save. 0114 * Loading might overwrite this, if it encounters problems and finds a better codec. 0115 * Might change BOM setting. 0116 * @param codec QTextCodec to use for encoding 0117 */ 0118 void setTextCodec(QTextCodec *codec); 0119 0120 /** 0121 * Get codec for this buffer 0122 * @return currently in use codec of this buffer 0123 */ 0124 QTextCodec *textCodec() const 0125 { 0126 return m_textCodec; 0127 } 0128 0129 /** 0130 * Generate byte order mark on save. 0131 * Loading might overwrite this setting, if there is a BOM found inside the file. 0132 * @param generateByteOrderMark should BOM be generated? 0133 */ 0134 void setGenerateByteOrderMark(bool generateByteOrderMark) 0135 { 0136 m_generateByteOrderMark = generateByteOrderMark; 0137 } 0138 0139 /** 0140 * Generate byte order mark on save? 0141 * @return should BOM be generated? 0142 */ 0143 bool generateByteOrderMark() const 0144 { 0145 return m_generateByteOrderMark; 0146 } 0147 0148 /** 0149 * Set end of line mode for this buffer, not allowed to be set to unknown. 0150 * Loading might overwrite this setting, if there is a eol found inside the file. 0151 * @param endOfLineMode new eol mode 0152 */ 0153 void setEndOfLineMode(EndOfLineMode endOfLineMode) 0154 { 0155 Q_ASSERT(endOfLineMode != eolUnknown); 0156 m_endOfLineMode = endOfLineMode; 0157 } 0158 0159 /** 0160 * Get end of line mode 0161 * @return end of line mode 0162 */ 0163 EndOfLineMode endOfLineMode() const 0164 { 0165 return m_endOfLineMode; 0166 } 0167 0168 /** 0169 * Set line length limit 0170 * @param lineLengthLimit new line length limit 0171 */ 0172 void setLineLengthLimit(int lineLengthLimit) 0173 { 0174 m_lineLengthLimit = lineLengthLimit; 0175 } 0176 0177 /** 0178 * Load the given file. This will first clear the buffer and then load the file. 0179 * Even on error during loading the buffer will still be cleared. 0180 * Before calling this, setTextCodec must have been used to set codec! 0181 * @param filename file to open 0182 * @param encodingErrors were there problems occurred while decoding the file? 0183 * @param tooLongLinesWrapped were too long lines found and wrapped? 0184 * @param longestLineLoaded the longest line in the file (before wrapping) 0185 * @param enforceTextCodec enforce to use only the set text codec 0186 * @return success, the file got loaded, perhaps with encoding errors 0187 * Virtual, can be overwritten. 0188 */ 0189 virtual bool load(const QString &filename, bool &encodingErrors, bool &tooLongLinesWrapped, int &longestLineLoaded, bool enforceTextCodec); 0190 0191 /** 0192 * Save the current buffer content to the given file. 0193 * Before calling this, setTextCodec and setFallbackTextCodec must have been used to set codec! 0194 * @param filename file to save 0195 * @return success 0196 * Virtual, can be overwritten. 0197 */ 0198 virtual bool save(const QString &filename); 0199 0200 /** 0201 * Lines currently stored in this buffer. 0202 * This is never 0, even clear will let one empty line remain. 0203 */ 0204 int lines() const 0205 { 0206 Q_ASSERT(m_lines > 0); 0207 return m_lines; 0208 } 0209 0210 /** 0211 * Revision of this buffer. Is set to 0 on construction, clear() (load will trigger clear()). 0212 * Is incremented on each change to the buffer. 0213 * @return current revision 0214 */ 0215 qint64 revision() const 0216 { 0217 return m_revision; 0218 } 0219 0220 /** 0221 * Retrieve a text line. 0222 * @param line wanted line number 0223 * @return text line 0224 */ 0225 TextLine line(int line) const; 0226 0227 /** 0228 * Retrieve length for @p line 0229 * @param line wanted line number 0230 * @return length of the line 0231 */ 0232 int lineLength(int line) const 0233 { 0234 // get block, this will assert on invalid line 0235 int blockIndex = blockForLine(line); 0236 0237 // get line length 0238 return m_blocks.at(blockIndex)->lineLength(line); 0239 } 0240 0241 /** 0242 * Retrieve text of complete buffer. 0243 * @return text for this buffer, lines separated by '\n' 0244 */ 0245 QString text() const; 0246 0247 /** 0248 * Start an editing transaction, the wrapLine/unwrapLine/insertText and removeText functions 0249 * are only allowed to be called inside a editing transaction. 0250 * Editing transactions can stack. The number of startEdit and endEdit calls must match. 0251 * @return returns true, if no transaction was already running 0252 * Virtual, can be overwritten. 0253 */ 0254 virtual bool startEditing(); 0255 0256 /** 0257 * Finish an editing transaction. Only allowed to be called if editing transaction is started. 0258 * @return returns true, if this finished last running transaction 0259 * Virtual, can be overwritten. 0260 */ 0261 virtual bool finishEditing(); 0262 0263 /** 0264 * Query the number of editing transactions running atm. 0265 * @return number of running transactions 0266 */ 0267 int editingTransactions() const 0268 { 0269 return m_editingTransactions; 0270 } 0271 0272 /** 0273 * Query the revision of this buffer before the ongoing editing transactions. 0274 * @return revision of buffer before current editing transaction altered it 0275 */ 0276 qint64 editingLastRevision() const 0277 { 0278 return m_editingLastRevision; 0279 } 0280 0281 /** 0282 * Query the number of lines of this buffer before the ongoing editing transactions. 0283 * @return number of lines of buffer before current editing transaction altered it 0284 */ 0285 int editingLastLines() const 0286 { 0287 return m_editingLastLines; 0288 } 0289 0290 /** 0291 * Query information from the last editing transaction: was the content of the buffer changed? 0292 * This is checked by comparing the editingLastRevision() with the current revision(). 0293 * @return content of buffer was changed in last transaction? 0294 */ 0295 bool editingChangedBuffer() const 0296 { 0297 return editingLastRevision() != revision(); 0298 } 0299 0300 /** 0301 * Query information from the last editing transaction: was the number of lines of the buffer changed? 0302 * This is checked by comparing the editingLastLines() with the current lines(). 0303 * @return content of buffer was changed in last transaction? 0304 */ 0305 bool editingChangedNumberOfLines() const 0306 { 0307 return editingLastLines() != lines(); 0308 } 0309 0310 /** 0311 * Get minimal line number changed by last editing transaction 0312 * @return maximal line number changed by last editing transaction, or -1, if none changed 0313 */ 0314 int editingMinimalLineChanged() const 0315 { 0316 return m_editingMinimalLineChanged; 0317 } 0318 0319 /** 0320 * Get maximal line number changed by last editing transaction 0321 * @return maximal line number changed by last editing transaction, or -1, if none changed 0322 */ 0323 int editingMaximalLineChanged() const 0324 { 0325 return m_editingMaximalLineChanged; 0326 } 0327 0328 /** 0329 * Wrap line at given cursor position. 0330 * @param position line/column as cursor where to wrap 0331 * Virtual, can be overwritten. 0332 */ 0333 virtual void wrapLine(const KTextEditor::Cursor position); 0334 0335 /** 0336 * Unwrap given line. 0337 * @param line line to unwrap 0338 * Virtual, can be overwritten. 0339 */ 0340 virtual void unwrapLine(int line); 0341 0342 /** 0343 * Insert text at given cursor position. Does nothing if text is empty, beside some consistency checks. 0344 * @param position position where to insert text 0345 * @param text text to insert 0346 * Virtual, can be overwritten. 0347 */ 0348 virtual void insertText(const KTextEditor::Cursor position, const QString &text); 0349 0350 /** 0351 * Remove text at given range. Does nothing if range is empty, beside some consistency checks. 0352 * @param range range of text to remove, must be on one line only. 0353 * Virtual, can be overwritten. 0354 */ 0355 virtual void removeText(KTextEditor::Range range); 0356 0357 /** 0358 * TextHistory of this buffer 0359 * @return text history for this buffer 0360 */ 0361 TextHistory &history() 0362 { 0363 return m_history; 0364 } 0365 0366 Q_SIGNALS: 0367 /** 0368 * Buffer got cleared. This is emitted when constructor or load have called clear() internally, 0369 * or when the user of the buffer has called clear() itself. 0370 */ 0371 void cleared(); 0372 0373 /** 0374 * Buffer loaded successfully a file 0375 * @param filename file which was loaded 0376 * @param encodingErrors were there problems occurred while decoding the file? 0377 */ 0378 void loaded(const QString &filename, bool encodingErrors); 0379 0380 /** 0381 * Buffer saved successfully a file 0382 * @param filename file which was saved 0383 */ 0384 void saved(const QString &filename); 0385 0386 /** 0387 * Editing transaction has started. 0388 */ 0389 void editingStarted(); 0390 0391 /** 0392 * Editing transaction has finished. 0393 */ 0394 void editingFinished(); 0395 0396 /** 0397 * A line got wrapped. 0398 * @param position position where the wrap occurred 0399 */ 0400 void lineWrapped(const KTextEditor::Cursor position); 0401 0402 /** 0403 * A line got unwrapped. 0404 * @param line line where the unwrap occurred 0405 */ 0406 void lineUnwrapped(int line); 0407 0408 /** 0409 * Text got inserted. 0410 * @param position position where the insertion occurred 0411 * @param text inserted text 0412 */ 0413 void textInserted(const KTextEditor::Cursor position, const QString &text); 0414 0415 /** 0416 * Text got removed. 0417 * @param range range where the removal occurred 0418 * @param text removed text 0419 */ 0420 void textRemoved(KTextEditor::Range range, const QString &text); 0421 0422 private: 0423 /** 0424 * Save result which indicates an abstract reason why the operation has 0425 * failed 0426 */ 0427 enum class SaveResult { Failed = 0, MissingPermissions, Success }; 0428 0429 /** 0430 * Find block containing given line. 0431 * @param line we want to find block for this line 0432 * @return index of found block 0433 */ 0434 int blockForLine(int line) const; 0435 // exported for movingrange_test 0436 0437 /** 0438 * Fix start lines of all blocks after the given one 0439 * @param startBlock index of block from which we start to fix 0440 */ 0441 KTEXTEDITOR_NO_EXPORT 0442 void fixStartLines(int startBlock); 0443 0444 /** 0445 * Balance the given block. Look if it is too small or too large. 0446 * @param index block to balance 0447 */ 0448 KTEXTEDITOR_NO_EXPORT 0449 void balanceBlock(int index); 0450 0451 /** 0452 * Block for given index in block list. 0453 * @param index block index 0454 * @return block matching this index 0455 */ 0456 TextBlock *blockForIndex(int index) 0457 { 0458 return m_blocks[index]; 0459 } 0460 0461 /** 0462 * A range changed, notify the views, in case of attributes or feedback. 0463 * @param view which view is affected? nullptr for all views 0464 * @param lineRange line range that the change spans 0465 * @param needsRepaint do we need to trigger repaints? e.g. if ranges with attributes change 0466 */ 0467 KTEXTEDITOR_NO_EXPORT 0468 void notifyAboutRangeChange(KTextEditor::View *view, KTextEditor::LineRange lineRange, bool needsRepaint); 0469 0470 /** 0471 * Mark all modified lines as lines saved on disk (modified line system). 0472 */ 0473 KTEXTEDITOR_NO_EXPORT 0474 void markModifiedLinesAsSaved(); 0475 0476 /** 0477 * Save the current buffer content to the given already opened device 0478 * 0479 * @param filename path name for display/debugging purposes 0480 * @param saveFile open device to write the buffer to 0481 */ 0482 KTEXTEDITOR_NO_EXPORT 0483 bool saveBuffer(const QString &filename, KCompressionDevice &saveFile); 0484 0485 /** 0486 * Attempt to save the buffer content in the given filename location using 0487 * current privileges. 0488 */ 0489 KTEXTEDITOR_NO_EXPORT 0490 SaveResult saveBufferUnprivileged(const QString &filename); 0491 0492 /** 0493 * Attempt to save the buffer content in the given filename location using 0494 * escalated privileges. 0495 */ 0496 KTEXTEDITOR_NO_EXPORT 0497 bool saveBufferEscalated(const QString &filename); 0498 0499 public: 0500 /** 0501 * Gets the document to which this buffer is bound. 0502 * \return a pointer to the document 0503 */ 0504 KTextEditor::DocumentPrivate *document() const 0505 { 0506 return m_document; 0507 } 0508 0509 /** 0510 * Debug output, print whole buffer content with line numbers and line length 0511 * @param title title for this output 0512 */ 0513 void debugPrint(const QString &title) const; 0514 0515 /** 0516 * Return the ranges which affect the given line. 0517 * @param line line to look at 0518 * @param view only return ranges associated with given view 0519 * @param rangesWithAttributeOnly only return ranges which have a attribute set 0520 * @return list of ranges affecting this line 0521 */ 0522 QVector<TextRange *> rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const 0523 { 0524 // get block, this will assert on invalid line 0525 const int blockIndex = blockForLine(line); 0526 return m_blocks.at(blockIndex)->rangesForLine(line, view, rangesWithAttributeOnly); 0527 } 0528 0529 /** 0530 * Check if the given range pointer is still valid. 0531 * @return range pointer still belongs to range for this buffer 0532 */ 0533 bool rangePointerValid(TextRange *range) const 0534 { 0535 return m_ranges.contains(range); 0536 } 0537 0538 /** 0539 * Invalidate all ranges in this buffer. 0540 */ 0541 void invalidateRanges(); 0542 0543 // 0544 // checksum handling 0545 // 0546 public: 0547 /** 0548 * Checksum of the document on disk, set either through file loading 0549 * in openFile() or in KTextEditor::DocumentPrivate::saveFile() 0550 * @return git compatible sha1 checksum for this document 0551 */ 0552 const QByteArray &digest() const; 0553 0554 /** 0555 * Set the checksum of this buffer. Make sure this checksum is up-to-date 0556 * when reading digest(). 0557 * @param checksum git compatible sha1 digest for the document on disk 0558 */ 0559 void setDigest(const QByteArray &checksum); 0560 0561 private: 0562 QByteArray m_digest; 0563 0564 private: 0565 /** 0566 * parent document 0567 */ 0568 KTextEditor::DocumentPrivate *m_document; 0569 0570 /** 0571 * text history 0572 */ 0573 TextHistory m_history; 0574 0575 /** 0576 * block size in lines the buffer will try to hold 0577 */ 0578 const int m_blockSize; 0579 0580 /** 0581 * List of blocks which contain the lines of this buffer 0582 */ 0583 std::vector<TextBlock *> m_blocks; 0584 0585 /** 0586 * Number of lines in buffer 0587 */ 0588 int m_lines; 0589 0590 /** 0591 * Last used block in the buffer. Is used for speeding up blockForLine. 0592 * May contain invalid index, must be checked before using. 0593 */ 0594 mutable int m_lastUsedBlock; 0595 0596 /** 0597 * Revision of the buffer. 0598 */ 0599 qint64 m_revision; 0600 0601 /** 0602 * Current number of running edit transactions 0603 */ 0604 int m_editingTransactions; 0605 0606 /** 0607 * Revision remembered at start of current editing transaction 0608 */ 0609 qint64 m_editingLastRevision; 0610 0611 /** 0612 * Number of lines remembered at start of current editing transaction 0613 */ 0614 int m_editingLastLines; 0615 0616 /** 0617 * minimal line number changed by last editing transaction 0618 */ 0619 int m_editingMinimalLineChanged; 0620 0621 /** 0622 * maximal line number changed by last editing transaction 0623 */ 0624 int m_editingMaximalLineChanged; 0625 0626 /** 0627 * Set of invalid cursors for this whole buffer. 0628 * Valid cursors are inside the block the belong to. 0629 */ 0630 QSet<TextCursor *> m_invalidCursors; 0631 0632 /** 0633 * Set of ranges of this whole buffer. 0634 */ 0635 QSet<TextRange *> m_ranges; 0636 0637 /** 0638 * Encoding prober type to use 0639 */ 0640 KEncodingProber::ProberType m_encodingProberType; 0641 0642 /** 0643 * Fallback text codec to use 0644 */ 0645 QTextCodec *m_fallbackTextCodec; 0646 0647 /** 0648 * Text codec to use 0649 */ 0650 QTextCodec *m_textCodec; 0651 0652 /** 0653 * Mime-Type used for transparent compression/decompression support 0654 * Set by load(), reset by clear() 0655 */ 0656 QString m_mimeTypeForFilterDev; 0657 0658 /** 0659 * Should byte order mark be created? 0660 */ 0661 bool m_generateByteOrderMark; 0662 0663 /** 0664 * End of line mode, default is Unix 0665 */ 0666 EndOfLineMode m_endOfLineMode; 0667 0668 /** 0669 * Limit for line length, longer lines will be wrapped on load 0670 */ 0671 int m_lineLengthLimit; 0672 0673 /** 0674 * For unit-testing purposes only. 0675 */ 0676 bool m_alwaysUseKAuthForSave; 0677 0678 /** 0679 * For copying QBuffer -> QTemporaryFile while saving document in privileged mode 0680 */ 0681 static const qint64 bufferLength = 4096; 0682 }; 0683 0684 } 0685 0686 #endif