vt: add OSC 133 prompt marks with jump and pipe-output actions

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I0afe252fefa3eb82559a35d03ba449376a6a6964
This commit is contained in:
raf 2026-06-25 13:41:34 +03:00
commit 72ec651ff1
No known key found for this signature in database
GPG key ID: 29D95B64378DB4BF
5 changed files with 208 additions and 1 deletions

View file

@ -4,7 +4,9 @@ use std::io::Write as _;
use vte::{Params, Perform};
use crate::grid::{Color, CursorShape, Flags, Grid, MouseEncoding, MouseProtocol, Underline};
use crate::grid::{
Color, CursorShape, Flags, Grid, MouseEncoding, MouseProtocol, PromptKind, Underline,
};
use crate::theme::{Rgb, Theme};
/// G0/G1 character set designation.
@ -667,6 +669,17 @@ impl Perform for Term {
self.cwd = file_uri_path(uri);
}
}
// OSC 133: shell-integration prompt marks (A/B/C/D, with optional
// `;key=value` attributes we ignore).
Some(&n) if n == b"133" => {
if let Some(kind) = params
.get(1)
.and_then(|p| p.first())
.and_then(|&b| prompt_kind(b))
{
self.grid.set_prompt_mark(kind);
}
}
// OSC 4: set/query palette entries (pairs of index;spec).
Some(&n) if n == b"4" => self.osc_palette(params, bell),
// OSC 104: reset palette (all, or the listed indices).
@ -810,6 +823,17 @@ fn base64_decode(data: &[u8]) -> Option<Vec<u8>> {
Some(out)
}
/// Map an OSC 133 mark letter to a [`PromptKind`].
fn prompt_kind(b: u8) -> Option<PromptKind> {
match b {
b'A' => Some(PromptKind::PromptStart),
b'B' => Some(PromptKind::CmdStart),
b'C' => Some(PromptKind::OutputStart),
b'D' => Some(PromptKind::CmdEnd),
_ => None,
}
}
/// Extract the local path from an OSC 7 `file://host/path` URI, percent-decoding
/// `%XX` escapes. The host part is ignored (we only spawn locally). Returns
/// `None` if it is not a usable absolute path.
@ -1114,6 +1138,15 @@ mod tests {
assert_eq!(t.grid().row_text(0), "─│");
}
#[test]
fn osc133_marks_capture_last_command_output() {
let mut t = Term::new(12, 6);
feed(&mut t, b"\x1b]133;A\x07$ echo hi\r\n"); // prompt + typed command
feed(&mut t, b"\x1b]133;C\x07hi\r\n"); // output start, then output
feed(&mut t, b"\x1b]133;D\x07"); // command finished
assert_eq!(t.grid().last_command_output().as_deref(), Some("hi\n"));
}
#[test]
fn osc7_tracks_cwd_and_decodes_percent() {
let mut t = Term::new(20, 1);