mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-31 12:21:19 -03:00
fish_tab_title to set terminal tab title independent of window title
Some modern terminals allow creating tabs in a single window; this functionality has a lot of overlap with what a window manager already provides, so I'm not sure if it's a good idea. Regardless, a lot of people still use terminal tabs (or even multiple levels of tabs via tmux!), so let's add a fish-native way to set the tab title independent of the window title. Closes #2692
This commit is contained in:
@@ -16,6 +16,7 @@ Deprecations and removed features
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
- The terminal's tab title can now be set without affecting the window title by defining the :doc:`fish_tab_title <cmds/fish_tab_title>` function (:issue:`2692`).
|
||||
- :doc:`fish_config prompt {choose,save} <cmds/fish_config>` have been taught to reset :doc:`fish_mode_prompt <cmds/fish_mode_prompt>` in addition to the other prompt functions (:issue:`11937`).
|
||||
- Fish now hides the portion of a multiline prompt that is scrolled out of view due to a huge command line. This prevents duplicate lines after repainting with partially visible prompt (:issue:`11911`).
|
||||
- On macOS, fish sets :envvar:`MANPATH` correctly also when that variable was already present in the environment (:issue:`10684`).
|
||||
|
||||
4
doc_src/cmds/fish_tab_title.rst
Normal file
4
doc_src/cmds/fish_tab_title.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
fish_tab_title - define the terminal tab's title
|
||||
================================================
|
||||
|
||||
.. include:: ./fish_title.inc.rst
|
||||
56
doc_src/cmds/fish_title.inc.rst
Normal file
56
doc_src/cmds/fish_title.inc.rst
Normal file
@@ -0,0 +1,56 @@
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
.. synopsis::
|
||||
|
||||
fish_title
|
||||
fish_tab_title
|
||||
|
||||
::
|
||||
|
||||
function fish_title
|
||||
...
|
||||
end
|
||||
|
||||
|
||||
function fish_tab_title
|
||||
...
|
||||
end
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``fish_title`` function is executed before and after a new command is executed or put into the foreground and the output is used as a titlebar message.
|
||||
|
||||
The first argument to ``fish_title`` contains the most recently executed foreground command as a string, if any.
|
||||
|
||||
This requires that your terminal supports :ref:`programmable titles <term-compat-osc-0>` and the feature is turned on.
|
||||
|
||||
To disable setting the title, use an empty function (see below).
|
||||
|
||||
To set the terminal tab title to something other than the terminal window title,
|
||||
define the ``fish_tab_title`` function, which works like ``fish_title`` but overrides that one.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
A simple title::
|
||||
|
||||
function fish_title
|
||||
set -q argv[1]; or set argv fish
|
||||
# Looks like ~/d/fish: git log
|
||||
# or /e/apt: fish
|
||||
echo (fish_prompt_pwd_dir_length=1 prompt_pwd): $argv;
|
||||
end
|
||||
|
||||
Do not change the title::
|
||||
|
||||
function fish_title
|
||||
end
|
||||
|
||||
Change the tab title only::
|
||||
|
||||
function fish_tab_title
|
||||
echo fish $fish_pid
|
||||
end
|
||||
@@ -1,44 +1,4 @@
|
||||
fish_title - define the terminal's title
|
||||
========================================
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
.. synopsis::
|
||||
|
||||
fish_title
|
||||
|
||||
::
|
||||
|
||||
function fish_title
|
||||
...
|
||||
end
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``fish_title`` function is executed before and after a new command is executed or put into the foreground and the output is used as a titlebar message.
|
||||
|
||||
The first argument to fish_title contains the most recently executed foreground command as a string, if any.
|
||||
|
||||
This requires that your terminal supports programmable titles and the feature is turned on.
|
||||
|
||||
To disable setting the title, use an empty function (see below).
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
A simple title::
|
||||
|
||||
function fish_title
|
||||
set -q argv[1]; or set argv fish
|
||||
# Looks like ~/d/fish: git log
|
||||
# or /e/apt: fish
|
||||
echo (fish_prompt_pwd_dir_length=1 prompt_pwd): $argv;
|
||||
end
|
||||
|
||||
Do not change the title::
|
||||
|
||||
function fish_title
|
||||
end
|
||||
.. include:: ./fish_title.inc.rst
|
||||
|
||||
@@ -58,6 +58,7 @@ Known functions are a customization point. You can change them to change how you
|
||||
- :doc:`fish_prompt <cmds/fish_prompt>` and :doc:`fish_right_prompt <cmds/fish_right_prompt>` and :doc:`fish_mode_prompt <cmds/fish_mode_prompt>` to print your prompt.
|
||||
- :doc:`fish_command_not_found <cmds/fish_command_not_found>` to tell fish what to do when a command is not found.
|
||||
- :doc:`fish_title <cmds/fish_title>` to change the terminal's title.
|
||||
- :doc:`fish_tab_title <cmds/fish_tab_title>` to change the terminal tab's title.
|
||||
- :doc:`fish_greeting <cmds/fish_greeting>` to show a greeting when fish starts.
|
||||
- :doc:`fish_should_add_to_history <cmds/fish_should_add_to_history>` to determine if a command should be added to history
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
import glob
|
||||
from glob import glob
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -262,7 +262,7 @@ man_pages = [
|
||||
),
|
||||
("faq", "fish-faq", "", [author], 1),
|
||||
]
|
||||
for path in sorted(glob.glob("cmds/*")):
|
||||
for path in sorted(set(glob("cmds/*.rst")) - set(glob("cmds/*.inc.rst"))):
|
||||
docname = os.path.splitext(path)[0]
|
||||
cmd = os.path.basename(docname)
|
||||
man_pages.append((docname, cmd, get_command_description(path, cmd), "", 1))
|
||||
|
||||
@@ -254,7 +254,9 @@ save this in config.fish or :ref:`a function file <syntax-function-autoloading>`
|
||||
Programmable title
|
||||
------------------
|
||||
|
||||
When using most terminals, it is possible to set the text displayed in the titlebar of the terminal window. Fish does this by running the :doc:`fish_title <cmds/fish_title>` function. It is executed before and after a command and the output is used as a titlebar message.
|
||||
Most terminals allow setting the text displayed in the titlebar of the terminal window.
|
||||
Fish does this by running the :doc:`fish_title <cmds/fish_title>` function.
|
||||
It is executed before and after a command and the output is used as a titlebar message.
|
||||
|
||||
The :doc:`status current-command <cmds/status>` builtin will always return the name of the job to be put into the foreground (or ``fish`` if control is returning to the shell) when the :doc:`fish_title <cmds/fish_title>` function is called. The first argument will contain the most recently executed foreground command as a string.
|
||||
|
||||
|
||||
@@ -263,10 +263,16 @@ Optional Commands
|
||||
-
|
||||
- Disable bracketed paste.
|
||||
- XTerm
|
||||
* - ``\e]0; Pt \x07``
|
||||
* - .. _term-compat-osc-0:
|
||||
|
||||
``\e]0; Pt \x07``
|
||||
- ts
|
||||
- Set window title (OSC 0).
|
||||
- Set terminal window title (OSC 0). Used in :doc:`fish_title <cmds/fish_title>`.
|
||||
- XTerm
|
||||
* - ``\e]2; Pt \x07``
|
||||
- ts
|
||||
- Set terminal tab title (OSC 2). Used in :doc:`fish_tab_title <cmds/fish_tab_title>`.
|
||||
- iTerm2
|
||||
* - ``\e]7;file:// Pt / Pt \x07``
|
||||
-
|
||||
- Report working directory (OSC 7).
|
||||
|
||||
@@ -127,8 +127,8 @@
|
||||
use crate::terminal::Output;
|
||||
use crate::terminal::Outputter;
|
||||
use crate::terminal::TerminalCommand::{
|
||||
ClearScreen, DecrstAlternateScreenBuffer, DecsetAlternateScreenBuffer, DecsetShowCursor,
|
||||
Osc0WindowTitle, Osc133CommandFinished, Osc133CommandStart, QueryCursorPosition,
|
||||
self, ClearScreen, DecrstAlternateScreenBuffer, DecsetAlternateScreenBuffer, DecsetShowCursor,
|
||||
Osc0WindowTitle, Osc2TabTitle, Osc133CommandFinished, Osc133CommandStart, QueryCursorPosition,
|
||||
QueryKittyKeyboardProgressiveEnhancements, QueryPrimaryDeviceAttribute, QueryXtgettcap,
|
||||
QueryXtversion,
|
||||
};
|
||||
@@ -4638,46 +4638,83 @@ pub fn fish_is_unwinding_for_exit() -> bool {
|
||||
/// Write the title to the titlebar. This function is called just before a new application starts
|
||||
/// executing and just after it finishes.
|
||||
///
|
||||
/// \param cmd Command line string passed to \c fish_title if is defined.
|
||||
/// \param parser The parser to use for autoloading fish_title.
|
||||
/// \param cmd Command line string passed to the title functions that are defined.
|
||||
/// \param parser The parser to use for autoloading title functions.
|
||||
/// \param reset_cursor_position If set, issue a \r so the line driver knows where we are
|
||||
pub fn reader_write_title(
|
||||
cmd: &wstr,
|
||||
parser: &Parser,
|
||||
reset_cursor_position: bool, /* = true */
|
||||
) {
|
||||
fn write_title<'a>(
|
||||
parser: &Parser,
|
||||
out: &mut BufferedOutputter,
|
||||
cmd: &wstr,
|
||||
osc: fn(&'a [WString]) -> TerminalCommand<'a>,
|
||||
function_name: &wstr,
|
||||
fallback_title: Option<&wstr>,
|
||||
title_buffer: &'a mut Vec<WString>,
|
||||
) -> bool {
|
||||
let mut title_function_call;
|
||||
let mut title_command = fallback_title;
|
||||
if function::exists(function_name, parser) {
|
||||
title_function_call = function_name.to_owned();
|
||||
if !cmd.is_empty() {
|
||||
title_function_call.push(' ');
|
||||
title_function_call.push_utfstr(&escape_string(
|
||||
cmd,
|
||||
EscapeStringStyle::Script(EscapeFlags::NO_QUOTED | EscapeFlags::NO_TILDE),
|
||||
));
|
||||
}
|
||||
title_command = Some(&title_function_call);
|
||||
}
|
||||
let Some(title_command) = title_command else {
|
||||
return false;
|
||||
};
|
||||
let _ = exec_subshell(
|
||||
title_command,
|
||||
parser,
|
||||
Some(title_buffer),
|
||||
/*apply_exit_status=*/ false,
|
||||
);
|
||||
|
||||
if !title_buffer.is_empty() {
|
||||
out.write_command(osc(&*title_buffer));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
let _scoped = parser.push_scope(|s| {
|
||||
s.is_interactive = false;
|
||||
s.suppress_fish_trace = true;
|
||||
});
|
||||
|
||||
let mut fish_title_command = DEFAULT_TITLE.to_owned();
|
||||
if function::exists(L!("fish_title"), parser) {
|
||||
fish_title_command = L!("fish_title").to_owned();
|
||||
if !cmd.is_empty() {
|
||||
fish_title_command.push(' ');
|
||||
fish_title_command.push_utfstr(&escape_string(
|
||||
cmd,
|
||||
EscapeStringStyle::Script(EscapeFlags::NO_QUOTED | EscapeFlags::NO_TILDE),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = BufferedOutputter::new(Outputter::stdoutput());
|
||||
let mut written = false;
|
||||
let mut lst = vec![];
|
||||
let _ = exec_subshell(
|
||||
&fish_title_command,
|
||||
written |= write_title(
|
||||
parser,
|
||||
Some(&mut lst),
|
||||
/*apply_exit_status=*/ false,
|
||||
&mut out,
|
||||
cmd,
|
||||
Osc0WindowTitle,
|
||||
L!("fish_title"),
|
||||
Some(DEFAULT_TITLE),
|
||||
&mut lst,
|
||||
);
|
||||
written |= write_title(
|
||||
parser,
|
||||
&mut out,
|
||||
cmd,
|
||||
Osc2TabTitle,
|
||||
L!("fish_tab_title"),
|
||||
/*default_title=*/ None,
|
||||
&mut lst,
|
||||
);
|
||||
|
||||
let mut out = BufferedOutputter::new(Outputter::stdoutput());
|
||||
if !lst.is_empty() {
|
||||
out.write_command(Osc0WindowTitle(&lst));
|
||||
}
|
||||
|
||||
out.reset_text_face();
|
||||
if reset_cursor_position && !lst.is_empty() {
|
||||
|
||||
if reset_cursor_position && written {
|
||||
// Put the cursor back at the beginning of the line (issue #2453).
|
||||
out.write_bytes(b"\r");
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ pub(crate) enum TerminalCommand<'a> {
|
||||
// Note that OSC 7 and OSC 52 are written from fish script, and OSC 8 is written in our
|
||||
// man pages (via "man_show_urls").
|
||||
Osc0WindowTitle(&'a [WString]),
|
||||
Osc2TabTitle(&'a [WString]),
|
||||
Osc133CommandStart(&'a wstr),
|
||||
Osc133PromptStart,
|
||||
Osc133CommandFinished(libc::c_int),
|
||||
@@ -175,7 +176,8 @@ fn write(out: &mut impl Output, sequence: &'static [u8]) -> bool {
|
||||
ModifyOtherKeysDisable => write(self, b"\x1b[>4;0m"),
|
||||
ApplicationKeypadModeEnable => write(self, b"\x1b="),
|
||||
ApplicationKeypadModeDisable => write(self, b"\x1b>"),
|
||||
Osc0WindowTitle(title) => osc_0_window_title(self, title),
|
||||
Osc0WindowTitle(title) => osc_0_or_2_terminal_title(self, false, title),
|
||||
Osc2TabTitle(title) => osc_0_or_2_terminal_title(self, true, title),
|
||||
Osc133PromptStart => osc_133_prompt_start(self),
|
||||
Osc133CommandStart(command) => osc_133_command_start(self, command),
|
||||
Osc133CommandFinished(s) => osc_133_command_finished(self, s),
|
||||
@@ -369,8 +371,8 @@ fn query_kitty_progressive_enhancements(out: &mut impl Output) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn osc_0_window_title(out: &mut impl Output, title: &[WString]) -> bool {
|
||||
out.write_bytes(b"\x1b]0;");
|
||||
fn osc_0_or_2_terminal_title(out: &mut impl Output, is_2: bool, title: &[WString]) -> bool {
|
||||
out.write_bytes(if is_2 { b"\x1b]2;" } else { b"\x1b]0;" });
|
||||
for title_line in title {
|
||||
out.write_bytes(&wcs2bytes(title_line));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ set -g fish_greeting ''
|
||||
|
||||
function fish_title
|
||||
end
|
||||
if functions -q fish_tab_title
|
||||
echo >&2 'ERROR: fish_tab_title unexpectedly defined in test environment'
|
||||
end
|
||||
|
||||
function _marker -d '_marker string - prints @MARKER:$string@'
|
||||
echo "@MARKER:$argv[1]@"
|
||||
|
||||
Reference in New Issue
Block a user