From 29d0ee82449f4d2f9682b29ce4b47592370fc015 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 16:39:20 +0000 Subject: [PATCH] Remove Buffer::Rows() and the Line wrapper The row cache rebuilt a string for every line of the file after each edit and had one production caller left (filetype detection in the ImGui frontend, which needs only the first line). Removed with it: rows_, nrows_, the dirty flag set on every edit, ensure_rows_cache and a mutex guarding only that cache. Tests read lines with GetLineString, Nrows or, where they compare every line, a test-only LinesForTests(). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB --- Buffer.cc | 55 +--- Buffer.h | 185 +----------- ImGuiFrontend.cc | 5 +- tests/TestHarness.h | 2 +- tests/test_benchmarks.cc | 45 +-- tests/test_buffer_rows.cc | 2 +- tests/test_daily_workflows.cc | 2 +- tests/test_migration_coverage.cc | 22 +- tests/test_reflow_indented_bullets.cc | 4 +- tests/test_reflow_paragraph.cc | 4 +- tests/test_reflow_undo.cc | 4 +- tests/test_search_replace_flow.cc | 2 +- tests/test_swap_recovery_prompt.cc | 2 +- tests/test_swap_replay.cc | 2 +- tests/test_undo.cc | 420 +++++++++++++------------- tests/test_visual_line_mode.cc | 2 +- 16 files changed, 262 insertions(+), 496 deletions(-) diff --git a/Buffer.cc b/Buffer.cc index 1b4a473..60afa03 100644 --- a/Buffer.cc +++ b/Buffer.cc @@ -354,12 +354,9 @@ Buffer::Buffer(const Buffer &other) { curx_ = other.curx_; cury_ = other.cury_; - nrows_ = other.nrows_; rowoffs_ = other.rowoffs_; coloffs_ = other.coloffs_; - rows_ = other.rows_; content_ = other.content_; - rows_cache_dirty_ = other.rows_cache_dirty_; filename_ = other.filename_; is_file_backed_ = other.is_file_backed_; is_virtual_ = other.is_virtual_; @@ -407,12 +404,9 @@ Buffer::operator=(const Buffer &other) return *this; curx_ = other.curx_; cury_ = other.cury_; - nrows_ = other.nrows_; rowoffs_ = other.rowoffs_; coloffs_ = other.coloffs_; - rows_ = other.rows_; content_ = other.content_; - rows_cache_dirty_ = other.rows_cache_dirty_; filename_ = other.filename_; is_file_backed_ = other.is_file_backed_; is_virtual_ = other.is_virtual_; @@ -455,10 +449,8 @@ Buffer::operator=(const Buffer &other) Buffer::Buffer(Buffer &&other) noexcept : curx_(other.curx_), cury_(other.cury_), - nrows_(other.nrows_), rowoffs_(other.rowoffs_), coloffs_(other.coloffs_), - rows_(std::move(other.rows_)), filename_(std::move(other.filename_)), is_file_backed_(other.is_file_backed_), is_virtual_(other.is_virtual_), @@ -483,7 +475,6 @@ Buffer::Buffer(Buffer &&other) noexcept filetype_ = std::move(other.filetype_); highlighter_ = std::move(other.highlighter_); content_ = std::move(other.content_); - rows_cache_dirty_ = other.rows_cache_dirty_; on_disk_identity_ = other.on_disk_identity_; // Non-owning: the recorder object itself is owned by SwapManager and outlives // this move. The caller (Editor) is responsible for calling SwapManager::Rehome() @@ -506,10 +497,8 @@ Buffer::operator=(Buffer &&other) noexcept curx_ = other.curx_; cury_ = other.cury_; - nrows_ = other.nrows_; rowoffs_ = other.rowoffs_; coloffs_ = other.coloffs_; - rows_ = std::move(other.rows_); filename_ = std::move(other.filename_); is_file_backed_ = other.is_file_backed_; is_virtual_ = other.is_virtual_; @@ -534,7 +523,6 @@ Buffer::operator=(Buffer &&other) noexcept filetype_ = std::move(other.filetype_); highlighter_ = std::move(other.highlighter_); content_ = std::move(other.content_); - rows_cache_dirty_ = other.rows_cache_dirty_; on_disk_identity_ = other.on_disk_identity_; // Non-owning: the recorder object itself is owned by SwapManager and outlives // this move. The caller (Editor) is responsible for calling SwapManager::Rehome() @@ -588,8 +576,6 @@ Buffer::OpenFromFile(const std::string &path, std::string &err) return false; } if (!exists) { - rows_.clear(); - nrows_ = 0; filename_ = norm; is_file_backed_ = false; is_virtual_ = false; @@ -603,7 +589,6 @@ Buffer::OpenFromFile(const std::string &path, std::string &err) // Empty PieceTable content_.Clear(); - rows_cache_dirty_ = true; // History recorded against the previous content no longer applies. if (undo_sys_) undo_sys_->clear(); @@ -662,9 +647,7 @@ Buffer::OpenFromFile(const std::string &path, std::string &err) // copy runs out of memory, the buffer keeps its old text (clearing first // left an empty buffer that a later save wrote over the file). // The bytes become the piece table's original storage, uncopied. - content_.AdoptOriginal(std::move(data)); - rows_cache_dirty_ = true; - nrows_ = 0; // not used under PieceTable + content_.AdoptOriginal(std::move(data)); // not used under PieceTable filename_ = norm; is_file_backed_ = true; is_virtual_ = false; @@ -778,7 +761,6 @@ Buffer::insert_text(int row, int col, std::string_view text) static_cast(col)); if (!text.empty()) { content_.Insert(off, text.data(), text.size()); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) swap_rec_->OnInsert(row, col, text); @@ -807,24 +789,6 @@ Buffer::GetLineView(std::size_t row) const } -void -Buffer::ensure_rows_cache() const -{ - std::lock_guard lock(buffer_mutex_); - if (!rows_cache_dirty_) - return; - rows_.clear(); - const std::size_t lc = content_.LineCount(); - rows_.reserve(lc); - for (std::size_t i = 0; i < lc; ++i) { - rows_.emplace_back(content_.GetLine(i)); - } - // Keep nrows_ in sync for any legacy code that still reads it - const_cast(this)->nrows_ = rows_.size(); - rows_cache_dirty_ = false; -} - - std::size_t Buffer::content_LineCount_() const { @@ -838,6 +802,16 @@ Buffer::BytesForTests() const { return Bytes(); } + + +std::vector +Buffer::LinesForTests() const +{ + std::vector out; + for (std::size_t i = 0; i < Nrows(); ++i) + out.push_back(GetLineString(i)); + return out; +} #endif @@ -860,7 +834,6 @@ Buffer::delete_text(int row, int col, std::size_t len) if (actual == 0) return; content_.Delete(start, actual); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) swap_rec_->OnDelete(row, col, actual); @@ -879,7 +852,6 @@ Buffer::split_line(int row, const int col) static_cast(c)); const char nl = '\n'; content_.Insert(off, &nl, 1); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) swap_rec_->OnInsert(row, c, std::string_view("\n", 1)); @@ -899,7 +871,6 @@ Buffer::join_lines(int row) std::size_t end_of_line = content_.LineColToByteOffset(r, std::numeric_limits::max()); // end_of_line now equals line end (clamped before newline). The newline should be exactly at this position. content_.Delete(end_of_line, 1); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) swap_rec_->OnDelete(row, col, 1); @@ -916,7 +887,6 @@ Buffer::insert_row(int row, const std::string_view text) content_.Insert(off, text.data(), text.size()); const char nl = '\n'; content_.Insert(off + text.size(), &nl, 1); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) { // One record: the first of two could trigger a journal checkpoint, @@ -948,7 +918,6 @@ Buffer::delete_row(int row) if (actual == 0) return; content_.Delete(start, actual); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) swap_rec_->OnDelete(row, 0, actual); @@ -961,7 +930,6 @@ Buffer::replace_all_bytes(const std::string_view bytes) content_.Clear(); if (!bytes.empty()) content_.Append(bytes.data(), bytes.size()); - rows_cache_dirty_ = true; if (undo_sys_) undo_sys_->clear(); MarkContentChanged(); @@ -978,7 +946,6 @@ Buffer::insert_spans(int row, int col, const std::vector &spans) const std::size_t off = content_.LineColToByteOffset(static_cast(row), static_cast(col)); content_.InsertSpans(off, spans); - rows_cache_dirty_ = true; edited_at_(row); if (swap_rec_) { // The journal needs the bytes as one record (see insert_row). diff --git a/Buffer.h b/Buffer.h index be24ee8..6da64e8 100644 --- a/Buffer.h +++ b/Buffer.h @@ -22,13 +22,8 @@ * - Buffers can be file-backed (associated with a path) or scratch (unnamed) * - File identity tracking detects external modifications * - * 3. Legacy Line wrapper: - * - Buffer::Line provides a string-like interface for legacy command code - * - New code should prefer direct PieceTable operations - * - See DEVELOPER_GUIDE.md for migration guidance - * - * 4. Content access: - * - Rows(): Materialized line cache (legacy, being phased out) + * 3. Content access: + * - Nrows(), GetLineString(): per-line access * - GetLineView(): line access via string_view (zero-copy within a piece) * - Direct PieceTable access for new editing operations */ @@ -136,169 +131,7 @@ public: } - // Line wrapper used by legacy command paths. - // Keep this lightweight: store materialized bytes only for that line. - class Line { - public: - Line() = default; - - - explicit Line(const char *s) - { - assign_from(s ? std::string(s) : std::string()); - } - - - explicit Line(const std::string &s) - { - assign_from(s); - } - - - Line(const Line &other) = default; - - Line &operator=(const Line &other) = default; - - Line(Line &&other) noexcept = default; - - Line &operator=(Line &&other) noexcept = default; - - // capacity helpers - void Clear() - { - s_.clear(); - } - - - // size/access - [[nodiscard]] std::size_t size() const - { - return s_.size(); - } - - - [[nodiscard]] bool empty() const - { - return s_.empty(); - } - - - // read-only raw view - [[nodiscard]] const char *Data() const - { - return s_.data(); - } - - - [[nodiscard]] std::size_t Size() const - { - return s_.size(); - } - - - // element access (read-only) - [[nodiscard]] char operator[](std::size_t i) const - { - return (i < s_.size()) ? s_[i] : '\0'; - } - - - // conversions - explicit operator std::string() const - { - return s_; - } - - - // string-like API used by command/renderer layers (implemented via materialization for now) - [[nodiscard]] std::string substr(std::size_t pos) const - { - return pos < s_.size() ? s_.substr(pos) : std::string(); - } - - - [[nodiscard]] std::string substr(std::size_t pos, std::size_t len) const - { - return pos < s_.size() ? s_.substr(pos, len) : std::string(); - } - - - // minimal find() to support search within a line - [[nodiscard]] std::size_t find(const std::string &needle, const std::size_t pos = 0) const - { - return s_.find(needle, pos); - } - - - void erase(std::size_t pos) - { - if (pos < s_.size()) - s_.erase(pos); - } - - - void erase(std::size_t pos, std::size_t len) - { - if (pos < s_.size()) - s_.erase(pos, len); - } - - - void insert(std::size_t pos, const std::string &seg) - { - if (pos > s_.size()) - pos = s_.size(); - s_.insert(pos, seg); - } - - - Line &operator+=(const Line &other) - { - s_ += other.s_; - return *this; - } - - - Line &operator+=(const std::string &s) - { - s_ += s; - return *this; - } - - - Line &operator=(const std::string &s) - { - assign_from(s); - return *this; - } - - private: - void assign_from(const std::string &s) - { - s_ = s; - } - - - std::string s_; - }; - - - [[nodiscard]] const std::vector &Rows() const - { - ensure_rows_cache(); - return rows_; - } - - - [[nodiscard]] std::vector &Rows() - { - ensure_rows_cache(); - return rows_; - } - - - // Lightweight, lazy per-line accessors that avoid materializing all rows. - // Prefer these over Rows() in hot paths to reduce memory overhead on large files. + // A line's text without its trailing newline. [[nodiscard]] std::string GetLineString(std::size_t row) const { return content_.GetLine(row); @@ -722,6 +555,10 @@ public: #if defined(KTE_TESTS) // Test-only: return the raw buffer bytes (including newlines) as a string. + + // Every line without its newline (tests only). + [[nodiscard]] std::vector LinesForTests() const; + [[nodiscard]] std::string BytesForTests() const; #endif @@ -742,15 +579,9 @@ private: // State mirroring original C struct (without undo_tree) std::size_t curx_ = 0, cury_ = 0; // cursor position in characters - std::size_t nrows_ = 0; // number of rows std::size_t rowoffs_ = 0, coloffs_ = 0; // viewport offsets - mutable std::vector rows_; // materialized cache of rows (without trailing newlines) // PieceTable is the source of truth. PieceTable content_{}; - mutable bool rows_cache_dirty_ = true; // invalidate on edits / I/O - - // Helper to rebuild rows_ from content_ - void ensure_rows_cache() const; // Helper to query content_.LineCount() while keeping header minimal std::size_t content_LineCount_() const; @@ -798,6 +629,4 @@ private: std::unique_ptr highlighter_; // Non-owning pointer to swap recorder managed by Editor/SwapManager kte::SwapRecorder *swap_rec_ = nullptr; - - mutable std::mutex buffer_mutex_; }; \ No newline at end of file diff --git a/ImGuiFrontend.cc b/ImGuiFrontend.cc index 65df2c0..4076c8e 100644 --- a/ImGuiFrontend.cc +++ b/ImGuiFrontend.cc @@ -56,10 +56,7 @@ apply_syntax_to_buffer(Buffer *b, const GUIConfig &cfg) b->EnsureHighlighter(); if (auto *eng = b->Highlighter()) { if (!eng->HasHighlighter()) { - std::string first_line; - const auto &rows = b->Rows(); - if (!rows.empty()) - first_line = static_cast(rows[0]); + const std::string first_line = b->Nrows() > 0 ? b->GetLineString(0) : std::string(); std::string ft = kte::HighlighterRegistry::DetectForPath( b->Filename(), first_line); if (!ft.empty()) { diff --git a/tests/TestHarness.h b/tests/TestHarness.h index a181b61..b44af2e 100644 --- a/tests/TestHarness.h +++ b/tests/TestHarness.h @@ -94,7 +94,7 @@ public: [[nodiscard]] std::string Text() const { - const auto &rows = Buf().Rows(); + const auto &rows = Buf().LinesForTests(); std::string out; for (std::size_t i = 0; i < rows.size(); ++i) { out += static_cast(rows[i]); diff --git a/tests/test_benchmarks.cc b/tests/test_benchmarks.cc index 8134459..1634936 100644 --- a/tests/test_benchmarks.cc +++ b/tests/test_benchmarks.cc @@ -7,7 +7,7 @@ * Benchmarks cover: * - PieceTable operations (insert, delete, GetLine, GetLineRange) * - Buffer operations (Nrows, GetLineString, GetLineView) - * - Iteration patterns (comparing old Rows() vs new GetLineString/GetLineView) + * - Iteration patterns (GetLineString vs GetLineView) * - Syntax highlighting on large files * * Each benchmark reports execution time in milliseconds. @@ -240,26 +240,6 @@ TEST (Benchmark_Buffer_GetLineView_Sequential) } -TEST (Benchmark_Buffer_Rows_Materialization) -{ - std::cout << "\n=== Buffer Rows() Materialization Benchmark ===\n"; - Buffer buf; - std::string data = generate_large_file(10000, 80); - buf.insert_text(0, 0, data); - - std::size_t total_chars = 0; - { - BenchmarkTimer timer("Rows() materialization + iteration on 10K lines"); - const auto &rows = buf.Rows(); - for (std::size_t i = 0; i < rows.size(); ++i) { - total_chars += rows[i].size(); - } - } - - EXPECT_TRUE(total_chars > 0); -} - - TEST (Benchmark_Buffer_Iteration_Comparison) { std::cout << "\n=== Buffer Iteration Pattern Comparison ===\n"; @@ -267,16 +247,8 @@ TEST (Benchmark_Buffer_Iteration_Comparison) std::string data = generate_large_file(5000, 80); buf.insert_text(0, 0, data); - std::size_t sum1 = 0, sum2 = 0, sum3 = 0; - - // Pattern 1: Old style with Rows() - { - BenchmarkTimer timer("Pattern 1: Rows() + iteration"); - const auto &rows = buf.Rows(); - for (std::size_t i = 0; i < rows.size(); ++i) { - sum1 += rows[i].size(); - } - } + std::size_t sum2 = 0, sum3 = 0; + std::size_t newlines = 0; // Pattern 2: New style with GetLineString { @@ -290,14 +262,15 @@ TEST (Benchmark_Buffer_Iteration_Comparison) { BenchmarkTimer timer("Pattern 3: Nrows() + GetLineView (zero-copy)"); for (std::size_t i = 0; i < buf.Nrows(); ++i) { - sum3 += buf.GetLineView(i).size(); + const auto v = buf.GetLineView(i); + sum3 += v.size(); + newlines += !v.empty() && v.back() == '\n'; } } - // sum1 and sum2 should match (both strip newlines) - ASSERT_EQ(sum1, sum2); - // sum3 includes newlines, so it will be larger - EXPECT_TRUE(sum3 > sum2); + // GetLineView includes the newlines GetLineString strips. + ASSERT_EQ(sum3, sum2 + newlines); + EXPECT_TRUE(newlines > 0); } diff --git a/tests/test_buffer_rows.cc b/tests/test_buffer_rows.cc index 7d4aa44..f0683f7 100644 --- a/tests/test_buffer_rows.cc +++ b/tests/test_buffer_rows.cc @@ -56,7 +56,7 @@ static void check_buffer_matches_model(const Buffer &b, const std::string &model) { auto expected_lines = split_lines_preserve_trailing_empty(model); - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); ASSERT_EQ(rows.size(), expected_lines.size()); ASSERT_EQ(b.Nrows(), rows.size()); diff --git a/tests/test_daily_workflows.cc b/tests/test_daily_workflows.cc index 2af2ebe..bb74267 100644 --- a/tests/test_daily_workflows.cc +++ b/tests/test_daily_workflows.cc @@ -51,7 +51,7 @@ read_file_bytes(const std::string &path) static std::string buffer_bytes_via_views(const Buffer &b) { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); std::string out; for (std::size_t i = 0; i < rows.size(); i++) { auto v = b.GetLineView(i); diff --git a/tests/test_migration_coverage.cc b/tests/test_migration_coverage.cc index 5330d9d..d41ab14 100644 --- a/tests/test_migration_coverage.cc +++ b/tests/test_migration_coverage.cc @@ -2,13 +2,13 @@ * test_migration_coverage.cc - Edge case tests for Buffer::Line migration * * This file provides comprehensive test coverage for the migration from - * Buffer::Rows() to direct PieceTable operations using Nrows(), GetLineString(), + * the removed Buffer::Rows() to direct PieceTable operations using Nrows(), GetLineString(), * and GetLineView(). * * Tests cover: * - Edge cases: empty buffers, single lines, very long lines * - Boundary conditions: first line, last line, out-of-bounds - * - Consistency: GetLineString vs GetLineView vs Rows() + * - Consistency: GetLineString vs GetLineView vs LinesForTests() * - Performance: large files, many small operations * - Correctness: special characters, newlines, unicode */ @@ -117,7 +117,7 @@ TEST (Migration_ManyEmptyLines) // ============================================================================ -// Consistency Tests: GetLineString vs GetLineView vs Rows() +// Consistency Tests: GetLineString vs GetLineView vs LinesForTests() // ============================================================================ TEST (Migration_Consistency_AllMethods) @@ -125,13 +125,13 @@ TEST (Migration_Consistency_AllMethods) Buffer buf; buf.insert_text(0, 0, std::string("abc\n123\nxyz")); - const auto &rows = buf.Rows(); + const auto &rows = buf.LinesForTests(); ASSERT_EQ(buf.Nrows(), rows.size()); for (std::size_t i = 0; i < buf.Nrows(); ++i) { std::string via_string = buf.GetLineString(i); std::string via_rows = std::string(rows[i]); - // GetLineString and Rows() both strip newlines + // GetLineString and LinesForTests() both strip newlines ASSERT_EQ(via_string, via_rows); // GetLineView includes the raw range (with newlines if present) // Just verify it's accessible @@ -148,18 +148,18 @@ TEST (Migration_Consistency_AfterEdits) // Edit: insert in middle buf.insert_text(1, 2, std::string("XX")); - const auto &rows = buf.Rows(); + const auto &rows = buf.LinesForTests(); ASSERT_EQ(buf.Nrows(), rows.size()); for (std::size_t i = 0; i < buf.Nrows(); ++i) { - // GetLineString and Rows() both strip newlines + // GetLineString and LinesForTests() both strip newlines ASSERT_EQ(buf.GetLineString(i), std::string(rows[i])); } // Edit: delete line buf.delete_row(1); - const auto &rows2 = buf.Rows(); + const auto &rows2 = buf.LinesForTests(); ASSERT_EQ(buf.Nrows(), rows2.size()); for (std::size_t i = 0; i < buf.Nrows(); ++i) { @@ -332,7 +332,7 @@ TEST (Migration_Stress_ManySmallInserts) ASSERT_EQ(buf.GetLineString(1).size(), (std::size_t) 100); // Verify consistency - const auto &rows = buf.Rows(); + const auto &rows = buf.LinesForTests(); ASSERT_EQ(buf.GetLineString(1), std::string(rows[1])); } @@ -367,11 +367,11 @@ TEST (Migration_Stress_AlternatingInsertDelete) } // Verify consistency after many operations - const auto &rows = buf.Rows(); + const auto &rows = buf.LinesForTests(); ASSERT_EQ(buf.Nrows(), rows.size()); for (std::size_t i = 0; i < buf.Nrows(); ++i) { - // GetLineString and Rows() both strip newlines + // GetLineString and LinesForTests() both strip newlines ASSERT_EQ(buf.GetLineString(i), std::string(rows[i])); } } diff --git a/tests/test_reflow_indented_bullets.cc b/tests/test_reflow_indented_bullets.cc index 2d08180..120f8cd 100644 --- a/tests/test_reflow_indented_bullets.cc +++ b/tests/test_reflow_indented_bullets.cc @@ -12,7 +12,7 @@ static std::string to_string_rows(const Buffer &buf) { std::string out; - for (const auto &r: buf.Rows()) { + for (const auto &r: buf.LinesForTests()) { out += static_cast(r); out.push_back('\n'); } @@ -45,7 +45,7 @@ TEST(ReflowParagraph_IndentedBullets_PreserveStructure) const int width = 80; ASSERT_TRUE(Execute(ed, std::string("reflow-paragraph"), std::string(), width)); - const auto &rows = buf->Rows(); + const auto &rows = buf->LinesForTests(); const std::string result = to_string_rows(*buf); // We should have 3 lines (plus possibly a trailing empty line) diff --git a/tests/test_reflow_paragraph.cc b/tests/test_reflow_paragraph.cc index 2c47276..ecc9813 100644 --- a/tests/test_reflow_paragraph.cc +++ b/tests/test_reflow_paragraph.cc @@ -12,7 +12,7 @@ static std::string to_string_rows(const Buffer &buf) { std::string out; - for (const auto &r: buf.Rows()) { + for (const auto &r: buf.LinesForTests()) { out += static_cast(r); out.push_back('\n'); } @@ -46,7 +46,7 @@ TEST(ReflowParagraph_NumberedList_HangingIndent) const int width = 25; ASSERT_TRUE(Execute(ed, std::string("reflow-paragraph"), std::string(), width)); - const auto &rows = buf->Rows(); + const auto &rows = buf->LinesForTests(); ASSERT_TRUE(!rows.empty()); const std::string dump = to_string_rows(*buf); diff --git a/tests/test_reflow_undo.cc b/tests/test_reflow_undo.cc index 8bcfff4..f9a8f47 100644 --- a/tests/test_reflow_undo.cc +++ b/tests/test_reflow_undo.cc @@ -12,7 +12,7 @@ static std::string to_string_rows(const Buffer &buf) { std::string out; - for (const auto &r: buf.Rows()) { + for (const auto &r: buf.LinesForTests()) { out += static_cast(r); out.push_back('\n'); } @@ -50,7 +50,7 @@ TEST (ReflowUndo) const std::string reflowed_dump = to_string_rows(*buf); ASSERT_TRUE(reflowed_dump != original_dump); - ASSERT_TRUE(buf->Rows().size() > 1); + ASSERT_TRUE(buf->Nrows() > 1); // Undo reflow ASSERT_TRUE(Execute(ed, "undo", "", 1)); diff --git a/tests/test_search_replace_flow.cc b/tests/test_search_replace_flow.cc index 9adb9c2..bd00512 100644 --- a/tests/test_search_replace_flow.cc +++ b/tests/test_search_replace_flow.cc @@ -127,7 +127,7 @@ TEST(SearchFlow_SearchReplace_EmptyWith_ReplacesAdjacentOverlappingMatches) ASSERT_TRUE(h.Exec(CommandId::Newline)); ASSERT_TRUE(!ed.PromptActive()); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); } diff --git a/tests/test_swap_recovery_prompt.cc b/tests/test_swap_recovery_prompt.cc index 42e4a40..937107f 100644 --- a/tests/test_swap_recovery_prompt.cc +++ b/tests/test_swap_recovery_prompt.cc @@ -37,7 +37,7 @@ read_file_bytes(const std::string &path) static std::string buffer_bytes_via_views(const Buffer &b) { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); std::string out; for (std::size_t i = 0; i < rows.size(); i++) { auto v = b.GetLineView(i); diff --git a/tests/test_swap_replay.cc b/tests/test_swap_replay.cc index 207bcde..185b30f 100644 --- a/tests/test_swap_replay.cc +++ b/tests/test_swap_replay.cc @@ -32,7 +32,7 @@ read_file_bytes(const std::string &path) static std::string buffer_bytes_via_views(const Buffer &b) { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); std::string out; for (std::size_t i = 0; i < rows.size(); i++) { auto v = b.GetLineView(i); diff --git a/tests/test_undo.cc b/tests/test_undo.cc index e7a1494..c0b03af 100644 --- a/tests/test_undo.cc +++ b/tests/test_undo.cc @@ -75,9 +75,9 @@ TEST (Undo_InsertRun_Coalesces_OneStep) b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("hi")); + ASSERT_EQ(b.GetLineString(0), std::string("hi")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); } @@ -101,11 +101,11 @@ TEST (Undo_InsertRun_BreaksOnNonAdjacentCursor) b.SetCursor(1, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ba")); + ASSERT_EQ(b.GetLineString(0), std::string("ba")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); } @@ -120,7 +120,7 @@ TEST (Undo_BackspaceRun_Coalesces_OneStep) // Delete 'c' then 'b' with backspace shape. { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[0][2]; b.delete_text(0, 2, 1); b.SetCursor(2, 0); @@ -128,7 +128,7 @@ TEST (Undo_BackspaceRun_Coalesces_OneStep) u->Append(deleted); } { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[0][1]; b.delete_text(0, 1, 1); b.SetCursor(1, 0); @@ -136,10 +136,10 @@ TEST (Undo_BackspaceRun_Coalesces_OneStep) u->Append(deleted); } u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("abc")); + ASSERT_EQ(b.GetLineString(0), std::string("abc")); } @@ -153,7 +153,7 @@ TEST (Undo_DeleteKeyRun_Coalesces_OneStep) // Simulate delete-key at col 1 twice (cursor stays). b.SetCursor(1, 0); { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[0][1]; b.delete_text(0, 1, 1); b.SetCursor(1, 0); @@ -161,7 +161,7 @@ TEST (Undo_DeleteKeyRun_Coalesces_OneStep) u->Append(deleted); } { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[0][1]; b.delete_text(0, 1, 1); b.SetCursor(1, 0); @@ -169,10 +169,10 @@ TEST (Undo_DeleteKeyRun_Coalesces_OneStep) u->Append(deleted); } u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ad")); + ASSERT_EQ(b.GetLineString(0), std::string("ad")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("abcd")); + ASSERT_EQ(b.GetLineString(0), std::string("abcd")); } @@ -201,8 +201,8 @@ TEST (Undo_Newline_IsStandalone) b.SetCursor(1, 1); u->commit(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("xi")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(1), std::string("xi")); u->undo(); // Undoing the insert should not also undo the newline. ASSERT_EQ(b.BytesForTests(), after_nl); @@ -233,9 +233,9 @@ TEST (Undo_ExplicitGroup_UndoesAsUnit) u->commit(); u->EndGroup(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); } @@ -254,12 +254,12 @@ TEST (Undo_Branching_RedoBranchSelectionDeterministic) b.SetCursor(b.Curx() + 1, 0); u->commit(); } - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ABC")); + ASSERT_EQ(b.GetLineString(0), std::string("ABC")); // Undo twice -> back to "A" u->undo(); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("A")); + ASSERT_EQ(b.GetLineString(0), std::string("A")); // Type D to create a new branch. u->Begin(UndoType::Insert); @@ -268,18 +268,18 @@ TEST (Undo_Branching_RedoBranchSelectionDeterministic) u->Append('D'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("AD")); + ASSERT_EQ(b.GetLineString(0), std::string("AD")); // Undo D, then redo branch 0 should redo D (new head). u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("A")); + ASSERT_EQ(b.GetLineString(0), std::string("A")); u->redo(0); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("AD")); + ASSERT_EQ(b.GetLineString(0), std::string("AD")); // Undo back to A again, redo branch 1 should follow the older path (to AB). u->undo(); u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("AB")); + ASSERT_EQ(b.GetLineString(0), std::string("AB")); } @@ -336,9 +336,9 @@ TEST (Undo_RoundTrip_Lossless_RandomEdits) } else { // Delete one char at a stable position. std::size_t x = b.Curx(); - if (x >= b.Rows()[0].size()) - x = b.Rows()[0].size() - 1; - char deleted = b.Rows()[0][x]; + if (x >= b.GetLineString(0).size()) + x = b.GetLineString(0).size() - 1; + char deleted = b.GetLineString(0)[x]; b.delete_text(0, static_cast(x), 1); b.SetCursor(x, 0); u->Begin(UndoType::Delete); @@ -390,10 +390,10 @@ TEST (Undo_Branching_RedoPreservedAfterNewEdit) b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // New edit after undo creates a new branch; the old redo should remain as an alternate branch. u->Begin(UndoType::Insert); @@ -402,17 +402,17 @@ TEST (Undo_Branching_RedoPreservedAfterNewEdit) b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); // No further redo from the tip. u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); // Undo back to the branch point and redo the original branch. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); } @@ -453,20 +453,20 @@ TEST (Undo_Newline_UndoRedo_SplitJoin) b.split_line(0, 2); u->commit(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("he")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("llo")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("he")); + ASSERT_EQ(b.GetLineString(1), std::string("llo")); // Undo should join the lines back. u->undo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("hello")); + ASSERT_EQ(b.Nrows(), (std::size_t) 1); + ASSERT_EQ(b.GetLineString(0), std::string("hello")); // Redo should split again at the same point. u->redo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("he")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("llo")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("he")); + ASSERT_EQ(b.GetLineString(1), std::string("llo")); } @@ -482,7 +482,7 @@ TEST (Undo_DeleteKeyRun_Coalesces) // Delete 'b' { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[0][1]; u->Begin(UndoType::Delete); b.delete_text(0, 1, 1); @@ -491,7 +491,7 @@ TEST (Undo_DeleteKeyRun_Coalesces) } // Delete next char (was 'c', now at same col=1) { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[0][1]; u->Begin(UndoType::Delete); b.delete_text(0, 1, 1); @@ -500,11 +500,11 @@ TEST (Undo_DeleteKeyRun_Coalesces) } u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ad")); + ASSERT_EQ(b.GetLineString(0), std::string("ad")); // One undo should restore both deleted characters. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("abcd")); + ASSERT_EQ(b.GetLineString(0), std::string("abcd")); } @@ -529,19 +529,19 @@ TEST (Undo_UndoPastFirstEdit_RedoFromPreFirstEdit) b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // Undo twice: we should reach the pre-first-edit state. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); // Redo twice should restore both edits. u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); } @@ -566,12 +566,12 @@ TEST (Undo_NewEditFromPreFirstEdit_PreservesOldHistoryAsAlternateRootBranch) b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // Undo past first edit so current becomes null. u->undo(); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); // Commit a new edit from the pre-first-edit state. b.SetCursor(0, 0); @@ -581,17 +581,17 @@ TEST (Undo_NewEditFromPreFirstEdit_PreservesOldHistoryAsAlternateRootBranch) b.SetCursor(1, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("x")); + ASSERT_EQ(b.GetLineString(0), std::string("x")); // From the tip, no further redo. u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("x")); + ASSERT_EQ(b.GetLineString(0), std::string("x")); // Undo back to pre-first-edit and select the older root branch. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); } @@ -603,9 +603,9 @@ TEST (Undo_MultiLineDelete_ConsumesNewline_UndoRestores) // Create two lines. PieceTable treats '\n' between logical lines. b.insert_text(0, 0, std::string_view("ab\ncd")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("cd")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); + ASSERT_EQ(b.GetLineString(1), std::string("cd")); // Delete spanning the newline: delete "b\n" starting at (0,1). b.SetCursor(1, 0); @@ -614,14 +614,14 @@ TEST (Undo_MultiLineDelete_ConsumesNewline_UndoRestores) u->Append(std::string_view("b\n")); u->commit(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("acd")); + ASSERT_EQ(b.Nrows(), (std::size_t) 1); + ASSERT_EQ(b.GetLineString(0), std::string("acd")); // Undo should restore exact original text/line structure. u->undo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("cd")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); + ASSERT_EQ(b.GetLineString(1), std::string("cd")); } @@ -634,12 +634,12 @@ TEST (Undo_DeleteIndent_UndoRestoresCursorAtText) // Seed 3-line content with indentation on the middle line. b.insert_text(0, 0, std::string_view("I did a thing\n and then I edited a thing\nbut there were gaps")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 3); + ASSERT_EQ(b.Nrows(), (std::size_t) 3); // Cursor at start of the line (before spaces), then C-d C-d deletes two spaces. b.SetCursor(0, 1); for (int i = 0; i < 2; ++i) { - const auto &rows = b.Rows(); + const auto &rows = b.LinesForTests(); char deleted = rows[1][0]; ASSERT_EQ(deleted, ' '); u->Begin(UndoType::Delete); @@ -649,13 +649,13 @@ TEST (Undo_DeleteIndent_UndoRestoresCursorAtText) } u->commit(); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("and then I edited a thing")); + ASSERT_EQ(b.GetLineString(1), std::string("and then I edited a thing")); ASSERT_EQ(b.Cury(), (std::size_t) 1); ASSERT_EQ(b.Curx(), (std::size_t) 0); // Undo should restore indentation, and keep cursor on the text (at 'a'), not at EOL. u->undo(); - ASSERT_EQ(std::string(b.Rows()[1]), std::string(" and then I edited a thing")); + ASSERT_EQ(b.GetLineString(1), std::string(" and then I edited a thing")); ASSERT_EQ(b.Cury(), (std::size_t) 1); ASSERT_EQ(b.Curx(), (std::size_t) 2); } @@ -681,12 +681,12 @@ TEST (Undo_StructuralInvariants_BranchingAndRoots) u->Append('b'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // Undo past first edit; now create a new root-level branch x. u->undo(); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); b.SetCursor(0, 0); u->Begin(UndoType::Insert); @@ -694,13 +694,13 @@ TEST (Undo_StructuralInvariants_BranchingAndRoots) u->Append('x'); b.SetCursor(1, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("x")); + ASSERT_EQ(b.GetLineString(0), std::string("x")); // Return to the older root branch. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Create a normal branch under 'a'. u->Begin(UndoType::Insert); @@ -708,7 +708,7 @@ TEST (Undo_StructuralInvariants_BranchingAndRoots) u->Append('c'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); validate_undo_tree(*u); } @@ -727,7 +727,7 @@ TEST (Undo_BranchSelection_ThreeSiblingsAndHeadPersists) u->Append('a'); b.SetCursor(1, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Branch 1: a->b u->Begin(UndoType::Insert); @@ -735,11 +735,11 @@ TEST (Undo_BranchSelection_ThreeSiblingsAndHeadPersists) u->Append('b'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // Back to branch point. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Branch 2: a->c u->Begin(UndoType::Insert); @@ -747,10 +747,10 @@ TEST (Undo_BranchSelection_ThreeSiblingsAndHeadPersists) u->Append('c'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Branch 3: a->d u->Begin(UndoType::Insert); @@ -758,37 +758,37 @@ TEST (Undo_BranchSelection_ThreeSiblingsAndHeadPersists) u->Append('d'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ad")); + ASSERT_EQ(b.GetLineString(0), std::string("ad")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Under 'a', the sibling list should now contain 3 branches. validate_undo_tree(*u); // Select the 3rd sibling (branch_index=2) which should be the oldest ("b"), and make it active. u->redo(2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Since we selected "b", redo with default should now follow "b" again. u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Select another branch by index and ensure it becomes the new default. u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ad")); + ASSERT_EQ(b.GetLineString(0), std::string("ad")); u->undo(); u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ad")); + ASSERT_EQ(b.GetLineString(0), std::string("ad")); u->undo(); // Out-of-range selection should be a no-op. u->redo(99); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); validate_undo_tree(*u); } @@ -812,7 +812,7 @@ TEST (Undo_Branching_SwitchBetweenTwoRedoBranches_TextAndCursor) u->Append('a'); b.SetCursor(1, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); ASSERT_EQ(b.Cury(), (std::size_t) 0); ASSERT_EQ(b.Curx(), (std::size_t) 1); @@ -821,12 +821,12 @@ TEST (Undo_Branching_SwitchBetweenTwoRedoBranches_TextAndCursor) u->Append('b'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); ASSERT_EQ(b.Curx(), (std::size_t) 2); // Undo to A. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); ASSERT_EQ(b.Curx(), (std::size_t) 1); // Create sibling branch A->C. @@ -835,37 +835,37 @@ TEST (Undo_Branching_SwitchBetweenTwoRedoBranches_TextAndCursor) u->Append('c'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); ASSERT_EQ(b.Curx(), (std::size_t) 2); // Back to A. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); ASSERT_EQ(b.Curx(), (std::size_t) 1); // Redo into B as the alternate branch (older sibling), and confirm cursor is consistent. u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); ASSERT_EQ(b.Curx(), (std::size_t) 2); // Both branches remain reachable: undo to A, redo defaults to B (head reordered). u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // And the other branch C should still be selectable. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); ASSERT_EQ(b.Curx(), (std::size_t) 2); // After selecting C, default redo from A should now follow C. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); validate_undo_tree(*u); } @@ -886,15 +886,15 @@ TEST (Undo_Randomized_Deterministic_EditUndoRedoBranchSelect) const int max_branch = 4; for (int i = 0; i < steps; ++i) { - ASSERT_TRUE(!b.Rows().empty()); + ASSERT_TRUE((b.Nrows() > 0)); ASSERT_EQ(b.Cury(), (std::size_t) 0); - ASSERT_EQ(b.Rows().size(), (std::size_t) 1); - ASSERT_TRUE(b.Curx() <= b.Rows()[0].size()); + ASSERT_EQ(b.Nrows(), (std::size_t) 1); + ASSERT_TRUE(b.Curx() <= b.GetLineString(0).size()); validate_undo_tree(*u); int r = op(rng); - std::string cur = std::string(b.Rows()[0]); + std::string cur = b.GetLineString(0); int len = static_cast(cur.size()); if (r < 40 && len < max_len) { @@ -917,14 +917,14 @@ TEST (Undo_Randomized_Deterministic_EditUndoRedoBranchSelect) } else if (r < 80) { // Undo then redo should round-trip to the exact same node/text/cursor when possible. const UndoNode *before_node = u->TreeForTests().current; - const std::string before_text(std::string(b.Rows()[0])); + const std::string before_text(b.GetLineString(0)); const std::size_t before_x = b.Curx(); if (before_node) { u->undo(); u->redo(); ASSERT_TRUE(u->TreeForTests().current == before_node); - ASSERT_EQ(std::string(b.Rows()[0]), before_text); + ASSERT_EQ(b.GetLineString(0), before_text); ASSERT_EQ(b.Curx(), before_x); } else { // Nothing to undo; just exercise redo/branch-select paths. @@ -962,14 +962,14 @@ TEST (Undo_PendingCoalescedRun_UndoCommitsThenUndoes) u->Append('b'); b.SetCursor(2, 0); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // undo() should implicitly commit pending and then undo it as one step. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("")); + ASSERT_EQ(b.GetLineString(0), std::string("")); u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); validate_undo_tree(*u); } @@ -994,32 +994,32 @@ TEST (Undo_PendingRunAtBranchPoint_UndoThenBranchSelectionStillWorks) u->Append('b'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); // Undo to the branch point. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Start a pending insert "c" at the branch point, but don't commit. u->Begin(UndoType::Insert); b.insert_text(0, 1, std::string_view("c")); u->Append('c'); b.SetCursor(2, 0); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); // Undo should seal the pending "c" as a new branch, then undo it, leaving us at "a". u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // The active redo should now be "c". u->redo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); // Select the older "b" branch. u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); validate_undo_tree(*u); } @@ -1049,21 +1049,21 @@ TEST (Undo_SavedNodeOnOtherBranch_DirtyClearsWhenReturning) // Move to a different branch. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->Begin(UndoType::Insert); b.insert_text(0, 1, std::string_view("c")); u->Append('c'); b.SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ac")); + ASSERT_EQ(b.GetLineString(0), std::string("ac")); ASSERT_TRUE(b.Dirty()); // Return to the saved node by selecting the older branch. u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("a")); + ASSERT_EQ(b.GetLineString(0), std::string("a")); u->redo(1); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("ab")); + ASSERT_EQ(b.GetLineString(0), std::string("ab")); ASSERT_TRUE(!b.Dirty()); validate_undo_tree(*u); @@ -1095,7 +1095,7 @@ TEST (Undo_Clear_AfterSaved_ResetsStateSafely) u->clear(); ASSERT_TRUE(!b.Dirty()); // clear() resets undo history, but does not mutate buffer contents. - ASSERT_EQ(std::string(b.Rows()[0]), std::string("xy")); + ASSERT_EQ(b.GetLineString(0), std::string("xy")); validate_undo_tree(*u); } @@ -1128,12 +1128,12 @@ TEST (Undo_Command_UndoHonorsRepeatCount) u->Append('b'); buf->SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("ab")); + ASSERT_EQ(buf->GetLineString(0), std::string("ab")); // Undo twice via command repeat count. ed.SetUniversalArg(1, 2); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("")); + ASSERT_EQ(buf->GetLineString(0), std::string("")); validate_undo_tree(*u); } @@ -1166,33 +1166,33 @@ TEST (Undo_Command_RedoCountSelectsBranch) u->Append('b'); buf->SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("ab")); + ASSERT_EQ(buf->GetLineString(0), std::string("ab")); // Undo to the branch point and create a sibling branch "c". u->undo(); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("a")); + ASSERT_EQ(buf->GetLineString(0), std::string("a")); u->Begin(UndoType::Insert); buf->insert_text(0, 1, std::string_view("c")); u->Append('c'); buf->SetCursor(2, 0); u->commit(); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("ac")); + ASSERT_EQ(buf->GetLineString(0), std::string("ac")); // Back to branch point. ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("a")); + ASSERT_EQ(buf->GetLineString(0), std::string("a")); // Command redo with count=2 should select branch_index=1 (the older "b" branch). ed.SetUniversalArg(1, 2); ASSERT_TRUE(Execute(ed, CommandId::Redo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("ab")); + ASSERT_EQ(buf->GetLineString(0), std::string("ab")); // After selection, "b" should be the default redo from the branch point. ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("a")); + ASSERT_EQ(buf->GetLineString(0), std::string("a")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("ab")); + ASSERT_EQ(buf->GetLineString(0), std::string("ab")); validate_undo_tree(*u); } @@ -1215,14 +1215,14 @@ TEST (Undo_Command_Newline_UndoRejoinsCorrectLines) ASSERT_TRUE(Execute(ed, CommandId::Newline)); ASSERT_EQ(buf->Nrows(), static_cast(3)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("def")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("ghijkl")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); + ASSERT_EQ(buf->GetLineString(1), std::string("def")); + ASSERT_EQ(buf->GetLineString(2), std::string("ghijkl")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); ASSERT_EQ(buf->Nrows(), static_cast(2)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abcdef")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("ghijkl")); + ASSERT_EQ(buf->GetLineString(0), std::string("abcdef")); + ASSERT_EQ(buf->GetLineString(1), std::string("ghijkl")); validate_undo_tree(*buf->Undo()); } @@ -1245,16 +1245,16 @@ TEST (Undo_Command_Backspace_JoinUndoRedo) ASSERT_TRUE(Execute(ed, CommandId::Backspace)); ASSERT_EQ(buf->Nrows(), static_cast(1)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abcdef")); + ASSERT_EQ(buf->GetLineString(0), std::string("abcdef")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); ASSERT_EQ(buf->Nrows(), static_cast(2)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("def")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); + ASSERT_EQ(buf->GetLineString(1), std::string("def")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); ASSERT_EQ(buf->Nrows(), static_cast(1)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abcdef")); + ASSERT_EQ(buf->GetLineString(0), std::string("abcdef")); validate_undo_tree(*buf->Undo()); } @@ -1277,16 +1277,16 @@ TEST (Undo_Command_DeleteChar_JoinUndoRedo) ASSERT_TRUE(Execute(ed, CommandId::DeleteChar)); ASSERT_EQ(buf->Nrows(), static_cast(1)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abcdef")); + ASSERT_EQ(buf->GetLineString(0), std::string("abcdef")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); ASSERT_EQ(buf->Nrows(), static_cast(2)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("def")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); + ASSERT_EQ(buf->GetLineString(1), std::string("def")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); ASSERT_EQ(buf->Nrows(), static_cast(1)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abcdef")); + ASSERT_EQ(buf->GetLineString(0), std::string("abcdef")); validate_undo_tree(*buf->Undo()); } @@ -1315,19 +1315,19 @@ TEST (Undo_Command_RegexReplaceAll_UndoRedo) ed.SetPromptText("baz"); ASSERT_TRUE(Execute(ed, CommandId::Newline)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("baz one")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("baz two")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("bar three")); + ASSERT_EQ(buf->GetLineString(0), std::string("baz one")); + ASSERT_EQ(buf->GetLineString(1), std::string("baz two")); + ASSERT_EQ(buf->GetLineString(2), std::string("bar three")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("foo one")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("foo two")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("bar three")); + ASSERT_EQ(buf->GetLineString(0), std::string("foo one")); + ASSERT_EQ(buf->GetLineString(1), std::string("foo two")); + ASSERT_EQ(buf->GetLineString(2), std::string("bar three")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("baz one")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("baz two")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("bar three")); + ASSERT_EQ(buf->GetLineString(0), std::string("baz one")); + ASSERT_EQ(buf->GetLineString(1), std::string("baz two")); + ASSERT_EQ(buf->GetLineString(2), std::string("bar three")); validate_undo_tree(*buf->Undo()); } @@ -1350,31 +1350,31 @@ TEST (Undo_Command_IndentUnindentRegion_UndoRedo) buf->SetCursor(0, 2); ASSERT_TRUE(Execute(ed, CommandId::IndentRegion)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("\tone")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("\ttwo")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("\tthree")); + ASSERT_EQ(buf->GetLineString(0), std::string("\tone")); + ASSERT_EQ(buf->GetLineString(1), std::string("\ttwo")); + ASSERT_EQ(buf->GetLineString(2), std::string("\tthree")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("one")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("two")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("three")); + ASSERT_EQ(buf->GetLineString(0), std::string("one")); + ASSERT_EQ(buf->GetLineString(1), std::string("two")); + ASSERT_EQ(buf->GetLineString(2), std::string("three")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("\tone")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("\ttwo")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("\tthree")); + ASSERT_EQ(buf->GetLineString(0), std::string("\tone")); + ASSERT_EQ(buf->GetLineString(1), std::string("\ttwo")); + ASSERT_EQ(buf->GetLineString(2), std::string("\tthree")); buf->SetMark(0, 0); buf->SetCursor(0, 2); ASSERT_TRUE(Execute(ed, CommandId::UnindentRegion)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("one")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("two")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("three")); + ASSERT_EQ(buf->GetLineString(0), std::string("one")); + ASSERT_EQ(buf->GetLineString(1), std::string("two")); + ASSERT_EQ(buf->GetLineString(2), std::string("three")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("\tone")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("\ttwo")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("\tthree")); + ASSERT_EQ(buf->GetLineString(0), std::string("\tone")); + ASSERT_EQ(buf->GetLineString(1), std::string("\ttwo")); + ASSERT_EQ(buf->GetLineString(2), std::string("\tthree")); validate_undo_tree(*buf->Undo()); } @@ -1396,15 +1396,15 @@ TEST (Undo_Command_KillToEol_UndoRedo) buf->SetCursor(3, 0); ASSERT_TRUE(Execute(ed, CommandId::KillToEOL)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); ASSERT_EQ(buf->Nrows(), static_cast(2)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abcdef")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("ghijkl")); + ASSERT_EQ(buf->GetLineString(0), std::string("abcdef")); + ASSERT_EQ(buf->GetLineString(1), std::string("ghijkl")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); validate_undo_tree(*buf->Undo()); } @@ -1427,19 +1427,19 @@ TEST (Undo_Command_KillLine_UndoRedo) ASSERT_TRUE(Execute(ed, CommandId::KillLine)); ASSERT_EQ(buf->Nrows(), static_cast(2)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); + ASSERT_EQ(buf->GetLineString(1), std::string("ghi")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); ASSERT_EQ(buf->Nrows(), static_cast(3)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("def")); - ASSERT_EQ(std::string(buf->Rows()[2]), std::string("ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); + ASSERT_EQ(buf->GetLineString(1), std::string("def")); + ASSERT_EQ(buf->GetLineString(2), std::string("ghi")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); ASSERT_EQ(buf->Nrows(), static_cast(2)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc")); - ASSERT_EQ(std::string(buf->Rows()[1]), std::string("ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc")); + ASSERT_EQ(buf->GetLineString(1), std::string("ghi")); validate_undo_tree(*buf->Undo()); } @@ -1462,13 +1462,13 @@ TEST (Undo_Command_KillRegion_UndoRedo) buf->SetCursor(8, 0); ASSERT_TRUE(Execute(ed, CommandId::KillRegion)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc ghi")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc def ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc def ghi")); ASSERT_TRUE(Execute(ed, CommandId::Redo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc ghi")); validate_undo_tree(*buf->Undo()); } @@ -1490,15 +1490,15 @@ TEST (Undo_Command_DeleteWordPrevNext_UndoRedo) buf->SetCursor(8, 0); ASSERT_TRUE(Execute(ed, CommandId::DeleteWordPrev)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc ghi")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc def ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc def ghi")); buf->SetCursor(4, 0); ASSERT_TRUE(Execute(ed, CommandId::DeleteWordNext)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc ghi")); ASSERT_TRUE(Execute(ed, CommandId::Undo)); - ASSERT_EQ(std::string(buf->Rows()[0]), std::string("abc def ghi")); + ASSERT_EQ(buf->GetLineString(0), std::string("abc def ghi")); validate_undo_tree(*buf->Undo()); } @@ -1512,12 +1512,12 @@ TEST (Undo_InsertRow_UndoDeletesRow) // Seed two lines so insert_row has proper newline context. b.insert_text(0, 0, std::string_view("first\nlast")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); // Insert a row at position 1 (between first and last), then record it. b.insert_row(1, std::string_view("second")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 3); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("second")); + ASSERT_EQ(b.Nrows(), (std::size_t) 3); + ASSERT_EQ(b.GetLineString(1), std::string("second")); b.SetCursor(0, 1); u->Begin(UndoType::InsertRow); @@ -1526,14 +1526,14 @@ TEST (Undo_InsertRow_UndoDeletesRow) // Undo should remove the inserted row. u->undo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("first")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("last")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("first")); + ASSERT_EQ(b.GetLineString(1), std::string("last")); // Redo should re-insert it. u->redo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 3); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("second")); + ASSERT_EQ(b.Nrows(), (std::size_t) 3); + ASSERT_EQ(b.GetLineString(1), std::string("second")); validate_undo_tree(*u); } @@ -1546,28 +1546,28 @@ TEST (Undo_DeleteRow_UndoRestoresRow) ASSERT_TRUE(u != nullptr); b.insert_text(0, 0, std::string_view("alpha\nbeta\ngamma")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 3); + ASSERT_EQ(b.Nrows(), (std::size_t) 3); // Record a DeleteRow for row 1 ("beta"). b.SetCursor(0, 1); u->Begin(UndoType::DeleteRow); - u->Append(static_cast(b.Rows()[1])); + u->Append(static_cast(b.GetLineString(1))); u->commit(); b.delete_row(1); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("alpha")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("gamma")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("alpha")); + ASSERT_EQ(b.GetLineString(1), std::string("gamma")); // Undo should restore "beta" at row 1. u->undo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 3); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("beta")); + ASSERT_EQ(b.Nrows(), (std::size_t) 3); + ASSERT_EQ(b.GetLineString(1), std::string("beta")); // Redo should delete it again. u->redo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("gamma")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(1), std::string("gamma")); validate_undo_tree(*u); } @@ -1581,7 +1581,7 @@ TEST (Undo_InsertRow_IsStandalone) // Seed with two lines so InsertRow has proper newline context. b.insert_text(0, 0, std::string_view("x\nend")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); // Start a pending insert on row 0. b.SetCursor(1, 0); @@ -1597,18 +1597,18 @@ TEST (Undo_InsertRow_IsStandalone) u->Append(std::string_view("row2")); u->commit(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("xy")); - ASSERT_EQ(std::string(b.Rows()[1]), std::string("row2")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 3); + ASSERT_EQ(b.GetLineString(0), std::string("xy")); + ASSERT_EQ(b.GetLineString(1), std::string("row2")); + ASSERT_EQ(b.Nrows(), (std::size_t) 3); // Undo InsertRow only. u->undo(); - ASSERT_EQ(b.Rows().size(), (std::size_t) 2); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("xy")); + ASSERT_EQ(b.Nrows(), (std::size_t) 2); + ASSERT_EQ(b.GetLineString(0), std::string("xy")); // Undo the insert "y". u->undo(); - ASSERT_EQ(std::string(b.Rows()[0]), std::string("x")); + ASSERT_EQ(b.GetLineString(0), std::string("x")); validate_undo_tree(*u); } @@ -1622,7 +1622,7 @@ TEST (Undo_GroupedDeleteAndInsertRows_UndoesAsUnit) // Seed three lines (with trailing newline so delete_row/insert_row work cleanly). b.insert_text(0, 0, std::string_view("aaa\nbbb\nccc\n")); - ASSERT_EQ(b.Rows().size(), (std::size_t) 4); // 3 content + 1 empty trailing + ASSERT_EQ(b.Nrows(), (std::size_t) 4); // 3 content + 1 empty trailing const std::string original = b.AsString(); // Group: delete content rows then insert replacements (simulates reflow). @@ -1632,7 +1632,7 @@ TEST (Undo_GroupedDeleteAndInsertRows_UndoesAsUnit) for (int i = 2; i >= 0; --i) { b.SetCursor(0, static_cast(i)); u->Begin(UndoType::DeleteRow); - u->Append(static_cast(b.Rows()[static_cast(i)])); + u->Append(static_cast(b.GetLineString(static_cast(i)))); u->commit(); b.delete_row(i); } diff --git a/tests/test_visual_line_mode.cc b/tests/test_visual_line_mode.cc index 3240f06..8e8bd9e 100644 --- a/tests/test_visual_line_mode.cc +++ b/tests/test_visual_line_mode.cc @@ -11,7 +11,7 @@ static std::string dump_buf(const Buffer &buf) { std::string out; - for (const auto &r: buf.Rows()) { + for (const auto &r: buf.LinesForTests()) { out += static_cast(r); out.push_back('\n'); }