Commands: second-pass fixes from the follow-up review

- Motion and delete commands read rows through RowsView (on-demand,
  version-cached) instead of Buffer::Rows(), which rebuilt every line of
  the file after each edit. On a 500k-line buffer after an edit:
  backspace/delete/motions 80-90 ms -> ~0.1 ms; C-u 3000 C-d 273 s ->
  0.25 s. Whole-buffer search scans keep Rows() (cached between edits).
- While a prompt is open only prompt-aware and view-only commands run.
  Others edited the buffer behind the prompt (bypassing the read-only
  check) or switched/closed buffers under a pending confirmation, which
  then acted on the wrong buffer. The ':' command path now applies the
  read-only check too.
- Pasting into a prompt (ImGui) flattens line breaks to spaces instead
  of emitting Newline, which accepted the prompt and sent the rest of
  the paste to the buffer (or ran replace-all).
- The mark region clamps both ends; a stale mark past the end of the
  buffer is treated as end of buffer. This fixes kill-region deleting
  the wrong text and leaving the cursor past end-of-line.
- Kill-line with a count stops at end of buffer instead of killing
  lines above the cursor (regression from the D3 change).
- Undo groups nest, so SmartNewline in visual-line mode is one step.
- Word motions and word deletion treat non-ASCII bytes as word
  characters and so never split a UTF-8 character.
- The universal-argument count saturates at 1,000,000 instead of
  overflowing int.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
Claude
2026-09-24 09:16:04 -07:00
committed by Kyle Isom
parent 0075c7aa86
commit 812cde090f
8 changed files with 355 additions and 51 deletions
+24
View File
@@ -46,3 +46,27 @@ inline std::vector<MappedInput> SplitPasteIntoCommands(const std::string &text)
return out;
}
// Pasted text destined for a prompt (search, file name, command). Newline
// would accept the prompt, so each line break (\n, \r\n or \r) becomes a
// single space and the whole paste is one InsertText.
inline std::vector<MappedInput> SplitPasteForPrompt(const std::string &text)
{
std::string flat;
flat.reserve(text.size());
for (std::size_t i = 0; i < text.size(); ++i) {
const char c = text[i];
if (c == '\n' || c == '\r') {
flat.push_back(' ');
if (c == '\r' && i + 1 < text.size() && text[i + 1] == '\n')
++i;
} else {
flat.push_back(c);
}
}
std::vector<MappedInput> out;
if (!flat.empty())
out.push_back(MappedInput{true, CommandId::InsertText, flat, 0});
return out;
}