Avoid an extra blank quote line when pasting text with a trailing newline (#52418)

## Why

Pasting text ending in a newline into a composer line beginning with `> ` adds an unnecessary empty quoted line before the next Markdown block.

## What changed

Remove one trailing newline from nonempty blockquote paste content before adding quote prefixes. Preserve interior blank lines and the two unquoted newlines that move the cursor to the next block.

## Testing

Add a trailing-newline regression case, update the CRLF/CR expectations, and exercise trailing-newline input in the existing rendering snapshot test.

GitOrigin-RevId: 2367efa8253dd0512de8ba6333b39eba6381fba8
This commit is contained in:
Benjamin Carlsson
2026-10-09 05:12:54 +00:00
committed by copyberry
parent e45069d770
commit 2351d9e1b6
2 changed files with 11 additions and 5 deletions
@@ -9,7 +9,8 @@ use pretty_assertions::assert_eq;
fn blockquote_paste_continues_current_line_prefix() {
for (draft, pasted, expected) in [
("> ", "first\nsecond", "> first\n> second\n\n"),
("intro\n> ", "α\r\n\r\nβ\r", "intro\n> α\n> \n> β\n> \n\n"),
("> ", "first\n", "> first\n\n"),
("intro\n> ", "α\r\n\r\nβ\r", "intro\n> α\n> \n> β\n\n"),
(
"> existing ",
"first\nsecond",
@@ -82,7 +83,7 @@ fn blockquote_paste_rendered() {
/*enhanced_keys_supported*/ false,
|composer| {
composer.insert_str("Please explain:\n> ");
composer.handle_paste("first line\n\nlast line".to_string());
composer.handle_paste("first line\n\nlast line\n".to_string());
composer.insert_str("Next block");
},
);
@@ -1,8 +1,9 @@
//! Classify and integrate composer pastes and capture raw paste tabs before completion or submission.
//! Multiline pastes on a line beginning with `> ` continue that prefix after every newline,
//! including blank and trailing lines. This happens before large pastes are collapsed, so their
//! expanded text retains the quoting. Two unquoted newlines after the inserted text or placeholder
//! leave the cursor in the next Markdown block, including in embedded answer fields.
//! including blank lines, but not after a single trailing newline. This happens before large pastes
//! are collapsed, so their expanded text retains the quoting. Two unquoted newlines after the
//! inserted text or placeholder leave the cursor in the next Markdown block, including in embedded
//! answer fields.
//! Provisional startup input, search queries, and shell input remain literal.
//! Paste classification and insertion share one textarea edit target, including selection replacement.
@@ -149,6 +150,10 @@ impl ChatComposer {
&& current_line.starts_with("> ")
&& pasted.contains('\n');
let pasted = if is_blockquote {
let pasted = pasted
.strip_suffix('\n')
.filter(|pasted| !pasted.is_empty())
.unwrap_or(pasted.as_ref());
std::borrow::Cow::Owned(pasted.replace('\n', "\n> "))
} else {
pasted