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'); }