Emit OSC 133 sequences to mark prompt/command output regions

This allows terminals like foot and kitty to
* scroll to the previous/next prompt with ctrl-shift-{z,x}
* pipe the last command's output to a pager with ctrl-shift-g

Kitty has existing fish shell integration
shell-integration/fish/vendor_conf.d/kitty-shell-integration.fish which we
can simplify now. They keep a state variable to decide which of prompt start,
command start or command end to output.  I think with our implementation
this is no longer necessary, at least I couldn't reproduce any difference.
We also don't need to hook into fish_cancel or fish_posterror like they do;
only in the one place where we actually draw the prompt.

As mentioned in the above shell integration script, kitty disables reflow
when it sees an OSC 133 marker, so we need to do it ourselves,
otherwise the prompt will go blank after a terminal resize.

Closes #10352
This commit is contained in:
Johannes Altmanninger 2024-04-06 21:34:38 +02:00
parent 33701faa8c
commit 3b9e3e251b
5 changed files with 19 additions and 2 deletions

View File

@ -133,6 +133,8 @@ Improved terminal support
^^^^^^^^^^^^^^^^^^^^^^^^^
- Fish now reports the working directory (via OSC 7) unconditionally instead of only for some terminals (:issue:`9955`).
- Fish now sets the terminal window title (via OSC 0) unconditionally instead of only for some terminals (:issue:`10037`).
- Fish now marks the prompt and command-output regions (via OSC 133) to enable terminal shell integration (:issue:`10352`).
Shell integration shortcuts can scroll to the next/previous prompt or show the last command output in a pager.
- Focus reporting is enabled unconditionally, not just inside tmux.
To use it, define functions that handle events ``fish_focus_in`` and ``fish_focus_out``.
- Focus reporting is no longer disabled on the first prompt.

View File

@ -200,7 +200,6 @@ end" >$__fish_config_dir/config.fish
if set -q VTE_VERSION
# Same for these terminals
or string match -q -- 'alacritty*' $TERM
or string match -q -- '*kitty' $TERM
or test "$TERM_PROGRAM" = WezTerm
set -g fish_handle_reflow 0
else if set -q KONSOLE_VERSION

View File

@ -427,8 +427,12 @@ impl Outputter {
/// Emit a terminfo string, like tputs.
/// affcnt (number of lines affected) is assumed to be 1, i.e. not applicable.
pub fn tputs(&mut self, str: &CStr) {
self.tputs_bytes(str.to_bytes());
}
pub fn tputs_bytes(&mut self, str: &[u8]) {
self.begin_buffering();
let _ = self.write(str.to_bytes());
let _ = self.write(str);
self.end_buffering();
}

View File

@ -23,6 +23,7 @@ use once_cell::sync::Lazy;
use std::cell::UnsafeCell;
use std::cmp;
use std::io::BufReader;
use std::io::Write;
use std::num::NonZeroUsize;
use std::ops::Range;
use std::os::fd::RawFd;
@ -625,6 +626,7 @@ fn read_i(parser: &Parser) -> i32 {
data.update_buff_pos(EditableLineTag::Commandline, Some(0));
data.command_line.clear();
data.command_line_changed(EditableLineTag::Commandline);
data.screen.write_bytes(b"\x1b]133;C\x07");
event::fire_generic(parser, L!("fish_preexec").to_owned(), vec![command.clone()]);
let eval_res = reader_run_command(parser, &command);
signal_clear_cancel();
@ -636,6 +638,11 @@ fn read_i(parser: &Parser) -> i32 {
data.exit_loop_requested |= parser.libdata().pods.exit_current_script;
parser.libdata_mut().pods.exit_current_script = false;
let _ = write!(
Outputter::stdoutput().borrow_mut(),
"\x1b]133;D;{}\x07",
parser.get_last_status()
);
event::fire_generic(parser, L!("fish_postexec").to_owned(), vec![command]);
// Allow any pending history items to be returned in the history array.
data.history.resolve_pending();

View File

@ -740,6 +740,10 @@ impl Screen {
self.outp.borrow_mut().tputs_if_some(s)
}
pub(crate) fn write_bytes(&mut self, s: &[u8]) {
self.outp.borrow_mut().tputs_bytes(s);
}
/// Convert a wide string to a multibyte string and append it to the buffer.
fn write_str(&mut self, s: &wstr) {
self.outp.borrow_mut().write_wstr(s);
@ -832,6 +836,7 @@ impl Screen {
// Output the left prompt if it has changed.
if left_prompt != zelf.actual_left_prompt {
zelf.r#move(0, 0);
zelf.write_bytes(b"\x1b]133;A\x07");
let mut start = 0;
for line_break in left_prompt_layout.line_breaks {
zelf.write_str(&left_prompt[start..line_break]);