T59: OSC 133 B stamps the line typed after a prompt with a link of its own, numbered by the commands ended, so a shell whose prompt prints nothing, as fish's empty prompt, takes clicks from its first typed cell, and fish's right prompt drawn again at each key keeps the line

This commit is contained in:
ZacharyZhang-NY committed 2026-10-19 16:09:11 -04:00
1 parent 9fa68b24a7
commit 84364ce03e
6 files changed
+247 -80

No files matched your search

+58 -16
View File
@@ -40,7 +40,12 @@ fn target_of(term: &Term<VoidListener>, line: i32, column: usize) -> Option<Stri
fn a_mark_becomes_its_prompt_s_numbered_link() {
assert_eq!(
passed(&[b"\x1b]133;A\x07$ \x1b]133;B\x1b\\x"]),
b"\x1b]8;id=\x7f1;\x7f\x07$ \x1b]8;;\x07\x1b\\x"
b"\x1b]8;id=\x7f1;\x7f\x07$ \x1b]8;id=\x7f0i;\x7f\x07\x1b\\x"
);
assert_eq!(
passed(&[b"\x1b]133;A\x07$ \x1b]133;B\x07ls\x1b]133;C\x07out"]),
b"\x1b]8;id=\x7f1;\x7f\x07$ \x1b]8;id=\x7f0i;\x7f\x07ls\x1b]8;;\x07out",
"the input runs from B to C"
);
assert_eq!(
passed(&[b"\x1b]133;A;k=i\x07\x1b]133;D;1\x18"]),
@@ -63,7 +68,11 @@ fn the_rewrite_reads_a_mark_as_the_session_hears_it() {
"A", "A;k=i", "N", "B", "I", "C;x", "D;1", "D;abc", "D", "", "AB", "L", "P;k=i", "Q", "a",
];
for body in bodies {
let heard = marks::read(body.as_bytes()).map(|mark| matches!(mark, Mark::Prompt(_)));
let heard = marks::read(body.as_bytes()).map(|mark| match mark {
Mark::Prompt(_) => Edge::Prompt,
Mark::Input => Edge::Input,
Mark::Output | Mark::Done(_) => Edge::End,
});
assert_eq!(mark(body.as_bytes()), heard, "{body}");
}
}
@@ -154,14 +163,11 @@ fn a_program_s_link_inside_a_prompt_carries_its_number() {
&open.grid()[Line(0)][Column(1)],
&open.grid()[Line(0)][Column(2)],
);
assert!(
z.hyperlink()
.is_some_and(|link| !link.id().starts_with(NUMBERED))
);
assert_eq!(part(z), Some(Part::Input(0)), "the input inside it");
assert_eq!(
target_of(&open, 0, 2).as_deref(),
Some("file:///x"),
"its own again past the prompt"
"its own target past the prompt"
);
assert!(
y.hyperlink()
@@ -178,16 +184,12 @@ fn a_long_target_numbers_a_prompt_in_either_order() {
for bytes in [before, inside] {
let term = parsed(bytes.as_bytes(), 8, 2);
let row = &term.grid()[Line(0)];
let numbered = |column: usize| {
row[Column(column)]
.hyperlink()
.is_some_and(|link| link.id().starts_with(NUMBERED))
};
assert_eq!(prompt(row), Some(1), "{bytes:?}");
assert!(
numbered(1) && !numbered(2),
prompted(&row[Column(1)]) && !prompted(&row[Column(2)]),
"the prompt's cell alone: {bytes:?}"
);
assert_eq!(part(&row[Column(2)]), Some(Part::Input(0)), "{bytes:?}");
assert_eq!(target_of(&term, 0, 2).as_deref(), Some(target.as_str()));
}
}
@@ -215,7 +217,7 @@ fn controls_after_an_osc_s_closing_escape_run_where_vte_runs_them() {
fn del_and_high_bytes_in_an_escape_go_on_after_it() {
assert_eq!(
passed(&[b"\x1b\x7f]133;A\x07$\x1b\xff", b"]133;B\x07"]),
b"\x1b\x7f]8;id=\x7f1;\x7f\x07$\x1b\xff]8;;\x07"
b"\x1b\x7f]8;id=\x7f1;\x7f\x07$\x1b\xff]8;id=\x7f0i;\x7f\x07"
);
}
@@ -228,7 +230,7 @@ fn controls_vte_runs_inside_an_escape_go_on_before_the_link() {
}
#[test]
fn a_prompt_s_cells_carry_its_link_through_sgr_0_and_its_input_none() {
fn a_prompt_s_cells_carry_its_link_through_sgr_0_and_its_input_its_own() {
let term = parsed(
b"x\r\n\x1b]133;A\x07\x1b[31m$\x1b[0m \x1b]133;B\x07ls",
8,
@@ -237,7 +239,17 @@ fn a_prompt_s_cells_carry_its_link_through_sgr_0_and_its_input_none() {
assert_eq!(prompt(&term.grid()[Line(0)]), None);
assert_eq!(prompt(&term.grid()[Line(1)]), Some(1));
assert_eq!(target_of(&term, 1, 1).as_deref(), Some(LINK));
assert_eq!(target_of(&term, 1, 2), None, "input");
assert_eq!(
part(&term.grid()[Line(1)][Column(2)]),
Some(Part::Input(0)),
"input"
);
let ran = parsed(
b"\x1b]133;A\x07$\x1b]133;B\x07ls\x1b]133;C\x07\r\nout",
8,
2,
);
assert_eq!(target_of(&ran, 1, 0), None, "output");
}
#[test]
@@ -329,3 +341,33 @@ fn a_prompt_keeps_its_start_through_a_resize() {
let top = term.topmost_line().0 + first.expect("prompt 1") as i32;
assert_eq!(offset as i32, -top);
}
#[test]
fn input_carries_the_commands_ended_before_it() {
let line = b"\x1b]133;A\x07$\x1b]133;B\x07ab\x1b]133;A\x07R\x1b]133;B\x07c";
let term = parsed(line, 8, 2);
let at = |column: usize| part(&term.grid()[Line(0)][Column(column)]);
assert_eq!(
(at(0), at(1), at(3)),
(
Some(Part::Prompt(1)),
Some(Part::Input(0)),
Some(Part::Prompt(2))
)
);
assert_eq!(
at(4),
Some(Part::Input(0)),
"a prompt drawn again keeps the line"
);
let next = parsed(
b"\x1b]133;A\x07$\x1b]133;B\x07a\x1b]133;C\x07\x1b]133;D\x07\x1b]133;A\x07$\x1b]133;B\x07b",
8,
2,
);
assert_eq!(
part(&next.grid()[Line(0)][Column(3)]),
Some(Part::Input(2)),
"C and D each end it"
);
}