Journal insert_row as one record; keep the cursor in the buffer; fix ENABLE_ASAN
- 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
This commit is contained in:
+22
-2
@@ -4103,8 +4103,12 @@ cmd_yank(CommandContext &ctx)
|
||||
}
|
||||
const std::size_t delta_y = nl_count;
|
||||
const std::size_t delta_x = (last_nl == std::string::npos) ? ins.size() : (ins.size() - last_nl - 1);
|
||||
const std::size_t above = (y0 >= sy) ? (y0 - sy) : 0;
|
||||
buf->SetCursor(delta_x, y0 + delta_y + above * nl_count);
|
||||
// The cursor may have left the selection (C-k a, Enter, ...) without
|
||||
// updating it; place it relative to the nearest selected line.
|
||||
const std::size_t yc = std::clamp(y0, sy, ey);
|
||||
const std::size_t above = yc - sy;
|
||||
buf->SetCursor(delta_x, yc + delta_y + above * nl_count);
|
||||
clamp_cursor_to_buffer(*buf);
|
||||
} else {
|
||||
if (u)
|
||||
u->Begin(UndoType::Paste);
|
||||
@@ -5765,6 +5769,22 @@ run_handler(Editor &ed, const Command &cmd, CommandContext &ctx)
|
||||
if (UndoSystem *u = b->Undo())
|
||||
u->AbortGroups();
|
||||
};
|
||||
// Whatever a command did to the cursor (remembered positions, visual-line
|
||||
// arithmetic, row counts from before an edit), never leave it past a line
|
||||
// or the buffer: the next edit would be applied at the clamped position
|
||||
// but recorded for undo at the stale one, and could not be undone.
|
||||
struct ClampOnExit {
|
||||
Editor &ed;
|
||||
|
||||
|
||||
~ClampOnExit()
|
||||
{
|
||||
if (Buffer *b = ed.CurrentBuffer())
|
||||
clamp_cursor_to_buffer(*b);
|
||||
}
|
||||
} clamp_on_exit{ed};
|
||||
if (Buffer *b = ed.CurrentBuffer())
|
||||
clamp_cursor_to_buffer(*b);
|
||||
try {
|
||||
return cmd.handler(ctx);
|
||||
} catch (const std::exception &e) {
|
||||
|
||||
Reference in New Issue
Block a user