From 3b9e3e251bf9d4c7d0b31275cac55df68fe0127a Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 6 Apr 2024 21:34:38 +0200 Subject: [PATCH] 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 --- CHANGELOG.rst | 2 ++ share/functions/__fish_config_interactive.fish | 1 - src/output.rs | 6 +++++- src/reader.rs | 7 +++++++ src/screen.rs | 5 +++++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3f9705035..92e2a1522 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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. diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 9138b255a..d2a7adbfe 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -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 diff --git a/src/output.rs b/src/output.rs index 35554b35c..e3a01a6bc 100644 --- a/src/output.rs +++ b/src/output.rs @@ -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(); } diff --git a/src/reader.rs b/src/reader.rs index 00ab9b427..965ce880d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -23,6 +23,7 @@ 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(); diff --git a/src/screen.rs b/src/screen.rs index 4458fae8c..3f57d5ba3 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -740,6 +740,10 @@ fn write_mbs_if_some(&mut self, s: &Option>) -> bool { 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 @@ fn update(&mut self, left_prompt: &wstr, right_prompt: &wstr, vars: &dyn Environ // 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]);