aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJJ Liebig <jonathan.liebig@gmail.com>2026-09-03 19:03:47 +0200
committerGitHub <noreply@github.com>2026-09-03 19:03:47 +0200
commitec745235a95df0004e14069b79689814104c199c (patch)
tree6d48abcad2b60082ed3127c792239d5fbce917b9 /src
parent27b05c1082790e33e072e94822522eaea1753903 (diff)
fix(windows): preserve LF paste order (#3582)
refs #3172
Diffstat (limited to 'src')
-rw-r--r--src/pane.rs1
-rw-r--r--src/platform/mod.rs9
-rw-r--r--src/platform/windows.rs12
-rw-r--r--src/server/headless/tests/mod.rs12
4 files changed, 32 insertions, 2 deletions
diff --git a/src/pane.rs b/src/pane.rs
index 262fbe51..b7bd30bc 100644
--- a/src/pane.rs
+++ b/src/pane.rs
@@ -3121,6 +3121,7 @@ impl PaneRuntime {
}
fn paste_payload(&self, text: String) -> Bytes {
+ let text = crate::platform::prepare_paste_text_for_pty(text);
let bracketed = self.bracketed_paste_enabled();
let payload = if bracketed {
format!("\x1b[200~{text}\x1b[201~")
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index b831e120..f15c71ef 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -39,6 +39,15 @@ pub(crate) fn apply_pane_runtime_marker(command: &mut portable_pty::CommandBuild
apply_pane_runtime_marker_platform(command);
}
+pub(crate) fn prepare_paste_text_for_pty(text: String) -> String {
+ prepare_paste_text_for_pty_platform(text)
+}
+
+#[cfg(not(windows))]
+fn prepare_paste_text_for_pty_platform(text: String) -> String {
+ text
+}
+
#[cfg(not(windows))]
pub(crate) fn terminal_title_for_presentation(title: &str) -> &str {
title
diff --git a/src/platform/windows.rs b/src/platform/windows.rs
index 890c24ca..8d370964 100644
--- a/src/platform/windows.rs
+++ b/src/platform/windows.rs
@@ -134,6 +134,10 @@ pub(crate) fn terminal_title_for_presentation(title: &str) -> &str {
title.strip_prefix("Administrator: ").unwrap_or(title)
}
+pub(crate) fn prepare_paste_text_for_pty_platform(text: String) -> String {
+ text.replace("\r\n", "\n").replace('\n', "\r\n")
+}
+
/// Resolves against the current foreground layout because asynchronous console
/// records do not retain the layout that was active when the key was pressed.
pub(crate) fn resolve_base_printable_key(vk: u16, scan: u16) -> Option<char> {
@@ -2599,6 +2603,14 @@ mod tests {
};
#[test]
+ fn paste_text_uses_windows_line_endings() {
+ assert_eq!(
+ super::prepare_paste_text_for_pty_platform("one\ntwo\r\nthree\rfour".to_owned()),
+ "one\r\ntwo\r\nthree\rfour"
+ );
+ }
+
+ #[test]
fn private_remote_directory_supports_long_paths() {
let base = std::env::temp_dir().join(format!(
"herdr-private-remote-dir-test-{}",
diff --git a/src/server/headless/tests/mod.rs b/src/server/headless/tests/mod.rs
index 77a16fc7..2951a82d 100644
--- a/src/server/headless/tests/mod.rs
+++ b/src/server/headless/tests/mod.rs
@@ -4276,7 +4276,11 @@ fn terminal_attach_paste_uses_plain_text_when_runtime_did_not_enable_brackets()
assert_eq!(
input_rx.try_recv().expect("forwarded paste"),
- Bytes::from_static(b"line one\nline two")
+ Bytes::from_static(if cfg!(windows) {
+ b"line one\r\nline two"
+ } else {
+ b"line one\nline two"
+ })
);
});
}
@@ -4289,7 +4293,11 @@ fn terminal_attach_paste_preserves_brackets_when_runtime_enabled_them() {
assert_eq!(
input_rx.try_recv().expect("forwarded paste"),
- Bytes::from_static(b"\x1b[200~line one\nline two\x1b[201~")
+ Bytes::from_static(if cfg!(windows) {
+ b"\x1b[200~line one\r\nline two\x1b[201~"
+ } else {
+ b"\x1b[200~line one\nline two\x1b[201~"
+ })
);
});
}