diff options
author | Neil <nyamatongwe@gmail.com> | 2024-02-15 14:17:08 +1100 |
---|---|---|
committer | Neil <nyamatongwe@gmail.com> | 2024-02-15 14:17:08 +1100 |
commit | 38d2856d341358bf9c691157ae0ca2e8960e35df (patch) | |
tree | a1a187630421e954b65181bb2d2fb4a58d2549ff | |
parent | a6a2fe933e0103464c1a357f987ad10e3bcf6622 (diff) | |
download | scintilla-mirror-38d2856d341358bf9c691157ae0ca2e8960e35df.tar.gz |
Perform validation of undo state when SCI_SETUNDOCURRENT called, setting status
when invalid.
-rw-r--r-- | doc/ScintillaDoc.html | 14 | ||||
-rw-r--r-- | src/CellBuffer.cxx | 4 | ||||
-rw-r--r-- | src/CellBuffer.h | 2 | ||||
-rw-r--r-- | src/Document.cxx | 2 | ||||
-rw-r--r-- | src/Document.h | 2 | ||||
-rw-r--r-- | src/UndoHistory.cxx | 54 | ||||
-rw-r--r-- | src/UndoHistory.h | 5 |
7 files changed, 68 insertions, 15 deletions
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 12f2be0eb..4a10d280f 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -2063,9 +2063,9 @@ struct Sci_TextToFindFull { </p> <p>The history should first be set up with <code>SCI_PUSHUNDOACTIONTYPE</code>, and - <code>SCI_CHANGELASTUNDOACTIONTEXT</code> then the save, current, and tentativ points set - with <code>SCI_SETUNDOSAVEPOINT</code>, <code>SCI_SETUNDOCURRENT</code>, and - <code>SCI_SETUNDOTENTATIVE</code>. + <code>SCI_CHANGELASTUNDOACTIONTEXT</code> then the save, current, and tentative points set + with <code>SCI_SETUNDOSAVEPOINT</code>, <code>SCI_SETUNDOTENTATIVE</code>, and + <code>SCI_SETUNDOCURRENT</code>. </p> <p><code>SCI_PUSHUNDOACTIONTYPE(int type, position pos)</code> appends an action to the undo stack @@ -2073,6 +2073,14 @@ struct Sci_TextToFindFull { <code>SCI_CHANGELASTUNDOACTIONTEXT(position length, const char *text)</code>. </p> + <p> + The last restoration API called should be <code>SCI_SETUNDOCURRENT</code> as this validates + the restored history and values against the document. For example, an undo history that could cause a negative + document length or inserting / removing text outside the document is invalid. + If the restored undo state is invalid then a failure status is set and the undo history cleared. + Check for failure with <a class="seealso" href="#SCI_GETSTATUS">SCI_GETSTATUS</a>. + </p> + <p>The current implementation may only work when the current and save point are the same and there is no tentative point. </p> diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx index b3111914e..691a811dd 100644 --- a/src/CellBuffer.cxx +++ b/src/CellBuffer.cxx @@ -1160,8 +1160,8 @@ int CellBuffer::UndoSavePoint() const noexcept { return uh->SavePoint(); } -void CellBuffer::SetUndoCurrent(int action) noexcept { - uh->SetCurrent(action); +void CellBuffer::SetUndoCurrent(int action) { + uh->SetCurrent(action, Length()); } int CellBuffer::UndoCurrent() const noexcept { diff --git a/src/CellBuffer.h b/src/CellBuffer.h index 5422e92aa..916937845 100644 --- a/src/CellBuffer.h +++ b/src/CellBuffer.h @@ -183,7 +183,7 @@ public: int UndoActions() const noexcept; void SetUndoSavePoint(int action) noexcept; int UndoSavePoint() const noexcept; - void SetUndoCurrent(int action) noexcept; + void SetUndoCurrent(int action); int UndoCurrent() const noexcept; void SetUndoTentative(int action) noexcept; int UndoTentative() const noexcept; diff --git a/src/Document.cxx b/src/Document.cxx index 52081e1cd..8ab837daa 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -363,7 +363,7 @@ int Document::UndoSavePoint() const noexcept { return cb.UndoSavePoint(); } -void Document::SetUndoCurrent(int action) noexcept { +void Document::SetUndoCurrent(int action) { cb.SetUndoCurrent(action); } diff --git a/src/Document.h b/src/Document.h index 185066a6c..81cb299c0 100644 --- a/src/Document.h +++ b/src/Document.h @@ -409,7 +409,7 @@ public: int UndoActions() const noexcept; void SetUndoSavePoint(int action) noexcept; int UndoSavePoint() const noexcept; - void SetUndoCurrent(int action) noexcept; + void SetUndoCurrent(int action); int UndoCurrent() const noexcept; void SetUndoTentative(int action) noexcept; int UndoTentative() const noexcept; diff --git a/src/UndoHistory.cxx b/src/UndoHistory.cxx index 1a956b998..62f871a60 100644 --- a/src/UndoHistory.cxx +++ b/src/UndoHistory.cxx @@ -173,6 +173,14 @@ bool UndoActions::AtStart(size_t index) const noexcept { return !types[index-1].mayCoalesce; } +size_t UndoActions::LengthTo(size_t index) const noexcept { + size_t sum = 0; + for (size_t act = 0; act < index; act++) { + sum += lengths.ValueAt(act); + } + return sum; +} + void ScrapStack::Clear() noexcept { stack.clear(); current = 0; @@ -395,15 +403,49 @@ bool UndoHistory::AfterOrAtDetachPoint() const noexcept { return detach && (*detach <= currentAction); } -void UndoHistory::SetCurrent(int action) noexcept { - // Find position in scraps for action - memory = {}; - size_t position = 0; +intptr_t UndoHistory::Delta(int action) noexcept { + intptr_t sizeChange = 0; for (int act = 0; act < action; act++) { - position += actions.lengths.ValueAt(act); + const intptr_t lengthChange = actions.lengths.SignedValueAt(act); + sizeChange += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange; + } + return sizeChange; +} + +bool UndoHistory::Validate(intptr_t lengthDocument) noexcept { + // Check history for validity + const intptr_t sizeChange = Delta(currentAction); + if (sizeChange > lengthDocument) { + // Current document size too small for changes made in undo history. + return false; } - scraps->SetCurrent(position); + const intptr_t lengthOriginal = lengthDocument - sizeChange; + intptr_t lengthCurrent = lengthOriginal; + for (int act = 0; act < actions.SSize(); act++) { + const intptr_t lengthChange = actions.lengths.SignedValueAt(act); + if (actions.positions.SignedValueAt(act) > lengthCurrent) { + // Change outside document. + return false; + } + lengthCurrent += (actions.types[act].at == ActionType::insert) ? lengthChange : -lengthChange; + if (lengthCurrent < 0) { + return false; + } + } + return true; +} + +void UndoHistory::SetCurrent(int action, intptr_t lengthDocument) { + // Find position in scraps for action + memory = {}; + const size_t lengthSum = actions.LengthTo(action); + scraps->SetCurrent(lengthSum); currentAction = action; + if (!Validate(lengthDocument)) { + currentAction = 0; + DeleteUndoHistory(); + throw std::runtime_error("UndoHistory::SetCurrent: invalid undo history."); + } } int UndoHistory::Current() const noexcept { diff --git a/src/UndoHistory.h b/src/UndoHistory.h index 1186a1e0e..13aaca98c 100644 --- a/src/UndoHistory.h +++ b/src/UndoHistory.h @@ -53,6 +53,7 @@ struct UndoActions { [[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; }; class ScrapStack { @@ -112,7 +113,9 @@ public: bool AfterDetachPoint() const noexcept; bool AfterOrAtDetachPoint() const noexcept; - void SetCurrent(int action) noexcept; + [[nodiscard]] intptr_t Delta(int action) noexcept; + [[nodiscard]] bool Validate(intptr_t lengthDocument) 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; |