fix(tui): a bracketed paste that is not the clipboard never attaches its image
The origin check ran only on Otty. An IME commit or a diverged tmux buffer on any other terminal attached whatever image sat on the clipboard.
This commit is contained in:
1 parent
0e063e5b02
commit
041c5ca030
6 files changed
+61
-36
No files matched your search
@@ -189,11 +189,6 @@ impl TerminalName {
|
||||
| Self::Zed
|
||||
)
|
||||
}
|
||||
|
||||
/// Only Otty is known to wrap macOS IME commits in bracketed paste.
|
||||
pub fn delivers_ime_as_bracketed_paste(self) -> bool {
|
||||
matches!(self, Self::Otty)
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalContext {
|
||||
|
||||
@@ -51,24 +51,6 @@ fn brand_otty_from_term_program() {
|
||||
assert_eq!(detect_terminal_brand_from_env(&env), TerminalName::Otty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn otty_delivers_ime_as_bracketed_paste_only() {
|
||||
assert!(TerminalName::Otty.delivers_ime_as_bracketed_paste());
|
||||
for brand in [
|
||||
TerminalName::AppleTerminal,
|
||||
TerminalName::Ghostty,
|
||||
TerminalName::Iterm2,
|
||||
TerminalName::Unknown,
|
||||
TerminalName::WezTerm,
|
||||
TerminalName::Kitty,
|
||||
] {
|
||||
assert!(
|
||||
!brand.delivers_ime_as_bracketed_paste(),
|
||||
"{brand:?} must not gate IME bracketed-paste origin"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn otty_is_capability_unclassified_like_unknown() {
|
||||
assert!(TerminalName::Otty.is_capability_unclassified());
|
||||
|
||||
@@ -1258,7 +1258,7 @@ pub enum ProbedAttachment {
|
||||
PersistFailed(String),
|
||||
/// The pasteboard probe completed normally without raster data.
|
||||
NoRaster,
|
||||
/// The attachment result was intentionally discarded because its baseline went stale.
|
||||
/// The attachment result was discarded: stale baseline, or the paste was not the clipboard.
|
||||
ProbeDropped,
|
||||
/// The attachment probe task failed or timed out.
|
||||
ProbeFailed,
|
||||
|
||||
@@ -1103,6 +1103,27 @@ pub(super) fn parse_usage_response(
|
||||
.map(|usage| usage.rows)
|
||||
.map_err(|e| format!("Parse error: {e}"))
|
||||
}
|
||||
/// `Some` ends a clipboard probe before any attachment read.
|
||||
pub(super) fn bracketed_origin_verdict(
|
||||
probe_bracketed: bool,
|
||||
payload: Option<&str>,
|
||||
) -> Option<crate::app::actions::ProbedAttachment> {
|
||||
use crate::app::actions::ProbedAttachment;
|
||||
if !probe_bracketed {
|
||||
return None;
|
||||
}
|
||||
match crate::clipboard::bracketed_payload_came_from_clipboard_result(payload.unwrap_or("")) {
|
||||
Ok(true) => None,
|
||||
Ok(false) => {
|
||||
tracing::debug!("bracketed paste differs from the clipboard; no attachment probe");
|
||||
Some(ProbedAttachment::ProbeDropped)
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::debug!("clipboard unreadable for a bracketed paste; no attachment probe");
|
||||
Some(ProbedAttachment::ProbeDropped)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// A blocking flock on the shared, possibly-network `~/.kigi` lock must never
|
||||
/// stall the event-loop thread (and would hang exit on `/quit`); the registry
|
||||
/// is best-effort, so skip on contention.
|
||||
|
||||
@@ -1637,18 +1637,11 @@ pub(crate) fn execute(
|
||||
{
|
||||
return (ProbedAttachment::ProbeDropped, None);
|
||||
}
|
||||
if probe_bracketed
|
||||
&& crate::terminal::terminal_context()
|
||||
.brand
|
||||
.delivers_ime_as_bracketed_paste()
|
||||
{
|
||||
match crate::clipboard::bracketed_payload_came_from_clipboard_result(
|
||||
probe_text.as_deref().unwrap_or(""),
|
||||
) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => return (ProbedAttachment::ProbeDropped, None),
|
||||
Err(_) => return (ProbedAttachment::ProbeFailed, None),
|
||||
}
|
||||
if let Some(verdict) = helpers::bracketed_origin_verdict(
|
||||
probe_bracketed,
|
||||
probe_text.as_deref(),
|
||||
) {
|
||||
return (verdict, None);
|
||||
}
|
||||
let (image_data, file_urls) = match crate::clipboard::system_clipboard_probe_attachments(
|
||||
probe_text.as_deref(),
|
||||
|
||||
@@ -1664,3 +1664,37 @@ fn session_picker_entry_maps_to_dormant_roster_row() {
|
||||
assert_eq!(roster.origin.kind, "local");
|
||||
assert_eq!(roster.origin.host.as_deref(), Some("box"));
|
||||
}
|
||||
|
||||
fn origin_verdict_with_clipboard(
|
||||
clipboard: Option<&str>,
|
||||
read_failed: bool,
|
||||
probe_bracketed: bool,
|
||||
payload: &str,
|
||||
) -> Option<crate::app::actions::ProbedAttachment> {
|
||||
crate::clipboard::set_clipboard_probe_hook(crate::clipboard::ClipboardProbeHook {
|
||||
text: clipboard.map(str::to_owned),
|
||||
text_read_failed: read_failed,
|
||||
..Default::default()
|
||||
});
|
||||
let verdict = bracketed_origin_verdict(probe_bracketed, Some(payload));
|
||||
crate::clipboard::clear_clipboard_probe_hook();
|
||||
verdict
|
||||
}
|
||||
#[test]
|
||||
fn bracketed_paste_that_is_not_the_clipboard_drops_the_probe() {
|
||||
let verdict = origin_verdict_with_clipboard(Some("copied text"), false, true, "typed by ime");
|
||||
assert!(matches!(verdict, Some(crate::app::actions::ProbedAttachment::ProbeDropped)));
|
||||
}
|
||||
#[test]
|
||||
fn bracketed_paste_matching_the_clipboard_keeps_probing() {
|
||||
assert!(origin_verdict_with_clipboard(Some("copied text"), false, true, "copied text").is_none());
|
||||
}
|
||||
#[test]
|
||||
fn unreadable_clipboard_drops_a_bracketed_probe_without_an_error() {
|
||||
let verdict = origin_verdict_with_clipboard(None, true, true, "anything");
|
||||
assert!(matches!(verdict, Some(crate::app::actions::ProbedAttachment::ProbeDropped)));
|
||||
}
|
||||
#[test]
|
||||
fn clipboard_key_probe_skips_the_origin_check() {
|
||||
assert!(origin_verdict_with_clipboard(Some("copied text"), false, false, "other").is_none());
|
||||
}
|
||||
Reference in new issue
Block a user