- 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
73 lines
2.1 KiB
C++
73 lines
2.1 KiB
C++
/*
|
|
* PasteSplit.h - split pasted/clipboard text into editor commands
|
|
*
|
|
* Pure logic (no SDL/GUI dependencies) so it can be unit tested and shared
|
|
* across frontends. Line breaks in the pasted text must become Newline
|
|
* commands; InsertText itself rejects embedded '\r'/'\n' (see Command.cc).
|
|
*/
|
|
#pragma once
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "InputHandler.h"
|
|
|
|
|
|
// Translate a block of pasted text into a sequence of editor commands.
|
|
//
|
|
// Any of "\n", "\r\n", or a bare "\r" is treated as a single line break and
|
|
// emitted as a Newline command; the text between breaks becomes InsertText
|
|
// commands. Empty text segments are skipped, but line breaks are always
|
|
// emitted so blank lines round-trip correctly.
|
|
inline std::vector<MappedInput> SplitPasteIntoCommands(const std::string &text)
|
|
{
|
|
std::vector<MappedInput> out;
|
|
std::string segment;
|
|
|
|
auto flush_segment = [&]() {
|
|
if (!segment.empty()) {
|
|
out.push_back(MappedInput{true, CommandId::InsertText, segment, 0});
|
|
segment.clear();
|
|
}
|
|
};
|
|
|
|
for (std::size_t i = 0; i < text.size(); ++i) {
|
|
const char c = text[i];
|
|
if (c == '\n' || c == '\r') {
|
|
flush_segment();
|
|
out.push_back(MappedInput{true, CommandId::Newline, std::string(), 0});
|
|
// Collapse a "\r\n" pair into one line break.
|
|
if (c == '\r' && i + 1 < text.size() && text[i + 1] == '\n')
|
|
++i;
|
|
} else {
|
|
segment.push_back(c);
|
|
}
|
|
}
|
|
flush_segment();
|
|
|
|
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;
|
|
}
|