- HighlighterEngine caches spans only for rows near the requested row
and trims the cache around it past 16k entries; per-row end states are
still kept to resume lexing. Jumping to the end of a 78 MB C++ file
cached every row's spans (854 MB RSS, now 197 MB), and the first edit
near the top spent 270 ms freeing them.
- Plain and regex search read lines as views into the materialized
buffer instead of Buffer::Rows(), which built (and kept) a string per
line, O(lines x pieces) after edits. Plain search scans the whole text
once and tracks rows by newline.
- transform_rows() applies a per-row rewrite of a row range as one edit
(replace_rows_text). Indent/unindent region and visual-line insert,
backspace, delete, newline and yank used one piece-table edit per row,
each linear in the buffer: 1.1 s per keystroke with 5,000 rows selected
in a 26 MB file (now 4 ms), and minutes to indent a large region.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
- Indent-region skips empty lines. With the whole buffer selected, the
empty row after the final newline became a tab-only last line.
- Reflow no longer wraps before a line's first word: a first word longer
than the width (a URL) got a prefix-only line before it, a blank line
or a bullet marker split from its text, and another on every reflow.
- A whitespace-only line (spaces, tabs, a CR) separates paragraphs for
reflow; such paragraphs were merged.
- Reflow of CRLF lines strips the CR before wrapping and restores it on
every output line; the CRs ended up in the middle of joined lines.
- A counted backspace or delete-char is one undo step even when it joins
lines.
- The C++ highlighter stops looking for a raw-string '(' after 16
delimiter characters; scanning the whole line at every R" was
quadratic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
- The shortened fallback swap name was capped at exactly NAME_MAX, so
compaction's '<journal>.tmp' failed with ENAMETOOLONG on every
checkpoint and the journal grew by a full copy of the buffer each
time. Cap it at NAME_MAX - 4.
- A lone UTF-8 lead byte (8-bit Meta terminals) waited a second for
continuation bytes on every key; wait 250 ms. An invalid byte now ends
a pending ESC/C-k prefix instead of leaving it for the next key.
- Journal notices are shown after a deferred open's own status (which
used to overwrite them), all pending ones at once, and the lock-out
notice is not repeated after every save while the lock is still held.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
- SwapManager queues user notices when a journal is held by another kte
(this session's edits are then not journaled) and, once per gap, when
journal writes fail. Editor shows them in the status line from its
per-frame hook. Both went only to the error log, so crash recovery was
silently off or incomplete.
- The fallback swap name '<basename>.<hash>.swp' exceeded NAME_MAX for
basenames over ~233 bytes, so such files were never journaled; the
basename is shortened (on a UTF-8 boundary) only in that case.
- Audit doc: sixth-round findings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
- Buffer::insert_row recorded its text and newline as two swap records
after inserting both. A checkpoint triggered by the first (1 MiB of
edits, or the checkpoint timer) snapshotted the newline already, and
the second added it again on replay: recovery after undoing C-k C-k
silently gained a blank line. It is now one record.
- Visual-line yank placed the cursor relative to its row assuming that
row was inside the selection; after C-k a or Enter it was not, and the
cursor ended past the end of a line or the buffer, where typed text was
recorded for undo at a position it was not inserted at and could not
be undone. The formula uses the nearest selected row, and run_handler
now clamps the cursor to the buffer before and after every command.
- ENABLE_ASAN added -fsanitize=address to CMAKE_C_FLAGS only; the
project is C++, so nothing was instrumented. Add it to
CMAKE_CXX_FLAGS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
After a lost record the journal drops position-based records until a
full checkpoint. That checkpoint was only requested from later edits,
rate-limited to once a second; when the writer handled the failing
record before its edit asked for a checkpoint, the checkpoint failed as
well and consumed the slot, so edits made within the next second were
not recovered and, if the user stopped typing, the journal stayed behind
indefinitely. SwapManager::RetryGapCheckpoints(), called once per frame
from Editor::ProcessPendingOpens, requests it once the interval has
passed. This also made SwapReplay_LostRecord_ResyncsWithCheckpoint flaky
under load; the test now exercises the retry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
- Save's in-place fallbacks (no temp file; no hard-link copy) now apply
only when the directory refuses a new name (EACCES, EPERM,
ENAMETOOLONG). On ENOSPC, EDQUOT or EIO the in-place write truncated
the file and then failed, leaving it corrupt; now the save fails and
the file is untouched (checked on a full tmpfs).
- replace_rows_text/replace_buffer_text delete and insert only the span
between the old and new text's common prefix and suffix (kept on
UTF-8 boundaries). Replace-all recorded the whole buffer twice in the
undo tree per run.
- Regex search skips long lines only while typing (limit lowered to
2,000 bytes, as for renderer highlighting; '.*q' on a 20 KB line took
12 s), reports how many were skipped, and next/previous match search
every line. Previously matches on long lines could not be found.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
- Added a comprehensive error propagation standardization report detailing dominant patterns, inconsistencies, and recommended remediations (`docs/audits/error-propagation-standardization.md`).
- Integrated `ErrorHandler` into key components, including `main.cc` for robust exception reporting, and added centralized logging to a user state path.
- Introduced EINTR-safe syscall wrappers (`SyscallWrappers.h`, `.cc`) to improve resilience of file and metadata operations.
- Enhanced `DEVELOPER_GUIDE.md` with an error handling conventions section, covering pattern guidelines and best practices.
- Identified gaps in `PieceTable` and internal helpers; deferred fixes with detailed recommendations for improved memory allocation error reporting.