diff options
-rw-r--r-- | src/ChangeHistory.cxx | 42 | ||||
-rw-r--r-- | src/ChangeHistory.h | 8 | ||||
-rw-r--r-- | test/unit/testCellBuffer.cxx | 49 |
3 files changed, 83 insertions, 16 deletions
diff --git a/src/ChangeHistory.cxx b/src/ChangeHistory.cxx index 7295f89af..704f3f1c9 100644 --- a/src/ChangeHistory.cxx +++ b/src/ChangeHistory.cxx @@ -30,7 +30,7 @@ namespace Scintilla::Internal { void ChangeStack::Clear() noexcept { steps.clear(); - insertions.clear(); + changes.clear(); } void ChangeStack::AddStep() { @@ -39,12 +39,12 @@ void ChangeStack::AddStep() { void ChangeStack::PushDeletion(Sci::Position positionDeletion, int edition) { steps.back()++; - insertions.push_back({ positionDeletion, 0, edition, InsertionSpan::Direction::deletion }); + changes.push_back({ positionDeletion, 0, edition, ChangeSpan::Direction::deletion }); } void ChangeStack::PushInsertion(Sci::Position positionInsertion, Sci::Position length, int edition) { steps.back()++; - insertions.push_back({ positionInsertion, length, edition, InsertionSpan::Direction::insertion }); + changes.push_back({ positionInsertion, length, edition, ChangeSpan::Direction::insertion }); } size_t ChangeStack::PopStep() noexcept { @@ -53,21 +53,33 @@ size_t ChangeStack::PopStep() noexcept { return spans; } -InsertionSpan ChangeStack::PopSpan() noexcept { - const InsertionSpan span = insertions.back(); - insertions.pop_back(); +ChangeSpan ChangeStack::PopSpan() noexcept { + const ChangeSpan span = changes.back(); + changes.pop_back(); return span; } void ChangeStack::SetSavePoint() noexcept { // Switch changeUnsaved to changeSaved - for (InsertionSpan &x : insertions) { + for (ChangeSpan &x : changes) { if (x.edition == changeModified) { x.edition = changeSaved; } } } +void ChangeStack::Check() const noexcept { +#ifdef _DEBUG + // Ensure count in steps same as insertions; + size_t sizeSteps = 0; + for (const size_t c : steps) { + sizeSteps += c; + } + const size_t sizeInsertions = changes.size(); + assert(sizeSteps == sizeInsertions); +#endif +} + void ChangeLog::Clear(Sci::Position length) { changeStack.Clear(); insertEdition.DeleteAll(); @@ -167,13 +179,16 @@ void ChangeLog::PopDeletion(Sci::Position position, Sci::Position deleteLength) editions->pop_back(); const size_t inserts = changeStack.PopStep(); for (size_t i = 0; i < inserts; i++) { - const InsertionSpan span = changeStack.PopSpan(); - if (span.direction == InsertionSpan::Direction::insertion) { + const ChangeSpan span = changeStack.PopSpan(); + if (span.direction == ChangeSpan::Direction::insertion) { insertEdition.FillRange(span.start, span.edition, span.length); } else { assert(editions); assert(editions->back() == span.edition); editions->pop_back(); + // Iterating backwards (pop) through changeStack, reverse order of insertion + // and original deletion list. + // Therefore need to insert at front to recreate original order. InsertFrontDeletionAt(span.start, span.edition); } } @@ -242,6 +257,7 @@ size_t ChangeLog::DeletionCount(Sci::Position start, Sci::Position length) const void ChangeLog::Check() const noexcept { assert(insertEdition.Length() == deleteEdition.Length()); + changeStack.Check(); } ChangeHistory::ChangeHistory(Sci::Position length) { @@ -270,7 +286,7 @@ void ChangeHistory::DeleteRange(Sci::Position position, Sci::Position deleteLeng if (changeLogReversions) { changeLogReversions->DeleteRangeSavingHistory(position, deleteLength); if (reverting) { - changeLogReversions->PushDeletionAt(position, 1); + changeLogReversions->PushDeletionAt(position, changeRevertedOriginal); } } Check(); @@ -349,9 +365,9 @@ int ChangeHistory::EditionAt(Sci::Position pos) const noexcept { if (changeLogReversions) { const int editionReversion = changeLogReversions->insertEdition.ValueAt(pos); if (editionReversion) { - if (edition < 0) - return 1; - return edition ? 4 : 1; + if (edition < 0) // Historical revision + return changeRevertedOriginal; + return edition ? changeRevertedToChange : changeRevertedOriginal; } } return edition; diff --git a/src/ChangeHistory.h b/src/ChangeHistory.h index 8a6e745dc..0475a3a6e 100644 --- a/src/ChangeHistory.h +++ b/src/ChangeHistory.h @@ -22,27 +22,29 @@ constexpr unsigned int bitSaved = 2; constexpr unsigned int bitModified = 4; constexpr unsigned int bitRevertedToModified = 8; -struct InsertionSpan { +struct ChangeSpan { Sci::Position start; Sci::Position length; int edition; enum class Direction { insertion, deletion } direction; }; +// EditionSet is ordered from oldest to newest, its not really a set using EditionSet = std::vector<int>; using EditionSetOwned = std::unique_ptr<EditionSet>; class ChangeStack { std::vector<size_t> steps; - std::vector<InsertionSpan> insertions; + std::vector<ChangeSpan> changes; public: void Clear() noexcept; void AddStep(); void PushDeletion(Sci::Position positionDeletion, int edition); void PushInsertion(Sci::Position positionInsertion, Sci::Position length, int edition); [[nodiscard]] size_t PopStep() noexcept; - [[nodiscard]] InsertionSpan PopSpan() noexcept; + [[nodiscard]] ChangeSpan PopSpan() noexcept; void SetSavePoint() noexcept; + void Check() const noexcept; }; struct ChangeLog { diff --git a/test/unit/testCellBuffer.cxx b/test/unit/testCellBuffer.cxx index cb69e7756..f7795c9e7 100644 --- a/test/unit/testCellBuffer.cxx +++ b/test/unit/testCellBuffer.cxx @@ -633,6 +633,55 @@ TEST_CASE("ChangeHistory") { } REQUIRE(il.DeletionCount(0, 10) == 0); REQUIRE(il.Length() == 10); + + } + + SECTION("Delete Contiguous Backward") { + // Deletes that touch + constexpr size_t length = 20; + constexpr size_t rounds = 8; + il.Insert(0, length, false, true); + REQUIRE(il.Length() == length); + il.SetSavePoint(); + for (size_t i = 0; i < rounds; i++) { + il.DeleteRangeSavingHistory(9-i, 1, false, false); + } + + constexpr size_t lengthAfterDeletions = length - rounds; + REQUIRE(il.Length() == lengthAfterDeletions); + REQUIRE(il.DeletionCount(0, lengthAfterDeletions) == rounds); + + for (size_t j = 0; j < rounds; j++) { + il.UndoDeleteStep(2+j, 1, false); + } + + // Restored to original + REQUIRE(il.DeletionCount(0, length) == 0); + REQUIRE(il.Length() == length); + } + + SECTION("Delete Contiguous Forward") { + // Deletes that touch + constexpr size_t length = 20; + constexpr size_t rounds = 8; + il.Insert(0, length, false, true); + REQUIRE(il.Length() == length); + il.SetSavePoint(); + for (size_t i = 0; i < rounds; i++) { + il.DeleteRangeSavingHistory(2,1, false, false); + } + + constexpr size_t lengthAfterDeletions = length - rounds; + REQUIRE(il.Length() == lengthAfterDeletions); + REQUIRE(il.DeletionCount(0, lengthAfterDeletions) == rounds); + + for (size_t j = 0; j < rounds; j++) { + il.UndoDeleteStep(2, 1, false); + } + + // Restored to original + REQUIRE(il.Length() == length); + REQUIRE(il.DeletionCount(0, length) == 0); } } |