From 306a3375166442ee2cc5c6bffdcb7e0f1fe358bd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 16:31:38 +0000 Subject: [PATCH] PieceTable: newline test from the line index, memchr rebuild; remove dead code GetLine and LineColToByteOffset fetched each line's last byte (a piece lookup, an allocation and a cache write) to see whether it was a newline; the line index already says so: every line but the last ends at one. LineColToByteOffset runs on every insert. RebuildLineIndex scans with memchr instead of byte by byte. Removed, having no callers: OptimizedSearch (built only into its own test), UndoNodePool.h, SwapManager::RecordSplit/RecordJoin (replay still reads SPLIT/JOIN), PieceTable::Reserve/Capacity and its capacity constructor, Buffer's write-only render column, Editor's mode, no-kill, dirtyex and repeatable flags and the UCount getter, and RetryPolicy::Conservative. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB --- Buffer.cc | 8 ++--- Buffer.h | 16 +-------- CMakeLists.txt | 2 -- Command.cc | 2 -- Editor.cc | 4 --- Editor.h | 60 --------------------------------- ErrorRecovery.h | 5 --- OptimizedSearch.cc | 79 -------------------------------------------- OptimizedSearch.h | 23 ------------- PieceTable.cc | 48 +++++++-------------------- PieceTable.h | 12 ------- Swap.cc | 41 ----------------------- Swap.h | 5 +-- UndoNodePool.h | 63 ----------------------------------- tests/test_search.cc | 46 -------------------------- 15 files changed, 16 insertions(+), 398 deletions(-) delete mode 100644 OptimizedSearch.cc delete mode 100644 OptimizedSearch.h delete mode 100644 UndoNodePool.h delete mode 100644 tests/test_search.cc diff --git a/Buffer.cc b/Buffer.cc index b68814f..ccc1265 100644 --- a/Buffer.cc +++ b/Buffer.cc @@ -347,7 +347,6 @@ Buffer::Buffer(const Buffer &other) { curx_ = other.curx_; cury_ = other.cury_; - rx_ = other.rx_; nrows_ = other.nrows_; rowoffs_ = other.rowoffs_; coloffs_ = other.coloffs_; @@ -401,7 +400,6 @@ Buffer::operator=(const Buffer &other) return *this; curx_ = other.curx_; cury_ = other.cury_; - rx_ = other.rx_; nrows_ = other.nrows_; rowoffs_ = other.rowoffs_; coloffs_ = other.coloffs_; @@ -450,7 +448,6 @@ Buffer::operator=(const Buffer &other) Buffer::Buffer(Buffer &&other) noexcept : curx_(other.curx_), cury_(other.cury_), - rx_(other.rx_), nrows_(other.nrows_), rowoffs_(other.rowoffs_), coloffs_(other.coloffs_), @@ -502,7 +499,6 @@ Buffer::operator=(Buffer &&other) noexcept curx_ = other.curx_; cury_ = other.cury_; - rx_ = other.rx_; nrows_ = other.nrows_; rowoffs_ = other.rowoffs_; coloffs_ = other.coloffs_; @@ -593,7 +589,7 @@ Buffer::OpenFromFile(const std::string &path, std::string &err) dirty_ = false; // Reset cursor/viewport state - curx_ = cury_ = rx_ = 0; + curx_ = cury_ = 0; rowoffs_ = coloffs_ = 0; mark_set_ = false; mark_curx_ = mark_cury_ = 0; @@ -687,7 +683,7 @@ Buffer::OpenFromFile(const std::string &path, std::string &err) MarkContentChanged(); // Reset cursor/viewport state - curx_ = cury_ = rx_ = 0; + curx_ = cury_ = 0; rowoffs_ = coloffs_ = 0; mark_set_ = false; mark_curx_ = mark_cury_ = 0; diff --git a/Buffer.h b/Buffer.h index 08101ad..2c36ec8 100644 --- a/Buffer.h +++ b/Buffer.h @@ -5,7 +5,7 @@ * or scratch document and manages: * * - Content storage: Uses PieceTable for efficient text operations - * - Cursor state: Current position (curx_, cury_), rendered column (rx_) + * - Cursor state: Current position (curx_, cury_) * - Viewport: Scroll offsets (rowoffs_, coloffs_) for display * - File backing: Optional association with a file on disk * - Undo/Redo: Integrated UndoSystem for operation history @@ -17,7 +17,6 @@ * * 1. Cursor coordinates: * - (curx_, cury_): Logical character position in the document - * - rx_: Rendered column accounting for tab expansion * * 2. File backing: * - Buffers can be file-backed (associated with a path) or scratch (unnamed) @@ -119,12 +118,6 @@ public: } - [[nodiscard]] std::size_t Rx() const - { - return rx_; - } - - [[nodiscard]] std::size_t Nrows() const { return content_LineCount_(); @@ -394,12 +387,6 @@ public: } - void SetRenderX(const std::size_t rx) - { - rx_ = rx; - } - - void SetOffsets(const std::size_t row, const std::size_t col) { rowoffs_ = row; @@ -740,7 +727,6 @@ private: // State mirroring original C struct (without undo_tree) std::size_t curx_ = 0, cury_ = 0; // cursor position in characters - std::size_t rx_ = 0; // render x (tabs expanded) 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) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5db8e4b..19d7da6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -389,7 +389,6 @@ if (BUILD_TESTS) tests/test_swap_cleanup2.cc tests/test_swap_git_editor.cc tests/test_piece_table.cc - tests/test_search.cc tests/test_search_replace_flow.cc tests/test_reflow_paragraph.cc tests/test_reflow_indented_bullets.cc @@ -418,7 +417,6 @@ if (BUILD_TESTS) SearchHighlight.cc KKeymap.cc SwapRecorder.h - OptimizedSearch.cc UndoNode.cc UndoTree.cc UndoSystem.cc diff --git a/Command.cc b/Command.cc index 7f185a7..507d1af 100644 --- a/Command.cc +++ b/Command.cc @@ -265,7 +265,6 @@ ensure_cursor_visible(const Editor &ed, Buffer &buf) } buf.SetOffsets(rowoffs, coloffs); - buf.SetRenderX(rx); } @@ -3051,7 +3050,6 @@ cmd_show_help(CommandContext &ctx) b.SetDirty(false); b.SetCursor(0, 0); b.SetOffsets(0, 0); - b.SetRenderX(0); }; if (help_index != static_cast(-1)) { diff --git a/Editor.cc b/Editor.cc index 1ae79e6..022fb95 100644 --- a/Editor.cc +++ b/Editor.cc @@ -728,15 +728,11 @@ void Editor::Reset() { rows_ = cols_ = 0; - mode_ = 0; kill_ = 0; - no_kill_ = 0; - dirtyex_ = 0; msg_.clear(); msgtm_ = 0; uarg_ = 0; ucount_ = 0; - repeatable_ = false; quit_requested_ = false; quit_confirm_pending_ = false; // Reset close-confirm/save state diff --git a/Editor.h b/Editor.h index 805881a..fd97eff 100644 --- a/Editor.h +++ b/Editor.h @@ -79,19 +79,6 @@ public: } - // Mode and flags (mirroring legacy fields) - void SetMode(int m) - { - mode_ = m; - } - - - [[nodiscard]] int Mode() const - { - return mode_; - } - - void SetKillChain(bool on) { kill_ = on ? 1 : 0; @@ -104,18 +91,6 @@ public: } - void SetNoKill(bool on) - { - no_kill_ = on ? 1 : 0; - } - - - [[nodiscard]] bool NoKill() const - { - return no_kill_ != 0; - } - - // Kill ring API void KillRingClear() { @@ -164,18 +139,6 @@ public: } - void SetDirtyEx(int d) - { - dirtyex_ = d; - } - - - [[nodiscard]] int DirtyEx() const - { - return dirtyex_; - } - - void SetUniversalArg(int uarg, int ucount) { uarg_ = uarg; @@ -189,12 +152,6 @@ public: } - [[nodiscard]] int UCount() const - { - return ucount_; - } - - // --- Universal argument control (C-u) --- // Begin or extend a universal argument (like ke's uarg_start) void UArgStart(); @@ -209,19 +166,6 @@ public: // If no universal argument active, returns 1. int UArgGet(); - // Repeatable command flag: input layer can mark the next command as repeatable - void SetRepeatable(bool on) - { - repeatable_ = on; - } - - - [[nodiscard]] bool Repeatable() const - { - return repeatable_; - } - - // Status message storage. Rendering is renderer-dependent; the editor // merely stores the current message and its timestamp. void SetStatus(const std::string &message); @@ -666,14 +610,10 @@ private: }; std::size_t rows_ = 0, cols_ = 0; - int mode_ = 0; int kill_ = 0; // KILL CHAIN - int no_kill_ = 0; // don't kill in delete_row - int dirtyex_ = 0; std::string msg_; std::time_t msgtm_ = 0; int uarg_ = 0, ucount_ = 0; // C-u support - bool repeatable_ = false; // whether the next command is repeatable std::vector buffers_; std::vector *shared_buffers_ = nullptr; // if set, use this instead of buffers_ diff --git a/ErrorRecovery.h b/ErrorRecovery.h index a98ce17..d2d3b3f 100644 --- a/ErrorRecovery.h +++ b/ErrorRecovery.h @@ -52,11 +52,6 @@ struct RetryPolicy { } - // Conservative policy for non-critical operations: fewer attempts, slower retries - static RetryPolicy Conservative() - { - return RetryPolicy{2, std::chrono::milliseconds(200), 2.5, std::chrono::milliseconds(10000)}; - } }; // Retry a function with exponential backoff for transient errors diff --git a/OptimizedSearch.cc b/OptimizedSearch.cc deleted file mode 100644 index 4ae71a6..0000000 --- a/OptimizedSearch.cc +++ /dev/null @@ -1,79 +0,0 @@ -#include "OptimizedSearch.h" - -#include - - -void -OptimizedSearch::build_bad_char(const std::string &pattern) -{ - if (pattern == last_pat_) - return; - last_pat_ = pattern; - std::fill(bad_char_.begin(), bad_char_.end(), -1); - for (std::size_t i = 0; i < pattern.size(); ++i) { - bad_char_[static_cast(pattern[i])] = static_cast(i); - } -} - - -std::size_t -OptimizedSearch::find_first(const std::string &text, const std::string &pattern, std::size_t start) -{ - const std::size_t n = text.size(); - const std::size_t m = pattern.size(); - if (m == 0) - return start <= n ? start : std::string::npos; - if (m > n || start >= n) - return std::string::npos; - build_bad_char(pattern); - std::size_t s = start; - while (s <= n - m) { - std::size_t j = m; - while (j > 0 && pattern[j - 1] == text[s + j - 1]) { - --j; - } - if (j == 0) { - return s; // match found - } - unsigned char badc = static_cast(text[s + j - 1]); - int bcidx = bad_char_[badc]; - std::size_t shift = (j - 1 > static_cast(bcidx)) - ? (j - 1 - static_cast(bcidx)) - : 1; - s += shift; - } - return std::string::npos; -} - - -std::vector -OptimizedSearch::find_all(const std::string &text, const std::string &pattern, std::size_t start) -{ - std::vector res; - const std::size_t n = text.size(); - const std::size_t m = pattern.size(); - if (m == 0) - return res; - if (m > n || start >= n) - return res; - build_bad_char(pattern); - std::size_t s = start; - while (s <= n - m) { - std::size_t j = m; - while (j > 0 && pattern[j - 1] == text[s + j - 1]) { - --j; - } - if (j == 0) { - res.push_back(s); - s += m; // non-overlapping - continue; - } - unsigned char badc = static_cast(text[s + j - 1]); - int bcidx = bad_char_[badc]; - std::size_t shift = (j - 1 > static_cast(bcidx)) - ? (j - 1 - static_cast(bcidx)) - : 1; - s += shift; - } - return res; -} diff --git a/OptimizedSearch.h b/OptimizedSearch.h deleted file mode 100644 index be5f13f..0000000 --- a/OptimizedSearch.h +++ /dev/null @@ -1,23 +0,0 @@ -// OptimizedSearch.h - Boyer–Moore (bad character) based substring search -#pragma once -#include -#include -#include -#include - -class OptimizedSearch { -public: - OptimizedSearch() = default; - - // Find first occurrence at or after start. Returns npos if not found. - std::size_t find_first(const std::string &text, const std::string &pattern, std::size_t start = 0); - - // Find all non-overlapping matches at or after start. Returns starting indices. - std::vector find_all(const std::string &text, const std::string &pattern, std::size_t start = 0); - -private: - std::array bad_char_{}; - std::string last_pat_; - - void build_bad_char(const std::string &pattern); -}; diff --git a/PieceTable.cc b/PieceTable.cc index 87cf81a..71a33f8 100644 --- a/PieceTable.cc +++ b/PieceTable.cc @@ -10,13 +10,6 @@ PieceTable::PieceTable() = default; -PieceTable::PieceTable(const std::size_t initialCapacity) -{ - add_.reserve(initialCapacity); - materialized_.reserve(initialCapacity); -} - - PieceTable::PieceTable(const std::size_t initialCapacity, const std::size_t piece_limit, const std::size_t small_piece_threshold, @@ -109,14 +102,6 @@ PieceTable::operator=(PieceTable &&other) noexcept PieceTable::~PieceTable() = default; -void -PieceTable::Reserve(const std::size_t newCapacity) -{ - add_.reserve(newCapacity); - materialized_.reserve(newCapacity); -} - - // Setter to allow tuning consolidation heuristics void PieceTable::SetConsolidationParams(const std::size_t piece_limit, @@ -417,12 +402,10 @@ PieceTable::RebuildLineIndex() const const std::string &src = pc.src == Source::Original ? original_ : add_; const char *base = src.data() + static_cast(pc.start); - for (std::size_t j = 0; j < pc.len; ++j) { - if (base[j] == '\n') { - // next line starts after the newline - line_index_.push_back(pos + j + 1); - } - } + const char *end = base + pc.len; + // The next line starts after each newline. + for (const char *p = base; (p = static_cast(std::memchr(p, '\n', end - p))) != nullptr; ++p) + line_index_.push_back(pos + static_cast(p - base) + 1); pos += pc.len; } @@ -800,14 +783,10 @@ PieceTable::GetLine(std::size_t line_num) const auto [start, end] = GetLineRange(line_num); if (end < start) return std::string(); - // Trim trailing '\n' - if (end > start) { - // To check last char, we can get it via GetRange of len 1 at end-1 without materializing whole - std::string last = GetRange(end - 1, 1); - if (!last.empty() && last[0] == '\n') { - end -= 1; - } - } + // Every line but the last ends at a newline (each newline starts an + // index entry); leave it out. + if (end > start && line_num + 1 < line_index_.size()) + end -= 1; return GetRange(start, end - start); } @@ -837,13 +816,10 @@ PieceTable::LineColToByteOffset(std::size_t row, std::size_t col) const return total_size_; std::size_t start = line_index_[row]; std::size_t end = (row + 1 < line_index_.size()) ? line_index_[row + 1] : total_size_; - // Clamp col to line length excluding trailing newline - if (end > start) { - std::string last = GetRange(end - 1, 1); - if (!last.empty() && last[0] == '\n') { - end -= 1; - } - } + // Clamp col to the line's length without its newline (every line but + // the last has one). + if (end > start && row + 1 < line_index_.size()) + end -= 1; std::size_t target = start + std::min(col, end - start); return target; } diff --git a/PieceTable.h b/PieceTable.h index 4224f33..c41545a 100644 --- a/PieceTable.h +++ b/PieceTable.h @@ -52,8 +52,6 @@ class PieceTable { public: PieceTable(); - explicit PieceTable(std::size_t initialCapacity); - // Advanced constructor allowing configuration of consolidation heuristics PieceTable(std::size_t initialCapacity, std::size_t piece_limit, @@ -70,9 +68,6 @@ public: ~PieceTable(); - // Public API mirrors GapBuffer - void Reserve(std::size_t newCapacity); - void AppendChar(char c); void Append(const char *s, std::size_t len); @@ -109,13 +104,6 @@ public: } - [[nodiscard]] std::size_t Capacity() const - { - // Capacity for piece table isn't directly meaningful; report materialized capacity - return materialized_.capacity(); - } - - // ===== New buffer-wide API (Phase 1) ===== // Byte-based editing operations void Insert(std::size_t byte_offset, const char *text, std::size_t len); diff --git a/Swap.cc b/Swap.cc index c1e6ddc..4386d7b 100644 --- a/Swap.cc +++ b/Swap.cc @@ -1202,47 +1202,6 @@ SwapManager::RecordDelete(Buffer &buf, int row, int col, std::size_t len) } -void -SwapManager::RecordSplit(Buffer &buf, int row, int col) -{ - { - std::lock_guard lg(mtx_); - auto it = journals_.find(&buf); - if (it == journals_.end() || it->second.suspended) - return; - } - Pending p; - p.buf = &buf; - p.type = SwapRecType::SPLIT; - // payload v1: [encver u8=1][row u32][col u32] - p.payload.push_back(1); - put_le32(p.payload, static_cast(std::max(0, row))); - put_le32(p.payload, static_cast(std::max(0, col))); - enqueue(std::move(p)); - maybe_request_checkpoint(buf, 1); -} - - -void -SwapManager::RecordJoin(Buffer &buf, int row) -{ - { - std::lock_guard lg(mtx_); - auto it = journals_.find(&buf); - if (it == journals_.end() || it->second.suspended) - return; - } - Pending p; - p.buf = &buf; - p.type = SwapRecType::JOIN; - // payload v1: [encver u8=1][row u32] - p.payload.push_back(1); - put_le32(p.payload, static_cast(std::max(0, row))); - enqueue(std::move(p)); - maybe_request_checkpoint(buf, 1); -} - - void SwapManager::maybe_request_checkpoint(Buffer &buf, const std::size_t approx_edit_bytes) { diff --git a/Swap.h b/Swap.h index 61ce73a..11383cc 100644 --- a/Swap.h +++ b/Swap.h @@ -24,6 +24,7 @@ namespace kte { enum class SwapRecType : std::uint8_t { INS = 1, DEL = 2, + // SPLIT and JOIN are no longer written; replay still reads them. SPLIT = 3, JOIN = 4, META = 0xF0, @@ -213,10 +214,6 @@ private: void RecordDelete(Buffer &buf, int row, int col, std::size_t len); - void RecordSplit(Buffer &buf, int row, int col); - - void RecordJoin(Buffer &buf, int row); - void RecordCheckpoint(Buffer &buf, bool urgent_flush); void maybe_request_checkpoint(Buffer &buf, std::size_t approx_edit_bytes); diff --git a/UndoNodePool.h b/UndoNodePool.h deleted file mode 100644 index b4d64bd..0000000 --- a/UndoNodePool.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include -#include -#include -#include "UndoNode.h" - -// Pool allocator for UndoNode to eliminate frequent malloc/free. -// Uses fixed-size blocks to keep node addresses stable. -class UndoNodePool { -public: - explicit UndoNodePool(std::size_t block_size = 64) - : block_size_(block_size) {} - - - UndoNode *acquire() - { - if (available_.empty()) - allocate_block(); - auto *node = available_.top(); - available_.pop(); - // Node comes zeroed; ensure links are reset - node->text.clear(); - node->parent = nullptr; - node->child = nullptr; - node->next = nullptr; - node->row = node->col = 0; - node->type = UndoType{}; - return node; - } - - - void release(UndoNode *node) - { - if (!node) - return; - // Clear heavy fields to free memory held by strings - node->text.clear(); - node->parent = nullptr; - node->child = nullptr; - node->next = nullptr; - node->row = node->col = 0; - node->type = UndoType{}; - available_.push(node); - } - -private: - void allocate_block() - { - // allocate a new block; keep ownership so memory stays valid - std::unique_ptr block(new UndoNode[block_size_]); - UndoNode *base = block.get(); - blocks_.push_back(std::move(block)); - for (std::size_t i = 0; i < block_size_; ++i) { - // ensure the node is reset; rely on default constructor/zero init - available_.push(&base[i]); - } - } - - - std::size_t block_size_; - std::vector > blocks_; - std::stack available_; -}; diff --git a/tests/test_search.cc b/tests/test_search.cc deleted file mode 100644 index 102770a..0000000 --- a/tests/test_search.cc +++ /dev/null @@ -1,46 +0,0 @@ -#include "Test.h" -#include "OptimizedSearch.h" -#include -#include - - -static std::vector -ref_find_all(const std::string &text, const std::string &pat) -{ - std::vector res; - if (pat.empty()) - return res; - std::size_t from = 0; - while (true) { - auto p = text.find(pat, from); - if (p == std::string::npos) - break; - res.push_back(p); - from = p + pat.size(); - } - return res; -} - - -TEST(OptimizedSearch_basic_cases) -{ - OptimizedSearch os; - struct Case { - std::string text; - std::string pat; - } cases[] = { - {"", ""}, - {"", "a"}, - {"a", ""}, - {"a", "a"}, - {"aaaaa", "aa"}, - {"hello world", "world"}, - {"abcabcabc", "abc"}, - {"the quick brown fox", "fox"}, - }; - for (auto &c: cases) { - auto got = os.find_all(c.text, c.pat, 0); - auto ref = ref_find_all(c.text, c.pat); - ASSERT_EQ(got, ref); - } -}