// Scintilla source code edit control /** @file UndoHistory.h ** Manages undo for the document. **/ // Copyright 1998-2024 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #ifndef UNDOHISTORY_H #define UNDOHISTORY_H namespace Scintilla::Internal { // ScaledVector is a vector of unsigned integers that uses elements sized to hold the largest value. // Thus, if an undo history only contains short insertions and deletions the lengths vector may // only use 2 bytes or even 1 byte for each length. // This saves much memory often reducing by 50% for 32-bit builds and 75% for 64-bit builds. struct SizeMax { size_t size = 1; size_t maxValue = UINT8_MAX; }; class ScaledVector { SizeMax element; std::vector bytes; public: [[nodiscard]] size_t Size() const noexcept; [[nodiscard]] size_t ValueAt(size_t index) const noexcept; [[nodiscard]] intptr_t SignedValueAt(size_t index) const noexcept; void SetValueAt(size_t index, size_t value); void ClearValueAt(size_t index) noexcept; void Clear() noexcept; void Truncate(size_t length) noexcept; void ReSize(size_t length); void PushBack(); // For testing [[nodiscard]] size_t SizeInBytes() const noexcept; }; class UndoActionType { public: ActionType at : 4; bool mayCoalesce : 1; UndoActionType() noexcept; }; struct UndoActions { std::vector types; ScaledVector positions; ScaledVector lengths; UndoActions() noexcept; void Truncate(size_t length) noexcept; void PushBack(); void Clear() noexcept; [[nodiscard]] intptr_t SSize() const noexcept; void Create(size_t index, ActionType at_, Sci::Position position_, Sci::Position lenData_, bool mayCoalesce_); [[nodiscard]] bool AtStart(size_t index) const noexcept; [[nodiscard]] size_t LengthTo(size_t index) const noexcept; [[nodiscard]] Sci::Position Position(int action) const noexcept; [[nodiscard]] Sci::Position Length(int action) const noexcept; }; class ScrapStack { std::string stack; size_t current = 0; public: void Clear() noexcept; const char *Push(const char *text, size_t length); void SetCurrent(size_t position) noexcept; void MoveForward(size_t length) noexcept; void MoveBack(size_t length) noexcept; [[nodiscard]] const char *CurrentText() const noexcept; [[nodiscard]] const char *TextAt(size_t position) const noexcept; }; constexpr int coalesceFlag = 0x100; /** * */ class UndoHistory { UndoActions actions; int currentAction = 0; int undoSequenceDepth = 0; int savePoint = 0; int tentativePoint = -1; std::optional detach; // Never set if savePoint set (>= 0) std::unique_ptr scraps; struct actPos { int act; size_t position; }; std::optional memory; int PreviousAction() const noexcept; public: UndoHistory(); ~UndoHistory() noexcept; const char *AppendAction(ActionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true); void BeginUndoAction(bool mayCoalesce=false) noexcept; void EndUndoAction() noexcept; void DropUndoSequence() noexcept; void DeleteUndoHistory() noexcept; [[nodiscard]] int Actions() const noexcept; /// The save point is a marker in the undo stack where the container has stated that /// the buffer was saved. Undo and redo can move over the save point. void SetSavePoint(int action) noexcept; [[nodiscard]] int SavePoint() const noexcept; void SetSavePoint() noexcept; bool IsSavePoint() const noexcept; bool BeforeSavePoint() const noexcept; bool PreviousBeforeSavePoint() const noexcept; bool BeforeReachableSavePoint() const noexcept; bool AfterSavePoint() const noexcept; /// The detach point is the last action that was before an inaccessible missing save point. void SetDetachPoint(int action) noexcept; [[nodiscard]] int DetachPoint() const noexcept; bool AfterDetachPoint() const noexcept; bool AfterOrAtDetachPoint() const noexcept; [[nodiscard]] intptr_t Delta(int action) const noexcept; [[nodiscard]] bool Validate(intptr_t lengthDocument) const noexcept; void SetCurrent(int action, intptr_t lengthDocument); [[nodiscard]] int Current() const noexcept; [[nodiscard]] int Type(int action) const noexcept; [[nodiscard]] Sci::Position Position(int action) const noexcept; [[nodiscard]] Sci::Position Length(int action) const noexcept; [[nodiscard]] std::string_view Text(int action) noexcept; void PushUndoActionType(int type, Sci::Position position); void ChangeLastUndoActionText(size_t length, const char *text); // Tentative actions are used for input composition so that it can be undone cleanly void SetTentative(int action) noexcept; [[nodiscard]] int TentativePoint() const noexcept; void TentativeStart() noexcept; void TentativeCommit() noexcept; bool TentativeActive() const noexcept; int TentativeSteps() const noexcept; /// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is /// called that many times. Similarly for redo. bool CanUndo() const noexcept; int StartUndo() const noexcept; Action GetUndoStep() const noexcept; void CompletedUndoStep() noexcept; bool CanRedo() const noexcept; int StartRedo() const noexcept; Action GetRedoStep() const noexcept; void CompletedRedoStep() noexcept; }; } #endif