diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 42fee34b8..dfa5de232 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -69,7 +69,7 @@ New or improved bindings - Before clearing the screen and redrawing, :kbd:`ctrl-l` now pushes all text located above the prompt to the terminal's scrollback, via a new special input function :ref:`scrollback-push `. For compatibility with terminals that do not implement ECMA-48's :ref:`SCROLL UP ` command, - this function is only enabled if the terminal advertises support for that via :ref:`XTGETTCAP `. + this function is only used if the terminal advertises support for that via :ref:`XTGETTCAP `. - Vi mode has learned :kbd:`ctrl-a` (increment) and :kbd:`ctrl-x` (decrement) (:issue:`11570`). Completions diff --git a/doc_src/cmds/bind.rst b/doc_src/cmds/bind.rst index 2a66500ba..d2fa48dea 100644 --- a/doc_src/cmds/bind.rst +++ b/doc_src/cmds/bind.rst @@ -187,9 +187,7 @@ The following special input functions are available: ``scrollback-push`` pushes earlier output to the terminal scrollback, positioning the prompt at the top. - - For compatibility with terminals that do not provide the ECMA-48 ``SCROLL UP`` command, - this command does nothing unless the terminal advertises support for that command via :ref:`XTGETTCAP `. + This requires the terminal to implement the ECMA-48 :ref:`SCROLL UP ` command and :ref:`cursor position reporting `. ``complete`` guess the remainder of the current token diff --git a/doc_src/cmds/status.rst b/doc_src/cmds/status.rst index 3bb11e92c..55b60e029 100644 --- a/doc_src/cmds/status.rst +++ b/doc_src/cmds/status.rst @@ -34,6 +34,7 @@ Synopsis status get-file FILE status list-files [PATH] status terminal + status test-terminal-feature FEATURE Description ----------- @@ -118,14 +119,25 @@ The following operations (subcommands) are available: This lists the files embedded in the fish binary at compile time. Only files where the path starts with the optional *FILE* argument are shown. Returns 0 if something was printed, 1 otherwise. +.. _status-terminal: + **terminal** Prints the name and version of the terminal fish is running inside (for example as reported via :ref:`XTVERSION `). - This is not available during early startup but only just before the first interactive prompt is shown (possibly via builtin :doc:`read `), - that is, on the first ``fish_prompt`` or ``fish_read`` :ref:`event `. + This is not available during early startup but only starting from when the first interactive prompt is shown, possibly via builtin :doc:`read `, + so before the first ``fish_prompt`` or ``fish_read`` :ref:`event `. + +.. _status-test-terminal-features: + +**test-terminal-feature** *FEATURE* + Returns 0 when the terminal was :ref:`detected ` to support the given feature. + Like :ref:`status terminal `, this only works once the first interactive prompt is shown. + + Currently the only available *FEATURE* is :ref:`scroll-content-up `. + An error will be printed when passed an unrecognized feature. Notes ----- -For backwards compatibility most subcommands can also be specified as a long or short option. For example, rather than ``status is-login`` you can type ``status --is-login``. The flag forms are deprecated and may be removed in a future release (but not before fish 4.0). +For backwards compatibility most subcommands can also be specified as a long or short option. For example, rather than ``status is-login`` you can type ``status --is-login``. The flag forms are deprecated and may be removed in a future release. You can only specify one subcommand per invocation even if you use the flag form of the subcommand. diff --git a/doc_src/terminal-compatibility.rst b/doc_src/terminal-compatibility.rst index ec2c565da..2732c0772 100644 --- a/doc_src/terminal-compatibility.rst +++ b/doc_src/terminal-compatibility.rst @@ -198,8 +198,9 @@ Optional Commands ``\e[ Ps S`` - indn - - Scroll up the content (not the viewport) Ps lines (called ``SCROLL UP`` / ``SU`` by ECMA-48 and "scroll forward" by terminfo) - This enables the :ref:`scrollback-push ` special input function which is used by :kbd:`ctrl-l`. + - Scroll up the content (not the viewport) Ps lines (called ``SCROLL UP`` / ``SU`` by ECMA-48 and "scroll forward" by terminfo). + When fish detects support for this feature, :ref:`status test-terminal-features scroll-content-up ` will return 0, + which enables the :kbd:`ctrl-l` binding to use the :ref:`scrollback-push ` special input function. - ECMA-48 * - ``\e[= Ps u``, ``\e[? Ps u`` - n/a @@ -215,10 +216,8 @@ Optional Commands and the second parameter is the column number. Both start at 1. - This is used inside terminals that either - - - implement the OSC 133 :ref:`click_events ` feature. - - advertise the :ref:`indn ` capability via :ref:`XTGETTCAP ` + This is used by the :ref:`scrollback-push ` special input function, + and inside terminals that implement the OSC 133 :ref:`click_events ` feature. - VT100 * - ``\e[ \x20 q`` - Se diff --git a/po/de.po b/po/de.po index 630553ae4..3ee0d07e7 100644 --- a/po/de.po +++ b/po/de.po @@ -265,10 +265,6 @@ msgstr "" msgid "%ls: --set-cursor option requires --add\n" msgstr "" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "%ls: -S erfordert eine nicht leere Zeichenkette\n" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "" @@ -811,6 +807,11 @@ msgstr "" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65152,6 +65153,9 @@ msgstr "" msgid "Test if snap has yet to be given the subcommand" msgstr "" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "" diff --git a/po/en.po b/po/en.po index 501c4f6e0..f18240b50 100644 --- a/po/en.po +++ b/po/en.po @@ -263,10 +263,6 @@ msgstr "" msgid "%ls: --set-cursor option requires --add\n" msgstr "" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "" @@ -809,6 +805,11 @@ msgstr "" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65148,6 +65149,9 @@ msgstr "" msgid "Test if snap has yet to be given the subcommand" msgstr "" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "" diff --git a/po/fr.po b/po/fr.po index 45c83ce4d..18213673b 100644 --- a/po/fr.po +++ b/po/fr.po @@ -364,10 +364,6 @@ msgstr "" msgid "%ls: --set-cursor option requires --add\n" msgstr "" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "%ls : -l requiert une chaîne non-vide\n" @@ -910,6 +906,11 @@ msgstr "" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65249,6 +65250,9 @@ msgstr "" msgid "Test if snap has yet to be given the subcommand" msgstr "" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "" diff --git a/po/pl.po b/po/pl.po index 3f87f491c..403611a71 100644 --- a/po/pl.po +++ b/po/pl.po @@ -259,10 +259,6 @@ msgstr "" msgid "%ls: --set-cursor option requires --add\n" msgstr "" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "" @@ -805,6 +801,11 @@ msgstr "" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65144,6 +65145,9 @@ msgstr "" msgid "Test if snap has yet to be given the subcommand" msgstr "" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index 55e17f87d..a1e0a6236 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -264,10 +264,6 @@ msgstr "" msgid "%ls: --set-cursor option requires --add\n" msgstr "" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "" @@ -810,6 +806,11 @@ msgstr "" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65171,6 +65172,9 @@ msgstr "" msgid "Test if snap has yet to be given the subcommand" msgstr "" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "" diff --git a/po/sv.po b/po/sv.po index 97b44d439..04a7eab4a 100644 --- a/po/sv.po +++ b/po/sv.po @@ -260,10 +260,6 @@ msgstr "" msgid "%ls: --set-cursor option requires --add\n" msgstr "" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "" @@ -806,6 +802,11 @@ msgstr "" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65147,6 +65148,9 @@ msgstr "" msgid "Test if snap has yet to be given the subcommand" msgstr "" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index a04ff12b6..ce2c6323a 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -257,10 +257,6 @@ msgstr "%ls: --set-cursor 参数不能为空\n" msgid "%ls: --set-cursor option requires --add\n" msgstr "%ls: --set-cursor 选项需要 --add\n" -#, c-format -msgid "%ls: -S requires a non-empty string\n" -msgstr "%ls: -S 需要非空字符串\n" - #, c-format msgid "%ls: -l requires a non-empty string\n" msgstr "%ls: -l 需要一个非空字符串\n" @@ -803,6 +799,11 @@ msgstr "%lu\n" msgid "%s" msgstr "" +# +#, c-format +msgid "%s %s: unrecognized feature '%ls'" +msgstr "" + # #, c-format msgid "%s and %s are mutually exclusive" @@ -65147,6 +65148,9 @@ msgstr "测试Snap命令是否应该有文件作为可能的补全" msgid "Test if snap has yet to be given the subcommand" msgstr "测试是否尚未给定 snap 命令" +msgid "Test if the terminal suports the given feature" +msgstr "" + msgid "Test if there is a subcommand given" msgstr "测试是否有一个子命令" diff --git a/share/completions/status.fish b/share/completions/status.fish index 502a76398..90b6c4409 100644 --- a/share/completions/status.fish +++ b/share/completions/status.fish @@ -28,7 +28,8 @@ set -l __fish_status_all_commands \ print-stack-trace \ stack-trace \ terminal \ - test-feature + test-feature \ + test-terminal-feature # These are the recognized flags. complete -c status -s h -l help -d "Display help and exit" @@ -66,6 +67,8 @@ complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_com complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a list-files -d "List embedded files contained in the fish binary" complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a fish-path -d "Print the path to the current instance of fish" complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a terminal -d "Print name and version of the terminal fish is running in" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a test-terminal-feature -d "Test if the terminal suports the given feature" +complete -f -c status -n "__fish_seen_subcommand_from test-terminal-feature" -a 'scroll-content-up\t"Command for scrolling up terminal contents"' # The job-control command changes fish state. complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a job-control -d "Set which jobs are under job control" diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index 56bbd4755..70130349d 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -80,7 +80,9 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv alt-l __fish_list_current_token bind --preset $argv alt-o __fish_preview_current_file bind --preset $argv alt-w __fish_whatis_current_token - bind --preset $argv ctrl-l scrollback-push clear-screen + bind --preset $argv ctrl-l \ + 'status test-terminal-feature scroll-content-up && commandline -f scrollback-push' \ + clear-screen bind --preset $argv ctrl-c clear-commandline bind --preset $argv ctrl-u backward-kill-line bind --preset $argv ctrl-k kill-line diff --git a/src/builtins/status.rs b/src/builtins/status.rs index d9d04ed66..71bf5b52c 100644 --- a/src/builtins/status.rs +++ b/src/builtins/status.rs @@ -7,7 +7,7 @@ get_job_control_mode, get_login, is_interactive_session, set_job_control_mode, JobControl, }; use crate::reader::reader_in_interactive_read; -use crate::tty_handoff::xtversion; +use crate::tty_handoff::{get_scroll_content_up_capability, xtversion}; use crate::wutil::{waccess, wbasename, wdirname, wrealpath, Error}; use libc::F_OK; use nix::errno::Errno; @@ -65,6 +65,7 @@ enum StatusCmd { STATUS_GET_FILE, STATUS_LIST_FILES, STATUS_TERMINAL, + STATUS_TEST_TERMINAL_FEATURE, } str_enum!( @@ -101,6 +102,7 @@ enum StatusCmd { (STATUS_STACK_TRACE, "stack-trace"), (STATUS_TERMINAL, "terminal"), (STATUS_TEST_FEATURE, "test-feature"), + (STATUS_TEST_TERMINAL_FEATURE, "test-terminal-feature"), ); /// Values that may be returned from the test-feature option to status. @@ -539,6 +541,33 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B return Err(STATUS_CMD_ERROR); } } + c @ STATUS_TEST_TERMINAL_FEATURE => { + if args.len() != 1 { + streams.err.append(wgettext_fmt!( + BUILTIN_ERR_ARG_COUNT2, + cmd, + c.to_wstr(), + 1, + args.len() + )); + return Err(STATUS_INVALID_ARGS); + } + if args[0] != "scroll-content-up" { + streams.err.appendln(wgettext_fmt!( + "%s %s: unrecognized feature '%ls'", + cmd, + c.to_wstr(), + args[0] + )); + return Err(STATUS_INVALID_ARGS); + }; + return if get_scroll_content_up_capability() == Some(true) { + Ok(SUCCESS) + } else { + Err(STATUS_CMD_ERROR) + }; + } + ref s => { if !args.is_empty() { streams.err.append(wgettext_fmt!( @@ -733,7 +762,8 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B | STATUS_FEATURES | STATUS_TEST_FEATURE | STATUS_GET_FILE - | STATUS_LIST_FILES => { + | STATUS_LIST_FILES + | STATUS_TEST_TERMINAL_FEATURE => { unreachable!("") } } diff --git a/src/reader.rs b/src/reader.rs index 6a973e61b..6811adab4 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -147,7 +147,6 @@ tok_command, MoveWordStateMachine, MoveWordStyle, TokenType, Tokenizer, TOK_ACCEPT_UNFINISHED, TOK_SHOW_COMMENTS, }; -use crate::tty_handoff::get_scroll_content_up_capability; use crate::tty_handoff::SCROLL_CONTENT_UP_TERMINFO_CODE; use crate::tty_handoff::{ get_tty_protocols_active, initialize_tty_metadata, safe_deactivate_tty_protocols, TtyHandoff, @@ -3988,9 +3987,6 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) { self.clear_screen_and_repaint(); } rl::ScrollbackPush => { - if !get_scroll_content_up_capability().unwrap() { - return; - } let query = self.blocking_query(); let Some(query) = &*query else { drop(query); diff --git a/src/terminal.rs b/src/terminal.rs index a9b9c7536..1dfe3f418 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -6,7 +6,6 @@ use crate::screen::{is_dumb, only_grayscale}; use crate::text_face::{TextFace, TextStyling, UnderlineStyle}; use crate::threads::MainThread; -use crate::tty_handoff::get_scroll_content_up_capability; use crate::wchar::prelude::*; use crate::FLOGF; use bitflags::bitflags; @@ -410,7 +409,6 @@ fn osc_133_command_finished(out: &mut impl Output, exit_status: libc::c_int) -> } fn scroll_content_up(out: &mut impl Output, lines: usize) -> bool { - assert!(get_scroll_content_up_capability().unwrap()); write_to_output!(out, "\x1b[{}S", lines); true } diff --git a/tests/checks/status.fish b/tests/checks/status.fish index b13d7426e..d74ac3979 100644 --- a/tests/checks/status.fish +++ b/tests/checks/status.fish @@ -134,3 +134,15 @@ printf "%s\n" (test-stack-trace-copy | string replace \t '')[1..4] # CHECK: called on line {{\d+}} of file {{.*}}/status.fish # CHECK: in function 'test-stack-trace-copy' # CHECK: called on line {{\d+}} of file {{.*}}/status.fish + +status test-terminal-feature +and should have failed on missing arg +# CHECKERR: status: test-terminal-feature: expected 1 arguments; got 0 +status test-terminal-feature 1 2 +and should have failed on too many args +# CHECKERR: status: test-terminal-feature: expected 1 arguments; got 2 +status test-terminal-feature unrecognized-feature +and should have failed on unrecognized feature +# CHECKERR: status test-terminal-feature: unrecognized feature 'unrecognized-feature' +status test-terminal-feature scroll-content-up +and should have failed when running without a TTY diff --git a/tests/pexpect_helper.py b/tests/pexpect_helper.py index f7d40e492..fe9f30b9a 100644 --- a/tests/pexpect_helper.py +++ b/tests/pexpect_helper.py @@ -154,7 +154,7 @@ class SpawnedProc(object): name="fish", timeout=TIMEOUT_SECS, env=os.environ.copy(), - scroll_up_content_supported: bool = False, + scroll_content_up_supported: bool = False, **kwargs, ): """Construct from a name, timeout, and environment. @@ -184,7 +184,7 @@ class SpawnedProc(object): ) self.spawn.delaybeforesend = None self.prompt_counter = 0 - if scroll_up_content_supported: + if scroll_content_up_supported: # XTGETTCAP key = bytes.hex(b"indn") value = bytes.hex(b"dont-care") diff --git a/tests/pexpects/scrollback.py b/tests/pexpects/scrollback.py index 898494533..48cb02b06 100644 --- a/tests/pexpects/scrollback.py +++ b/tests/pexpects/scrollback.py @@ -5,7 +5,7 @@ import os env = os.environ.copy() env["TERM"] = "not-dumb" -sp = SpawnedProc(env=env, scroll_up_content_supported=True) +sp = SpawnedProc(env=env, scroll_content_up_supported=True) sendline, expect_prompt = sp.sendline, sp.expect_prompt expect_prompt() @@ -14,5 +14,9 @@ expect_prompt() sp.send(control("g")) sp.send_cursor_position_report(y=10, x=5) sp.send_primary_device_attribute() - sp.expect_str("\x1b[9S\x1b[9A") + +sp.send(control("l")) +sp.send_cursor_position_report(y=15, x=5) +sp.send_primary_device_attribute() +sp.expect_str("\x1b[14S\x1b[14A")