File indexing completed on 2024-12-01 03:42:33

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