mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-28 01:11:15 -03:00
Compare commits
3 Commits
4.3.2
...
curly-unde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba2ad33a81 | ||
|
|
3453565a41 | ||
|
|
cb2b1c6621 |
@@ -69,7 +69,12 @@ Improved terminal support
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
- Support for curly underlines in `fish_color_*` variables and :doc:`set_color <cmds/set_color>` (:issue:`10957`).
|
- Support for curly underlines in `fish_color_*` variables and :doc:`set_color <cmds/set_color>` (:issue:`10957`).
|
||||||
- Underlines can now be colored independent of text (:issue:`7619`).
|
- Underlines can now be colored independent of text (:issue:`7619`).
|
||||||
|
- Errors are now highlighted with curly underlines in the default themes.
|
||||||
|
For compatibility with terminals that interpret the curly-underline escape sequence in an unexpected way,
|
||||||
|
the default themes enable this only if the terminal advertises support for the ``Su`` capability via XTGETTCAP.
|
||||||
- New documentation page `Terminal Compatibility <terminal-compatibility.html>`_ (also accessible via ``man fish-terminal-compatibility``) lists required and optional terminal control sequences used by fish.
|
- New documentation page `Terminal Compatibility <terminal-compatibility.html>`_ (also accessible via ``man fish-terminal-compatibility``) lists required and optional terminal control sequences used by fish.
|
||||||
|
- :doc:`status <cmds/status>` learned the ``xtgettcap`` subcommand, to query terminfo capabilities via XTGETTCAP commands.
|
||||||
|
- :doc:`status <cmds/status>` learned the ``xtversion`` subcommand, to show the terminal's name and version (as reported by via the XTVERSION command).
|
||||||
|
|
||||||
Other improvements
|
Other improvements
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Synopsis
|
|||||||
|
|
||||||
fish_config [browse]
|
fish_config [browse]
|
||||||
fish_config prompt (choose | list | save | show)
|
fish_config prompt (choose | list | save | show)
|
||||||
fish_config theme (choose | demo | dump | list | save | show)
|
fish_config theme (choose | demo | dump | list | save | show | update)
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
@@ -40,6 +40,9 @@ Available subcommands for the ``theme`` command:
|
|||||||
- ``list`` lists the names of the available sample themes.
|
- ``list`` lists the names of the available sample themes.
|
||||||
- ``save`` saves the given theme to :ref:`universal variables <variables-universal>`.
|
- ``save`` saves the given theme to :ref:`universal variables <variables-universal>`.
|
||||||
- ``show`` shows what the given sample theme (or all) would look like.
|
- ``show`` shows what the given sample theme (or all) would look like.
|
||||||
|
- ``update`` updates any ``fish_color_*`` and ``fish_pager_color_*`` variables whose value contains
|
||||||
|
"--track=THEME". They are set to the latest version of that theme, and the tracking
|
||||||
|
option is preserved. Note that ``fish_config theme update`` is run at fish startup.
|
||||||
|
|
||||||
The **-h** or **--help** option displays help about using this command.
|
The **-h** or **--help** option displays help about using this command.
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ The following options are available:
|
|||||||
**-u** or **--underline**, or **-uSTYLE** or **--underline=STYLE**
|
**-u** or **--underline**, or **-uSTYLE** or **--underline=STYLE**
|
||||||
Set the underline mode; supported styles are **single** (default) and **curly**.
|
Set the underline mode; supported styles are **single** (default) and **curly**.
|
||||||
|
|
||||||
|
**--track=THEME**
|
||||||
|
Ignored. Included by default in universally-scoped color variables to tell fish to update
|
||||||
|
them as the associated theme changes.
|
||||||
|
This flag can be set for all variables when loading a theme with the `--track`` option, that is
|
||||||
|
:doc:`fish_config theme save THEME --track <fish_config>`.
|
||||||
|
|
||||||
**-h** or **--help**
|
**-h** or **--help**
|
||||||
Displays help about using this command.
|
Displays help about using this command.
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ Synopsis
|
|||||||
status buildinfo
|
status buildinfo
|
||||||
status get-file FILE
|
status get-file FILE
|
||||||
status list-files [PATH]
|
status list-files [PATH]
|
||||||
|
status xtgettcap TERMINFO-CAPABILITY
|
||||||
|
status xtversion
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
@@ -117,6 +119,13 @@ 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.
|
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.
|
Returns 0 if something was printed, 1 otherwise.
|
||||||
|
|
||||||
|
**xtgettcap** *TERMINFO-CAPABILITY*
|
||||||
|
Query the terminal for a terminfo capability via XTGETTCAP.
|
||||||
|
Returns 0 if the capability is present and 1 otherwise.
|
||||||
|
|
||||||
|
**xtversion**
|
||||||
|
Show the terminal name and version (XTVERSION).
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,9 @@ Optional Commands
|
|||||||
- Su
|
- Su
|
||||||
- Reset underline color to the default (follow the foreground color).
|
- Reset underline color to the default (follow the foreground color).
|
||||||
- kitty
|
- kitty
|
||||||
* - ``\e[ Ps S``
|
* - .. _indn:
|
||||||
|
|
||||||
|
``\e[ Ps S``
|
||||||
- indn
|
- indn
|
||||||
- Scroll forward Ps lines.
|
- Scroll forward Ps lines.
|
||||||
-
|
-
|
||||||
@@ -269,6 +271,12 @@ Optional Commands
|
|||||||
- FinalTerm
|
- FinalTerm
|
||||||
* - ``\eP+q Pt \e\\``
|
* - ``\eP+q Pt \e\\``
|
||||||
-
|
-
|
||||||
- Request terminfo capability (XTGETTCAP). The parameter is the capability's hex-encoded terminfo code.
|
- Request terminfo capability (XTGETTCAP).
|
||||||
Specifically, fish asks for the ``indn`` string capability. At the time of writing string capabilities are supported by kitty and foot.
|
The parameter is the capability's hex-encoded terminfo code.
|
||||||
- XTerm, kitty, foot
|
To advertise a capability, the response must of the form
|
||||||
|
``\eP1+q Pt \e\\`` or ``\eP1+q Pt = Pt \e\\``.
|
||||||
|
In either variant the first parameter must be the hex-encoded terminfo code.
|
||||||
|
In the second variant, fish ignores the part after the equals sign.
|
||||||
|
|
||||||
|
At startup, fish checks for the presence of the `indn <#indn>`_ string capability.
|
||||||
|
- XTerm, kitty, foot, wezterm, contour
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
complete fish_config -f
|
complete fish_config -f
|
||||||
set -l prompt_commands choose save show list
|
set -l prompt_commands choose save show list
|
||||||
set -l theme_commands choose demo dump save show list
|
set -l theme_commands choose demo dump save show list update
|
||||||
complete fish_config -n __fish_use_subcommand -a prompt -d 'View and pick from the sample prompts'
|
complete fish_config -n __fish_use_subcommand -a prompt -d 'View and pick from the sample prompts'
|
||||||
complete fish_config -n "__fish_seen_subcommand_from prompt; and not __fish_seen_subcommand_from $prompt_commands" \
|
complete fish_config -n "__fish_seen_subcommand_from prompt; and not __fish_seen_subcommand_from $prompt_commands" \
|
||||||
-a choose -d 'View and pick from the sample prompts'
|
-a choose -d 'View and pick from the sample prompts'
|
||||||
@@ -16,6 +16,7 @@ complete fish_config -n __fish_use_subcommand -a browse -d 'Open the web-based U
|
|||||||
|
|
||||||
complete fish_config -n __fish_use_subcommand -a theme -d 'View and pick from the sample themes'
|
complete fish_config -n __fish_use_subcommand -a theme -d 'View and pick from the sample themes'
|
||||||
complete fish_config -n '__fish_seen_subcommand_from theme; and __fish_seen_subcommand_from choose save show' -a '(fish_config theme list)'
|
complete fish_config -n '__fish_seen_subcommand_from theme; and __fish_seen_subcommand_from choose save show' -a '(fish_config theme list)'
|
||||||
|
complete fish_config -n '__fish_seen_subcommand_from theme; and __fish_seen_subcommand_from save' -l track -d 'Add --track to color variables to apply future theme updates'
|
||||||
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
||||||
-a choose -d 'View and pick from the sample themes'
|
-a choose -d 'View and pick from the sample themes'
|
||||||
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
||||||
@@ -28,3 +29,5 @@ complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_
|
|||||||
-a demo -d 'Show example in the current theme'
|
-a demo -d 'Show example in the current theme'
|
||||||
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
||||||
-a dump -d 'Print the current theme in .theme format'
|
-a dump -d 'Print the current theme in .theme format'
|
||||||
|
complete fish_config -n "__fish_seen_subcommand_from theme; and not __fish_seen_subcommand_from $theme_commands" \
|
||||||
|
-a update -d "Update universal colors that have the tracking flag set"
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ complete -c set_color -s r -l reverse -d 'Reverse color text'
|
|||||||
complete -c set_color -s u -l underline -d 'Underline style' -a 'single curly'
|
complete -c set_color -s u -l underline -d 'Underline style' -a 'single curly'
|
||||||
complete -c set_color -s h -l help -d 'Display help and exit'
|
complete -c set_color -s h -l help -d 'Display help and exit'
|
||||||
complete -c set_color -s c -l print-colors -d 'Print a list of all accepted color names'
|
complete -c set_color -s c -l print-colors -d 'Print a list of all accepted color names'
|
||||||
|
complete -c set_color -l track -xa '(fish_config theme list)' -d 'Ignored. Used in color variables to follow theme changes'
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ set -l __fish_status_all_commands \
|
|||||||
list-files \
|
list-files \
|
||||||
print-stack-trace \
|
print-stack-trace \
|
||||||
stack-trace \
|
stack-trace \
|
||||||
test-feature
|
test-feature \
|
||||||
|
xtgettcap \
|
||||||
|
xtversion
|
||||||
|
|
||||||
# These are the recognized flags.
|
# These are the recognized flags.
|
||||||
complete -c status -s h -l help -d "Display help and exit"
|
complete -c status -s h -l help -d "Display help and exit"
|
||||||
@@ -64,6 +66,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 get-file -d "Print an embedded file from the fish binary"
|
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a get-file -d "Print an embedded file from the fish binary"
|
||||||
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 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 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 xtgettcap -d "Query the terminal for a terminfo capability"
|
||||||
|
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a xtversion -d "Show terminal name and version"
|
||||||
|
|
||||||
# The job-control command changes fish state.
|
# 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"
|
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"
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ if status is-interactive
|
|||||||
# Commands to run in interactive sessions can go here
|
# Commands to run in interactive sessions can go here
|
||||||
end" >$__fish_config_dir/config.fish
|
end" >$__fish_config_dir/config.fish
|
||||||
|
|
||||||
echo yes | fish_config theme save "fish default"
|
fish_config theme save "fish default" --yes --track
|
||||||
set -Ue fish_color_keyword fish_color_option
|
set -Ue fish_color_keyword fish_color_option
|
||||||
end
|
end
|
||||||
if test $__fish_initialized -lt 3800 && test "$fish_color_search_match[1]" = bryellow
|
if test $__fish_initialized -lt 3800 && test "$fish_color_search_match[1]" = bryellow
|
||||||
set --universal fish_color_search_match[1] white
|
set --universal fish_color_search_match[1] white
|
||||||
end
|
end
|
||||||
|
fish_config theme update
|
||||||
|
|
||||||
#
|
#
|
||||||
# Generate man page completions if not present.
|
# Generate man page completions if not present.
|
||||||
|
|||||||
3
share/functions/__fish_in_gnu_screen.fish
Normal file
3
share/functions/__fish_in_gnu_screen.fish
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
function __fish_in_gnu_screen
|
||||||
|
test -n "$STY" || contains -- $TERM screen screen-256color
|
||||||
|
end
|
||||||
9
share/functions/__fish_in_terminal_multiplexer.fish
Normal file
9
share/functions/__fish_in_terminal_multiplexer.fish
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
function __fish_in_terminal_multiplexer
|
||||||
|
set -l terminal_name "(status xtversion | string match -r '^\S*')"
|
||||||
|
string match -q -- tmux $terminal_name ||
|
||||||
|
__fish_in_gnu_screen ||
|
||||||
|
# Emacs does probably not support multiplexing between multiple parent
|
||||||
|
# terminals, but it is affected by the same issues. Same for Vim's
|
||||||
|
# :terminal TODO detect that before they implement XTGETTCAP.
|
||||||
|
contains -- $TERM dvtm-256color eterm eterm-color
|
||||||
|
end
|
||||||
@@ -77,7 +77,11 @@ 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-l __fish_list_current_token
|
||||||
bind --preset $argv alt-o __fish_preview_current_file
|
bind --preset $argv alt-o __fish_preview_current_file
|
||||||
bind --preset $argv alt-w __fish_whatis_current_token
|
bind --preset $argv alt-w __fish_whatis_current_token
|
||||||
bind --preset $argv ctrl-l scrollback-push clear-screen
|
bind --preset $argv ctrl-l (
|
||||||
|
if not __fish_in_gnu_screen && and __fish_xtgettcap indn
|
||||||
|
echo scrollback-push
|
||||||
|
end
|
||||||
|
) clear-screen
|
||||||
bind --preset $argv ctrl-c clear-commandline
|
bind --preset $argv ctrl-c clear-commandline
|
||||||
bind --preset $argv ctrl-u backward-kill-line
|
bind --preset $argv ctrl-u backward-kill-line
|
||||||
bind --preset $argv ctrl-k kill-line
|
bind --preset $argv ctrl-k kill-line
|
||||||
|
|||||||
12
share/functions/__fish_xtgettcap.fish
Normal file
12
share/functions/__fish_xtgettcap.fish
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
function __fish_xtgettcap --argument-names terminfo_code
|
||||||
|
if test $FISH_UNIT_TESTS_RUNNING = 1
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
set -l varname __fish_tcap_$terminfo_code
|
||||||
|
if not set -q $varname\[0\]
|
||||||
|
set --global $varname (
|
||||||
|
status xtgettcap $terminfo_code && echo true || echo false
|
||||||
|
)
|
||||||
|
end
|
||||||
|
test "$$varname" = true
|
||||||
|
end
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
function fish_config --description "Launch fish's web based configuration"
|
# Variables a theme is allowed to set
|
||||||
argparse h/help -- $argv
|
set -l theme_var_filter '^fish_(?:pager_)?color.*$'
|
||||||
|
|
||||||
|
function fish_config --description "Launch fish's web based configuration" \
|
||||||
|
--inherit-variable theme_var_filter
|
||||||
|
|
||||||
|
set -l _flag_track
|
||||||
|
argparse h/help track yes -- $argv
|
||||||
or return
|
or return
|
||||||
|
|
||||||
if set -q _flag_help
|
if set -q _flag_help
|
||||||
@@ -10,6 +16,15 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
set -l cmd $argv[1]
|
set -l cmd $argv[1]
|
||||||
set -e argv[1]
|
set -e argv[1]
|
||||||
|
|
||||||
|
if set -q _flag_track[1] && not { test "$cmd" = theme && test "$argv[1]" = save }
|
||||||
|
echo >&2 fish_config: --track: unknown option
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if set -q _flag_yes[1] && not { contains "$cmd" prompt theme && test "$argv[1]" = save }
|
||||||
|
echo >&2 fish_config: --yes: unknown option
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
set -q cmd[1]
|
set -q cmd[1]
|
||||||
or set cmd browse
|
or set cmd browse
|
||||||
|
|
||||||
@@ -65,9 +80,6 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
# Variables a theme is allowed to set
|
|
||||||
set -l theme_var_filter '^fish_(?:pager_)?color.*$'
|
|
||||||
|
|
||||||
switch $cmd
|
switch $cmd
|
||||||
case prompt
|
case prompt
|
||||||
# prompt - for prompt switching
|
# prompt - for prompt switching
|
||||||
@@ -144,8 +156,8 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
functions --erase fish_right_prompt
|
functions --erase fish_right_prompt
|
||||||
end
|
end
|
||||||
case save
|
case save
|
||||||
read -P"Overwrite prompt? [y/N]" -l yesno
|
if set -q _flag_yes[1] ||
|
||||||
if string match -riq 'y(es)?' -- $yesno
|
{ read -P"Overwrite prompt? [y/N]" -l yesno; string match -riq 'y(es)?' -- $yesno }
|
||||||
echo Overwriting
|
echo Overwriting
|
||||||
# Skip the cp if unnecessary,
|
# Skip the cp if unnecessary,
|
||||||
# or we'd throw an error on a stock fish.
|
# or we'd throw an error on a stock fish.
|
||||||
@@ -274,8 +286,10 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
set -l have_colors
|
set -l have_colors
|
||||||
|
|
||||||
if contains -- $cmd save
|
if contains -- $cmd save
|
||||||
read -P"Overwrite your current theme? [y/N] " -l yesno
|
if not set -q _flag_yes &&
|
||||||
if not string match -riq 'y(es)?' -- $yesno
|
{ read -P"Overwrite your current theme? [y/N] " -l yesno
|
||||||
|
not string match -riq 'y(es)?' -- $yesno
|
||||||
|
}
|
||||||
echo Not overwriting >&2
|
echo Not overwriting >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
@@ -292,34 +306,7 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
# If we are choosing a theme or saving from a named theme, load the theme now.
|
# If we are choosing a theme or saving from a named theme, load the theme now.
|
||||||
# Otherwise, we'll persist the currently loaded/themed variables (in case of `theme save`).
|
# Otherwise, we'll persist the currently loaded/themed variables (in case of `theme save`).
|
||||||
if set -q argv[1]
|
if set -q argv[1]
|
||||||
set -l files $dirs/$argv[1].theme
|
__fish_config_theme_get $argv[1] | while read -lat toks
|
||||||
set -l file
|
|
||||||
|
|
||||||
for f in $files
|
|
||||||
if test -e "$f"
|
|
||||||
set file $f
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not set -q file[1]
|
|
||||||
if status list-files tools/web_config/themes/$argv[1].theme &>/dev/null
|
|
||||||
set file tools/web_config/themes/$argv[1].theme
|
|
||||||
else
|
|
||||||
echo "No such theme: $argv[1]" >&2
|
|
||||||
echo "Searched directories: $dirs" >&2
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
set -l content
|
|
||||||
if string match -qr '^tools/' -- $file
|
|
||||||
set content (status get-file $file)
|
|
||||||
else
|
|
||||||
read -z content < $file
|
|
||||||
end
|
|
||||||
|
|
||||||
printf %s\n $content | while read -lat toks
|
|
||||||
# The whitelist allows only color variables.
|
# The whitelist allows only color variables.
|
||||||
# Not the specific list, but something named *like* a color variable.
|
# Not the specific list, but something named *like* a color variable.
|
||||||
# This also takes care of empty lines and comment lines.
|
# This also takes care of empty lines and comment lines.
|
||||||
@@ -331,7 +318,7 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
if test x"$scope" = x-U; and set -qg $toks[1]
|
if test x"$scope" = x-U; and set -qg $toks[1]
|
||||||
set -eg $toks[1]
|
set -eg $toks[1]
|
||||||
end
|
end
|
||||||
set $scope $toks
|
set $scope $toks $_flag_track=$argv[1]
|
||||||
set -a have_colors $toks[1]
|
set -a have_colors $toks[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -343,7 +330,7 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
# Erase conflicting global variables so we don't get a warning and
|
# Erase conflicting global variables so we don't get a warning and
|
||||||
# so changes are observed immediately.
|
# so changes are observed immediately.
|
||||||
set -eg $c
|
set -eg $c
|
||||||
set $scope $c
|
set $scope $c $_flag_track=$argv[1]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# We're persisting whatever current colors are loaded (maybe in the global scope)
|
# We're persisting whatever current colors are loaded (maybe in the global scope)
|
||||||
@@ -365,6 +352,8 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
# If we've made it this far, we've either found a theme file or persisted the current
|
# If we've made it this far, we've either found a theme file or persisted the current
|
||||||
# state (if any). In all cases we haven't failed, so return 0.
|
# state (if any). In all cases we haven't failed, so return 0.
|
||||||
return 0
|
return 0
|
||||||
|
case update
|
||||||
|
__fish_config_theme_update $argv
|
||||||
case dump
|
case dump
|
||||||
# Write the current theme in .theme format, to stdout.
|
# Write the current theme in .theme format, to stdout.
|
||||||
set -L | string match -r $theme_var_filter
|
set -L | string match -r $theme_var_filter
|
||||||
@@ -374,3 +363,100 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function __fish_config_theme_get
|
||||||
|
set -l dirs $__fish_config_dir/themes $__fish_data_dir/tools/web_config/themes
|
||||||
|
set -l files $dirs/$argv[1].theme
|
||||||
|
set -l file
|
||||||
|
set -l is_default_theme false
|
||||||
|
|
||||||
|
for f in $files
|
||||||
|
if test -e "$f"
|
||||||
|
set file $f
|
||||||
|
if test $f = $__fish_data_dir/tools/web_config/themes/$argv[1].theme
|
||||||
|
set is_default_theme true
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not set -q file[1]
|
||||||
|
if status list-files tools/web_config/themes/$argv[1].theme &>/dev/null
|
||||||
|
set file tools/web_config/themes/$argv[1].theme
|
||||||
|
set is_default_theme true
|
||||||
|
else
|
||||||
|
echo "No such theme: $argv[1]" >&2
|
||||||
|
echo "Searched directories: $dirs" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l content (
|
||||||
|
if string match -qr '^tools/' -- $file
|
||||||
|
status get-file $file
|
||||||
|
else
|
||||||
|
string join \n <$file
|
||||||
|
end
|
||||||
|
)
|
||||||
|
if $is_default_theme && not __fish_in_gnu_screen && not __fish_xtgettcap Su && not __fish_in_terminal_multiplexer
|
||||||
|
set content (string replace -- --underline=curly "" $content)
|
||||||
|
end
|
||||||
|
|
||||||
|
string join \n $content
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fish_config_show_tracked_color_vars
|
||||||
|
set -l color_var $argv[1]
|
||||||
|
set -l _flag_track
|
||||||
|
argparse --ignore-unknown track= -- _set_color $argv[2..]
|
||||||
|
or return $status
|
||||||
|
if not set -q _flag_track[1]
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if set -q _flag_track[2]
|
||||||
|
echo >&2 "fish_config: $color_var: --track option can only be specified once"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
if test (printf %s $_flag_track | count) -ne 0
|
||||||
|
echo >&2 "fish_config: $color_var: error: tracking theme name must not contain newlines"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
printf %s\n $color_var $_flag_track
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fish_config_theme_update --inherit-variable theme_var_filter
|
||||||
|
if set -q argv[1]
|
||||||
|
echo "fish_config: too many arguments" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
set -l themes
|
||||||
|
|
||||||
|
set -l tracking_variables (
|
||||||
|
set --universal --long |
|
||||||
|
string match -r '^fish_(?:pager_)?color.*$' |
|
||||||
|
string replace -r '.*' '__fish_config_show_tracked_color_vars $0' |
|
||||||
|
source
|
||||||
|
)
|
||||||
|
or return $status
|
||||||
|
string join \n $tracking_variables |
|
||||||
|
while read --line _colorvar theme
|
||||||
|
if not contains -- $theme $themes
|
||||||
|
set -a themes $theme
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for theme in $themes
|
||||||
|
set -l colorvars
|
||||||
|
string join \n $tracking_variables |
|
||||||
|
while read --line color_var t
|
||||||
|
if test $t = $theme
|
||||||
|
set -a colorvars $color_var
|
||||||
|
end
|
||||||
|
end
|
||||||
|
set -l theme_escaped (string escape -- $theme)
|
||||||
|
__fish_config_theme_get $theme |
|
||||||
|
string match -r -- "^(?:$(string join '|' $colorvars))\b .*" |
|
||||||
|
string replace -r '.*' "set -U \$0 --track=$theme_escaped" |
|
||||||
|
source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment f7ca88
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end ba8baf
|
fish_color_end ba8baf
|
||||||
fish_color_error ab4642
|
fish_color_error ab4642 --underline=curly
|
||||||
fish_color_escape 86c1b9
|
fish_color_escape 86c1b9
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment f7ca88
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end ba8baf
|
fish_color_end ba8baf
|
||||||
fish_color_error ab4642
|
fish_color_error ab4642 --underline=curly
|
||||||
fish_color_escape 86c1b9
|
fish_color_escape 86c1b9
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment ffcc66
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end cc99cc
|
fish_color_end cc99cc
|
||||||
fish_color_error f2777a
|
fish_color_error f2777a --underline=curly
|
||||||
fish_color_escape 66cccc
|
fish_color_escape 66cccc
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment FF9640
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end FFB273
|
fish_color_end FFB273
|
||||||
fish_color_error FF7400
|
fish_color_error FF7400 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ fish_color_comment 6272a4
|
|||||||
fish_color_cwd 50fa7b
|
fish_color_cwd 50fa7b
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end ffb86c
|
fish_color_end ffb86c
|
||||||
fish_color_error ff5555
|
fish_color_error ff5555 --underline=curly
|
||||||
fish_color_escape ff79c6
|
fish_color_escape ff79c6
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host bd93f9
|
fish_color_host bd93f9
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment FFE100
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end 8D003B
|
fish_color_end 8D003B
|
||||||
fish_color_error EC3B86
|
fish_color_error EC3B86 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment B0B0B0
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end 969696
|
fish_color_end 969696
|
||||||
fish_color_error FFA779
|
fish_color_error FFA779 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ fish_color_command FF9400
|
|||||||
fish_color_quote BF9C30
|
fish_color_quote BF9C30
|
||||||
fish_color_redirection BF5B30
|
fish_color_redirection BF5B30
|
||||||
fish_color_end FF4C00
|
fish_color_end FF4C00
|
||||||
fish_color_error FFDD73
|
fish_color_error FFDD73 --underline=curly
|
||||||
fish_color_param FFC000
|
fish_color_param FFC000
|
||||||
fish_color_comment A63100
|
fish_color_comment A63100
|
||||||
fish_color_selection white --background=brblack --bold
|
fish_color_selection white --background=brblack --bold
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment 4e4e4e
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end 767676
|
fish_color_end 767676
|
||||||
fish_color_error b2b2b2
|
fish_color_error b2b2b2 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ fish_color_command ffffff
|
|||||||
fish_color_quote a8a8a8
|
fish_color_quote a8a8a8
|
||||||
fish_color_redirection 808080
|
fish_color_redirection 808080
|
||||||
fish_color_end 949494
|
fish_color_end 949494
|
||||||
fish_color_error 585858
|
fish_color_error 585858 --underline=curly
|
||||||
fish_color_param d7d7d7
|
fish_color_param d7d7d7
|
||||||
fish_color_comment bcbcbc
|
fish_color_comment bcbcbc
|
||||||
fish_color_selection white --background=brblack --bold
|
fish_color_selection white --background=brblack --bold
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment
|
|||||||
fish_color_cwd normal
|
fish_color_cwd normal
|
||||||
fish_color_cwd_root normal
|
fish_color_cwd_root normal
|
||||||
fish_color_end
|
fish_color_end
|
||||||
fish_color_error
|
fish_color_error --underline=curly
|
||||||
fish_color_escape
|
fish_color_escape
|
||||||
fish_color_history_current
|
fish_color_history_current
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ fish_color_comment 4c566a --italics
|
|||||||
fish_color_cwd 5e81ac
|
fish_color_cwd 5e81ac
|
||||||
fish_color_cwd_root bf616a
|
fish_color_cwd_root bf616a
|
||||||
fish_color_end 81a1c1
|
fish_color_end 81a1c1
|
||||||
fish_color_error bf616a
|
fish_color_error bf616a --underline=curly
|
||||||
fish_color_escape ebcb8b
|
fish_color_escape ebcb8b
|
||||||
fish_color_history_current e5e9f0 --bold
|
fish_color_history_current e5e9f0 --bold
|
||||||
fish_color_host a3be8c
|
fish_color_host a3be8c
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment 30BE30
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end FF7B7B
|
fish_color_end FF7B7B
|
||||||
fish_color_error A40000
|
fish_color_error A40000 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment 5C9900
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end 8EEB00
|
fish_color_end 8EEB00
|
||||||
fish_color_error 60B9CE
|
fish_color_error 60B9CE --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ fish_color_command 164CC9
|
|||||||
fish_color_quote 4C3499
|
fish_color_quote 4C3499
|
||||||
fish_color_redirection 248E8E
|
fish_color_redirection 248E8E
|
||||||
fish_color_end 02BDBD
|
fish_color_end 02BDBD
|
||||||
fish_color_error 9177E5
|
fish_color_error 9177E5 --underline=curly
|
||||||
fish_color_param 4319CC
|
fish_color_param 4319CC
|
||||||
fish_color_comment 007B7B
|
fish_color_comment 007B7B
|
||||||
fish_color_selection white --background=brblack --bold
|
fish_color_selection white --background=brblack --bold
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment 586e75
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end 268bd2
|
fish_color_end 268bd2
|
||||||
fish_color_error dc322f
|
fish_color_error dc322f --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ fish_color_command 586e75
|
|||||||
fish_color_quote 839496
|
fish_color_quote 839496
|
||||||
fish_color_redirection 6c71c4
|
fish_color_redirection 6c71c4
|
||||||
fish_color_end 268bd2
|
fish_color_end 268bd2
|
||||||
fish_color_error dc322f
|
fish_color_error dc322f --underline=curly
|
||||||
fish_color_param 657b83
|
fish_color_param 657b83
|
||||||
fish_color_comment 93a1a1
|
fish_color_comment 93a1a1
|
||||||
fish_color_selection white --background=brblack --bold
|
fish_color_selection white --background=brblack --bold
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment e7c547
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end c397d8
|
fish_color_end c397d8
|
||||||
fish_color_error d54e53
|
fish_color_error d54e53 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ fish_color_command b294bb
|
|||||||
fish_color_quote b5bd68
|
fish_color_quote b5bd68
|
||||||
fish_color_redirection 8abeb7
|
fish_color_redirection 8abeb7
|
||||||
fish_color_end b294bb
|
fish_color_end b294bb
|
||||||
fish_color_error cc6666
|
fish_color_error cc6666 --underline=curly
|
||||||
fish_color_param 81a2be
|
fish_color_param 81a2be
|
||||||
fish_color_comment f0c674
|
fish_color_comment f0c674
|
||||||
fish_color_selection white --background=brblack --bold
|
fish_color_selection white --background=brblack --bold
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment eab700
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end 8959a8
|
fish_color_end 8959a8
|
||||||
fish_color_error c82829
|
fish_color_error c82829 --underline=curly
|
||||||
fish_color_escape 00a6b2
|
fish_color_escape 00a6b2
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ fish_color_command 39BAE6
|
|||||||
fish_color_quote C2D94C
|
fish_color_quote C2D94C
|
||||||
fish_color_redirection FFEE99
|
fish_color_redirection FFEE99
|
||||||
fish_color_end F29668
|
fish_color_end F29668
|
||||||
fish_color_error FF3333
|
fish_color_error FF3333 --underline=curly
|
||||||
fish_color_param B3B1AD
|
fish_color_param B3B1AD
|
||||||
fish_color_comment 626A73
|
fish_color_comment 626A73
|
||||||
fish_color_selection --background=E6B450 --bold
|
fish_color_selection --background=E6B450 --bold
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment ABB0B6
|
|||||||
fish_color_cwd 399EE6
|
fish_color_cwd 399EE6
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end ED9366
|
fish_color_end ED9366
|
||||||
fish_color_error F51818
|
fish_color_error F51818 --underline=curly
|
||||||
fish_color_escape 4CBF99
|
fish_color_escape 4CBF99
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fish_color_comment 5C6773
|
|||||||
fish_color_cwd 73D0FF
|
fish_color_cwd 73D0FF
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end F29E74
|
fish_color_end F29E74
|
||||||
fish_color_error FF3333
|
fish_color_error FF3333 --underline=curly
|
||||||
fish_color_escape 95E6CB
|
fish_color_escape 95E6CB
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fish_color_comment '888' '--italics'
|
|||||||
fish_color_cwd 0A0
|
fish_color_cwd 0A0
|
||||||
fish_color_cwd_root A00
|
fish_color_cwd_root A00
|
||||||
fish_color_end 009900
|
fish_color_end 009900
|
||||||
fish_color_error F22
|
fish_color_error F22 --underline=curly
|
||||||
fish_color_escape 0AA
|
fish_color_escape 0AA
|
||||||
fish_color_history_current 0AA
|
fish_color_history_current 0AA
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ fish_color_comment red
|
|||||||
fish_color_cwd green
|
fish_color_cwd green
|
||||||
fish_color_cwd_root red
|
fish_color_cwd_root red
|
||||||
fish_color_end green
|
fish_color_end green
|
||||||
fish_color_error brred
|
fish_color_error brred --underline=curly
|
||||||
fish_color_escape brcyan
|
fish_color_escape brcyan
|
||||||
fish_color_history_current --bold
|
fish_color_history_current --bold
|
||||||
fish_color_host normal
|
fish_color_host normal
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ fn setup_and_process_keys(
|
|||||||
unsafe { libc::tcsetattr(0, TCSANOW, &*shell_modes()) };
|
unsafe { libc::tcsetattr(0, TCSANOW, &*shell_modes()) };
|
||||||
terminal_protocol_hacks();
|
terminal_protocol_hacks();
|
||||||
let blocking_query: OnceCell<RefCell<Option<TerminalQuery>>> = OnceCell::new();
|
let blocking_query: OnceCell<RefCell<Option<TerminalQuery>>> = OnceCell::new();
|
||||||
initial_query(&blocking_query, streams.out, None);
|
initial_query(&blocking_query, streams.out);
|
||||||
|
|
||||||
if continuous_mode {
|
if continuous_mode {
|
||||||
streams.err.append(L!("\n"));
|
streams.err.append(L!("\n"));
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
use crate::wutil::encoding::zero_mbstate;
|
use crate::wutil::encoding::zero_mbstate;
|
||||||
use crate::wutil::perror;
|
use crate::wutil::perror;
|
||||||
use libc::SEEK_CUR;
|
use libc::SEEK_CUR;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::os::fd::RawFd;
|
use std::os::fd::RawFd;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
@@ -219,12 +220,14 @@ fn read_interactive(
|
|||||||
conf.complete_ok = shell;
|
conf.complete_ok = shell;
|
||||||
conf.highlight_ok = shell;
|
conf.highlight_ok = shell;
|
||||||
conf.syntax_check_ok = shell;
|
conf.syntax_check_ok = shell;
|
||||||
|
conf.prompt_ok = true;
|
||||||
|
|
||||||
// No autosuggestions or abbreviations in builtin_read.
|
// No autosuggestions or abbreviations in builtin_read.
|
||||||
conf.autosuggest_ok = false;
|
conf.autosuggest_ok = false;
|
||||||
conf.expand_abbrev_ok = false;
|
conf.expand_abbrev_ok = false;
|
||||||
|
|
||||||
conf.exit_on_interrupt = true;
|
conf.exit_on_interrupt = true;
|
||||||
|
conf.in_builtin_read = true;
|
||||||
conf.in_silent_mode = silent;
|
conf.in_silent_mode = silent;
|
||||||
|
|
||||||
conf.left_prompt_cmd = prompt.to_owned();
|
conf.left_prompt_cmd = prompt.to_owned();
|
||||||
@@ -244,7 +247,7 @@ fn read_interactive(
|
|||||||
|
|
||||||
let mline = {
|
let mline = {
|
||||||
let _interactive = parser.push_scope(|s| s.is_interactive = true);
|
let _interactive = parser.push_scope(|s| s.is_interactive = true);
|
||||||
reader_readline(parser, nchars)
|
reader_readline(parser, NonZeroUsize::try_from(nchars).ok())
|
||||||
};
|
};
|
||||||
terminal_protocols_disable_ifn();
|
terminal_protocols_disable_ifn();
|
||||||
if let Some(line) = mline {
|
if let Some(line) = mline {
|
||||||
|
|||||||
@@ -86,6 +86,13 @@ pub fn set_color(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -
|
|||||||
// In future we change both to actually print an error.
|
// In future we change both to actually print an error.
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
Err(MultipleTracking) => {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%ls: --track option can only be specified once\n",
|
||||||
|
argv[0]
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
}
|
||||||
Err(UnknownColor(arg)) => {
|
Err(UnknownColor(arg)) => {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
@@ -124,7 +131,7 @@ pub fn set_color(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -
|
|||||||
specified_face.fg.unwrap_or(Color::None),
|
specified_face.fg.unwrap_or(Color::None),
|
||||||
specified_face.bg.unwrap_or(Color::None),
|
specified_face.bg.unwrap_or(Color::None),
|
||||||
specified_face.underline_color.unwrap_or(Color::None),
|
specified_face.underline_color.unwrap_or(Color::None),
|
||||||
specified_face.style,
|
specified_face.style.unwrap_or_default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
if specified_face.fg.is_some() && outp.contents().is_empty() {
|
if specified_face.fg.is_some() && outp.contents().is_empty() {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::os::unix::prelude::*;
|
|
||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::common::{get_executable_path, str2wcstring, PROGRAM_NAME};
|
use crate::common::{get_executable_path, str2wcstring, PROGRAM_NAME};
|
||||||
use crate::future_feature_flags::{self as features, feature_test};
|
use crate::future_feature_flags::{self as features, feature_test};
|
||||||
@@ -11,6 +9,9 @@
|
|||||||
use libc::F_OK;
|
use libc::F_OK;
|
||||||
use nix::errno::Errno;
|
use nix::errno::Errno;
|
||||||
use nix::NixPath;
|
use nix::NixPath;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
|
mod query;
|
||||||
|
|
||||||
macro_rules! str_enum {
|
macro_rules! str_enum {
|
||||||
($name:ident, $(($val:ident, $str:expr)),* $(,)?) => {
|
($name:ident, $(($val:ident, $str:expr)),* $(,)?) => {
|
||||||
@@ -37,7 +38,7 @@ fn to_wstr(self) -> &'static wstr {
|
|||||||
|
|
||||||
use StatusCmd::*;
|
use StatusCmd::*;
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum StatusCmd {
|
pub(in crate::builtins::status) enum StatusCmd {
|
||||||
STATUS_CURRENT_CMD = 1,
|
STATUS_CURRENT_CMD = 1,
|
||||||
STATUS_BASENAME,
|
STATUS_BASENAME,
|
||||||
STATUS_DIRNAME,
|
STATUS_DIRNAME,
|
||||||
@@ -62,6 +63,8 @@ enum StatusCmd {
|
|||||||
STATUS_BUILDINFO,
|
STATUS_BUILDINFO,
|
||||||
STATUS_GET_FILE,
|
STATUS_GET_FILE,
|
||||||
STATUS_LIST_FILES,
|
STATUS_LIST_FILES,
|
||||||
|
STATUS_XTGETTCAP,
|
||||||
|
STATUS_XTVERSION,
|
||||||
}
|
}
|
||||||
|
|
||||||
str_enum!(
|
str_enum!(
|
||||||
@@ -96,6 +99,8 @@ enum StatusCmd {
|
|||||||
(STATUS_STACK_TRACE, "print-stack-trace"),
|
(STATUS_STACK_TRACE, "print-stack-trace"),
|
||||||
(STATUS_STACK_TRACE, "stack-trace"),
|
(STATUS_STACK_TRACE, "stack-trace"),
|
||||||
(STATUS_TEST_FEATURE, "test-feature"),
|
(STATUS_TEST_FEATURE, "test-feature"),
|
||||||
|
(STATUS_XTGETTCAP, "xtgettcap"),
|
||||||
|
(STATUS_XTVERSION, "xtversion"),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Values that may be returned from the test-feature option to status.
|
/// Values that may be returned from the test-feature option to status.
|
||||||
@@ -527,6 +532,12 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B
|
|||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
STATUS_XTGETTCAP => {
|
||||||
|
return query::status_xtgettcap(parser, streams, cmd, args);
|
||||||
|
}
|
||||||
|
STATUS_XTVERSION => {
|
||||||
|
return query::status_xtversion(parser, streams, cmd, args);
|
||||||
|
}
|
||||||
ref s => {
|
ref s => {
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
@@ -714,7 +725,9 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B
|
|||||||
| STATUS_FEATURES
|
| STATUS_FEATURES
|
||||||
| STATUS_TEST_FEATURE
|
| STATUS_TEST_FEATURE
|
||||||
| STATUS_GET_FILE
|
| STATUS_GET_FILE
|
||||||
| STATUS_LIST_FILES => {
|
| STATUS_LIST_FILES
|
||||||
|
| STATUS_XTGETTCAP
|
||||||
|
| STATUS_XTVERSION => {
|
||||||
unreachable!("")
|
unreachable!("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
166
src/builtins/status/query.rs
Normal file
166
src/builtins/status/query.rs
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
use std::ops::ControlFlow;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::builtins::prelude::*;
|
||||||
|
|
||||||
|
use crate::common::wcs2string;
|
||||||
|
use crate::global_safety::RelaxedAtomicBool;
|
||||||
|
use crate::input_common::{
|
||||||
|
terminal_protocols_disable_ifn, InputEventQueuer, TerminalQuery, XtgettcapQuery,
|
||||||
|
};
|
||||||
|
use crate::nix::isatty;
|
||||||
|
use crate::reader::{
|
||||||
|
query_xtgettcap, querying_allowed, reader_pop, reader_push, reader_readline, ReaderConfig,
|
||||||
|
UserQuery,
|
||||||
|
};
|
||||||
|
use crate::terminal::TerminalCommand::QueryPrimaryDeviceAttribute;
|
||||||
|
use crate::terminal::{Output, Outputter, XTVERSION};
|
||||||
|
use libc::STDOUT_FILENO;
|
||||||
|
|
||||||
|
use super::StatusCmd;
|
||||||
|
|
||||||
|
pub(crate) fn status_xtversion(
|
||||||
|
parser: &Parser,
|
||||||
|
streams: &mut IoStreams,
|
||||||
|
cmd: &wstr,
|
||||||
|
args: &[&wstr],
|
||||||
|
) -> BuiltinResult {
|
||||||
|
use super::StatusCmd::STATUS_XTVERSION;
|
||||||
|
if !args.is_empty() {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
BUILTIN_ERR_ARG_COUNT2,
|
||||||
|
cmd,
|
||||||
|
STATUS_XTVERSION.to_wstr(),
|
||||||
|
0,
|
||||||
|
args.len()
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let run_query = { move |_query: &mut Option<TerminalQuery>| ControlFlow::Break(()) };
|
||||||
|
synchronous_query(parser, streams, cmd, &STATUS_XTVERSION, Box::new(run_query))?;
|
||||||
|
|
||||||
|
let Some(xtversion) = XTVERSION.get() else {
|
||||||
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
};
|
||||||
|
|
||||||
|
streams.out.appendln(xtversion);
|
||||||
|
|
||||||
|
Ok(SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn status_xtgettcap(
|
||||||
|
parser: &Parser,
|
||||||
|
streams: &mut IoStreams,
|
||||||
|
cmd: &wstr,
|
||||||
|
args: &[&wstr],
|
||||||
|
) -> BuiltinResult {
|
||||||
|
use super::StatusCmd::STATUS_XTGETTCAP;
|
||||||
|
if !querying_allowed() {
|
||||||
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
}
|
||||||
|
if args.len() != 1 {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
BUILTIN_ERR_ARG_COUNT2,
|
||||||
|
cmd,
|
||||||
|
STATUS_XTGETTCAP.to_wstr(),
|
||||||
|
1,
|
||||||
|
args.len()
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
let result = Rc::new(RelaxedAtomicBool::new(false));
|
||||||
|
let run_query = {
|
||||||
|
let terminfo_code = wcs2string(args[0]);
|
||||||
|
let result = Rc::clone(&result);
|
||||||
|
move |query: &mut Option<TerminalQuery>| {
|
||||||
|
assert!(matches!(
|
||||||
|
*query,
|
||||||
|
None | Some(TerminalQuery::PrimaryDeviceAttribute(None))
|
||||||
|
));
|
||||||
|
let mut output = Outputter::stdoutput().borrow_mut();
|
||||||
|
output.begin_buffering();
|
||||||
|
query_xtgettcap(output.by_ref(), &terminfo_code);
|
||||||
|
output.write_command(QueryPrimaryDeviceAttribute);
|
||||||
|
output.end_buffering();
|
||||||
|
*query = Some(TerminalQuery::PrimaryDeviceAttribute(Some(
|
||||||
|
XtgettcapQuery {
|
||||||
|
terminfo_code,
|
||||||
|
result,
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
synchronous_query(parser, streams, cmd, &STATUS_XTGETTCAP, Box::new(run_query))?;
|
||||||
|
if !result.load() {
|
||||||
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
}
|
||||||
|
Ok(SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synchronous_query(
|
||||||
|
parser: &Parser,
|
||||||
|
streams: &mut IoStreams,
|
||||||
|
cmd: &wstr,
|
||||||
|
subcmd: &StatusCmd,
|
||||||
|
run_query: UserQuery,
|
||||||
|
) -> Result<(), ErrorCode> {
|
||||||
|
if !isatty(streams.stdin_fd) {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%s %s: stdin is not a TTY",
|
||||||
|
cmd,
|
||||||
|
subcmd.to_wstr(),
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
};
|
||||||
|
let out_fd = STDOUT_FILENO;
|
||||||
|
if !isatty(out_fd) {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%s %s: stdout is not a TTY",
|
||||||
|
cmd,
|
||||||
|
subcmd.to_wstr(),
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(query_state) = parser.blocking_query.get() {
|
||||||
|
if (run_query)(&mut query_state.borrow_mut()).is_break() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are the first reader.
|
||||||
|
let empty_spot = parser.pending_user_query.replace(Some(run_query));
|
||||||
|
assert!(empty_spot.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut conf = ReaderConfig::default();
|
||||||
|
conf.inputfd = streams.stdin_fd;
|
||||||
|
conf.prompt_ok = false;
|
||||||
|
conf.exit_on_interrupt = true;
|
||||||
|
|
||||||
|
let pending_keys = {
|
||||||
|
let mut reader = reader_push(parser, L!(""), conf);
|
||||||
|
{
|
||||||
|
let _interactive = parser.push_scope(|s| s.is_interactive = true);
|
||||||
|
let no_line = reader_readline(parser, None);
|
||||||
|
assert!(no_line.is_none());
|
||||||
|
}
|
||||||
|
terminal_protocols_disable_ifn();
|
||||||
|
let input_data = reader.get_input_data_mut();
|
||||||
|
let pending_keys = std::mem::take(&mut input_data.queue);
|
||||||
|
// We blocked code and mapping execution so input function args must be empty.
|
||||||
|
assert!(input_data.input_function_args.is_empty());
|
||||||
|
if input_data.paste_buffer.is_some() {
|
||||||
|
FLOG!(
|
||||||
|
reader,
|
||||||
|
"Bracketed paste was interrupted; dropping uncommitted paste buffer"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
reader_pop();
|
||||||
|
pending_keys
|
||||||
|
};
|
||||||
|
FLOGF!(reader, "Adding %lu pending keys", pending_keys.len());
|
||||||
|
parser.pending_keys.borrow_mut().extend(pending_keys);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
};
|
};
|
||||||
use crate::path::{path_as_implicit_cd, path_get_cdpath, path_get_path, paths_are_same_file};
|
use crate::path::{path_as_implicit_cd, path_get_cdpath, path_get_path, paths_are_same_file};
|
||||||
use crate::terminal::Outputter;
|
use crate::terminal::Outputter;
|
||||||
use crate::text_face::{parse_text_face, TextFace, UnderlineStyle};
|
use crate::text_face::{parse_text_face, SpecifiedTextFace, TextFace, UnderlineStyle};
|
||||||
use crate::threads::assert_is_background_thread;
|
use crate::threads::assert_is_background_thread;
|
||||||
use crate::tokenizer::{variable_assignment_equals_pos, PipeOrRedir};
|
use crate::tokenizer::{variable_assignment_equals_pos, PipeOrRedir};
|
||||||
use crate::wchar::{wstr, WString, L};
|
use crate::wchar::{wstr, WString, L};
|
||||||
@@ -140,12 +140,16 @@ pub(crate) fn resolve_spec_uncached(
|
|||||||
vars: &dyn Environment,
|
vars: &dyn Environment,
|
||||||
) -> TextFace {
|
) -> TextFace {
|
||||||
let resolve_role = |role| {
|
let resolve_role = |role| {
|
||||||
vars.get_unless_empty(get_highlight_var_name(role))
|
for role in [role, get_fallback(role), HighlightRole::normal] {
|
||||||
.or_else(|| vars.get_unless_empty(get_highlight_var_name(get_fallback(role))))
|
if let Some(face) = vars
|
||||||
.or_else(|| vars.get_unless_empty(get_highlight_var_name(HighlightRole::normal)))
|
.get_unless_empty(get_highlight_var_name(role))
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(parse_text_face_for_highlight)
|
.and_then(parse_text_face_for_highlight)
|
||||||
.unwrap_or_else(TextFace::default)
|
{
|
||||||
|
return face;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextFace::default()
|
||||||
};
|
};
|
||||||
let mut face = resolve_role(highlight.foreground);
|
let mut face = resolve_role(highlight.foreground);
|
||||||
|
|
||||||
@@ -162,7 +166,8 @@ pub(crate) fn resolve_spec_uncached(
|
|||||||
if highlight.valid_path {
|
if highlight.valid_path {
|
||||||
if let Some(valid_path_var) = vars.get(L!("fish_color_valid_path")) {
|
if let Some(valid_path_var) = vars.get(L!("fish_color_valid_path")) {
|
||||||
// Historical behavior is to not apply background.
|
// Historical behavior is to not apply background.
|
||||||
let valid_path_face = parse_text_face_for_highlight(&valid_path_var);
|
let valid_path_face =
|
||||||
|
parse_text_face_for_highlight(&valid_path_var).unwrap_or_default();
|
||||||
// Apply the foreground, except if it's normal. The intention here is likely
|
// Apply the foreground, except if it's normal. The intention here is likely
|
||||||
// to only override foreground if the valid path color has an explicit foreground.
|
// to only override foreground if the valid path color has an explicit foreground.
|
||||||
if !valid_path_face.fg.is_normal() {
|
if !valid_path_face.fg.is_normal() {
|
||||||
@@ -181,19 +186,21 @@ pub(crate) fn resolve_spec_uncached(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the internal color code representing the specified color.
|
/// Return the internal color code representing the specified color.
|
||||||
pub(crate) fn parse_text_face_for_highlight(var: &EnvVar) -> TextFace {
|
pub(crate) fn parse_text_face_for_highlight(var: &EnvVar) -> Option<TextFace> {
|
||||||
let face = parse_text_face(var.as_list());
|
let face = parse_text_face(var.as_list());
|
||||||
let default = TextFace::default();
|
(face != SpecifiedTextFace::default()).then(|| {
|
||||||
let fg = face.fg.unwrap_or(default.fg);
|
let default = TextFace::default();
|
||||||
let bg = face.bg.unwrap_or(default.bg);
|
let fg = face.fg.unwrap_or(default.fg);
|
||||||
let underline_color = face.underline_color.unwrap_or(default.underline_color);
|
let bg = face.bg.unwrap_or(default.bg);
|
||||||
let style = face.style;
|
let underline_color = face.underline_color.unwrap_or(default.underline_color);
|
||||||
TextFace {
|
let style = face.style.unwrap_or_default();
|
||||||
fg,
|
TextFace {
|
||||||
bg,
|
fg,
|
||||||
underline_color,
|
bg,
|
||||||
style,
|
underline_color,
|
||||||
}
|
style,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_is_valid(
|
fn command_is_valid(
|
||||||
|
|||||||
@@ -18,10 +18,7 @@
|
|||||||
KittyKeyboardProgressiveEnhancementsDisable, KittyKeyboardProgressiveEnhancementsEnable,
|
KittyKeyboardProgressiveEnhancementsDisable, KittyKeyboardProgressiveEnhancementsEnable,
|
||||||
ModifyOtherKeysDisable, ModifyOtherKeysEnable,
|
ModifyOtherKeysDisable, ModifyOtherKeysEnable,
|
||||||
};
|
};
|
||||||
use crate::terminal::{
|
use crate::terminal::{Capability, Output, Outputter, KITTY_KEYBOARD_SUPPORTED, XTVERSION};
|
||||||
Capability, Output, Outputter, KITTY_KEYBOARD_SUPPORTED, SCROLL_FORWARD_SUPPORTED,
|
|
||||||
SCROLL_FORWARD_TERMINFO_CODE,
|
|
||||||
};
|
|
||||||
use crate::threads::{iothread_port, is_main_thread};
|
use crate::threads::{iothread_port, is_main_thread};
|
||||||
use crate::universal_notifier::default_notifier;
|
use crate::universal_notifier::default_notifier;
|
||||||
use crate::wchar::{encode_byte_to_char, prelude::*};
|
use crate::wchar::{encode_byte_to_char, prelude::*};
|
||||||
@@ -32,6 +29,7 @@
|
|||||||
use std::os::fd::RawFd;
|
use std::os::fd::RawFd;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -327,6 +325,8 @@ pub struct KeyInputEvent {
|
|||||||
pub enum ImplicitEvent {
|
pub enum ImplicitEvent {
|
||||||
/// end-of-file was reached.
|
/// end-of-file was reached.
|
||||||
Eof,
|
Eof,
|
||||||
|
/// Done
|
||||||
|
Break,
|
||||||
/// An event was handled internally, or an interrupt was received. Check to see if the reader
|
/// An event was handled internally, or an interrupt was received. Check to see if the reader
|
||||||
/// loop should exit.
|
/// loop should exit.
|
||||||
CheckExit,
|
CheckExit,
|
||||||
@@ -758,15 +758,21 @@ pub fn function_set_status(&mut self, status: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum CursorPositionQuery {
|
pub enum CursorPositionQuery {
|
||||||
MouseLeft(ViewportPosition),
|
MouseLeft(ViewportPosition),
|
||||||
ScrollbackPush,
|
ScrollbackPush,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Debug)]
|
||||||
|
pub struct XtgettcapQuery {
|
||||||
|
pub terminfo_code: Vec<u8>,
|
||||||
|
pub result: Rc<RelaxedAtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TerminalQuery {
|
pub enum TerminalQuery {
|
||||||
PrimaryDeviceAttribute,
|
PrimaryDeviceAttribute(Option<XtgettcapQuery>),
|
||||||
CursorPositionReport(CursorPositionQuery),
|
CursorPositionReport(CursorPositionQuery),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,6 +938,8 @@ fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
|
|||||||
);
|
);
|
||||||
let ok = stop_query(self.blocking_query());
|
let ok = stop_query(self.blocking_query());
|
||||||
assert!(ok);
|
assert!(ok);
|
||||||
|
// TODO only if cancellation
|
||||||
|
self.push_front(CharEvent::Implicit(ImplicitEvent::Break));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -959,10 +967,7 @@ fn parse_escape_sequence(
|
|||||||
assert!(buffer.len() <= 2);
|
assert!(buffer.len() <= 2);
|
||||||
let recursive_invocation = buffer.len() == 2;
|
let recursive_invocation = buffer.len() == 2;
|
||||||
let Some(next) = self.try_readb(buffer) else {
|
let Some(next) = self.try_readb(buffer) else {
|
||||||
if !self.paste_is_buffering() {
|
return Some(KeyEvent::from_raw(key::Escape));
|
||||||
return Some(KeyEvent::from_raw(key::Escape));
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
let invalid = KeyEvent::from_raw(key::Invalid);
|
let invalid = KeyEvent::from_raw(key::Invalid);
|
||||||
if recursive_invocation && next == b'\x1b' {
|
if recursive_invocation && next == b'\x1b' {
|
||||||
@@ -1223,6 +1228,7 @@ fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
b'c' if private_mode == Some(b'?') => {
|
b'c' if private_mode == Some(b'?') => {
|
||||||
|
FLOG!(reader, "Received primary device attribute response");
|
||||||
self.push_front(CharEvent::QueryResponse(
|
self.push_front(CharEvent::QueryResponse(
|
||||||
QueryResponseEvent::PrimaryDeviceAttribute,
|
QueryResponseEvent::PrimaryDeviceAttribute,
|
||||||
));
|
));
|
||||||
@@ -1369,13 +1375,12 @@ fn parse_xtversion(&mut self, buffer: &mut Vec<u8>) -> Option<()> {
|
|||||||
if buffer.get(3)? != &b'|' {
|
if buffer.get(3)? != &b'|' {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let xtversion = str2wcstring(&buffer[4..buffer.len()]);
|
||||||
FLOG!(
|
FLOG!(
|
||||||
reader,
|
reader,
|
||||||
format!(
|
format!("Received XTVERSION response: {}", xtversion)
|
||||||
"Received XTVERSION response: {}",
|
|
||||||
str2wcstring(&buffer[4..buffer.len()])
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
XTVERSION.get_or_init(|| xtversion);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,9 +1437,17 @@ fn parse_dcs(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
|
|||||||
format!("Received XTGETTCAP response: {}", str2wcstring(&key))
|
format!("Received XTGETTCAP response: {}", str2wcstring(&key))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if key == SCROLL_FORWARD_TERMINFO_CODE.as_bytes() {
|
if let Some(TerminalQuery::PrimaryDeviceAttribute(Some(tcap_query))) =
|
||||||
SCROLL_FORWARD_SUPPORTED.store(true);
|
&*self.blocking_query()
|
||||||
FLOG!(reader, "Scroll forward is supported");
|
{
|
||||||
|
if tcap_query.terminfo_code == key {
|
||||||
|
if tcap_query.result.swap(true) {
|
||||||
|
FLOG!(
|
||||||
|
reader,
|
||||||
|
"Error: received multiple XTGETTCAP responses for user query"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -772,6 +772,10 @@ pub fn new(fd: RawFd) -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.fd
|
||||||
|
}
|
||||||
|
|
||||||
fn append(&mut self, s: &wstr) -> bool {
|
fn append(&mut self, s: &wstr) -> bool {
|
||||||
if self.errored {
|
if self.errored {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
};
|
};
|
||||||
use crate::fds::{open_dir, BEST_O_SEARCH};
|
use crate::fds::{open_dir, BEST_O_SEARCH};
|
||||||
use crate::global_safety::RelaxedAtomicBool;
|
use crate::global_safety::RelaxedAtomicBool;
|
||||||
use crate::input_common::{terminal_protocols_disable_ifn, TerminalQuery};
|
use crate::input_common::{terminal_protocols_disable_ifn, CharEvent, TerminalQuery};
|
||||||
use crate::io::IoChain;
|
use crate::io::IoChain;
|
||||||
use crate::job_group::MaybeJobId;
|
use crate::job_group::MaybeJobId;
|
||||||
use crate::operation_context::{OperationContext, EXPANSION_LIMIT_DEFAULT};
|
use crate::operation_context::{OperationContext, EXPANSION_LIMIT_DEFAULT};
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
use crate::parse_execution::{EndExecutionReason, ExecutionContext};
|
use crate::parse_execution::{EndExecutionReason, ExecutionContext};
|
||||||
use crate::parse_tree::{parse_source, LineCounter, ParsedSourceRef};
|
use crate::parse_tree::{parse_source, LineCounter, ParsedSourceRef};
|
||||||
use crate::proc::{job_reap, JobGroupRef, JobList, JobRef, Pid, ProcStatus};
|
use crate::proc::{job_reap, JobGroupRef, JobList, JobRef, Pid, ProcStatus};
|
||||||
|
use crate::reader::UserQuery;
|
||||||
use crate::signal::{signal_check_cancel, signal_clear_cancel, Signal};
|
use crate::signal::{signal_check_cancel, signal_clear_cancel, Signal};
|
||||||
use crate::threads::assert_is_main_thread;
|
use crate::threads::assert_is_main_thread;
|
||||||
use crate::util::get_time;
|
use crate::util::get_time;
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
#[cfg(not(target_has_atomic = "64"))]
|
#[cfg(not(target_has_atomic = "64"))]
|
||||||
use portable_atomic::AtomicU64;
|
use portable_atomic::AtomicU64;
|
||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::ffi::{CStr, OsStr};
|
use std::ffi::{CStr, OsStr};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -442,6 +444,10 @@ pub struct Parser {
|
|||||||
pub global_event_blocks: AtomicU64,
|
pub global_event_blocks: AtomicU64,
|
||||||
|
|
||||||
pub blocking_query: OnceCell<RefCell<Option<TerminalQuery>>>,
|
pub blocking_query: OnceCell<RefCell<Option<TerminalQuery>>>,
|
||||||
|
|
||||||
|
pub pending_user_query: RefCell<Option<UserQuery>>,
|
||||||
|
|
||||||
|
pub pending_keys: RefCell<VecDeque<CharEvent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
@@ -460,6 +466,8 @@ pub fn new(variables: Rc<EnvStack>, cancel_behavior: CancelBehavior) -> Parser {
|
|||||||
profile_items: RefCell::default(),
|
profile_items: RefCell::default(),
|
||||||
global_event_blocks: AtomicU64::new(0),
|
global_event_blocks: AtomicU64::new(0),
|
||||||
blocking_query: OnceCell::new(),
|
blocking_query: OnceCell::new(),
|
||||||
|
pending_user_query: RefCell::new(None),
|
||||||
|
pending_keys: RefCell::new(VecDeque::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
match open_dir(CStr::from_bytes_with_nul(b".\0").unwrap(), BEST_O_SEARCH) {
|
match open_dir(CStr::from_bytes_with_nul(b".\0").unwrap(), BEST_O_SEARCH) {
|
||||||
|
|||||||
209
src/reader.rs
209
src/reader.rs
@@ -85,6 +85,7 @@
|
|||||||
use crate::input_common::terminal_protocols_disable_ifn;
|
use crate::input_common::terminal_protocols_disable_ifn;
|
||||||
use crate::input_common::CursorPositionQuery;
|
use crate::input_common::CursorPositionQuery;
|
||||||
use crate::input_common::ImplicitEvent;
|
use crate::input_common::ImplicitEvent;
|
||||||
|
use crate::input_common::InputEventQueuer;
|
||||||
use crate::input_common::QueryResponseEvent;
|
use crate::input_common::QueryResponseEvent;
|
||||||
use crate::input_common::TerminalQuery;
|
use crate::input_common::TerminalQuery;
|
||||||
use crate::input_common::IN_DVTM;
|
use crate::input_common::IN_DVTM;
|
||||||
@@ -134,9 +135,7 @@
|
|||||||
QueryCursorPosition, QueryKittyKeyboardProgressiveEnhancements, QueryPrimaryDeviceAttribute,
|
QueryCursorPosition, QueryKittyKeyboardProgressiveEnhancements, QueryPrimaryDeviceAttribute,
|
||||||
QueryXtgettcap, QueryXtversion,
|
QueryXtgettcap, QueryXtversion,
|
||||||
};
|
};
|
||||||
use crate::terminal::{
|
use crate::terminal::{Capability, KITTY_KEYBOARD_SUPPORTED};
|
||||||
Capability, KITTY_KEYBOARD_SUPPORTED, SCROLL_FORWARD_SUPPORTED, SCROLL_FORWARD_TERMINFO_CODE,
|
|
||||||
};
|
|
||||||
use crate::termsize::{termsize_invalidate_tty, termsize_last, termsize_update};
|
use crate::termsize::{termsize_invalidate_tty, termsize_last, termsize_update};
|
||||||
use crate::text_face::parse_text_face;
|
use crate::text_face::parse_text_face;
|
||||||
use crate::text_face::TextFace;
|
use crate::text_face::TextFace;
|
||||||
@@ -238,25 +237,25 @@ fn redirect_tty_after_sighup() {
|
|||||||
pub(crate) fn initial_query(
|
pub(crate) fn initial_query(
|
||||||
blocking_query: &OnceCell<RefCell<Option<TerminalQuery>>>,
|
blocking_query: &OnceCell<RefCell<Option<TerminalQuery>>>,
|
||||||
out: &mut impl Output,
|
out: &mut impl Output,
|
||||||
vars: Option<&dyn Environment>,
|
|
||||||
) {
|
) {
|
||||||
blocking_query.get_or_init(|| {
|
blocking_query.get_or_init(|| {
|
||||||
let query = if is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load() {
|
let query = querying_allowed().then(|| {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// Query for kitty keyboard protocol support.
|
// Query for kitty keyboard protocol support.
|
||||||
out.write_command(QueryKittyKeyboardProgressiveEnhancements);
|
out.write_command(QueryKittyKeyboardProgressiveEnhancements);
|
||||||
out.write_command(QueryXtversion);
|
out.write_command(QueryXtversion);
|
||||||
if let Some(vars) = vars {
|
|
||||||
query_capabilities_via_dcs(out.by_ref(), vars);
|
|
||||||
}
|
|
||||||
out.write_command(QueryPrimaryDeviceAttribute);
|
out.write_command(QueryPrimaryDeviceAttribute);
|
||||||
Some(TerminalQuery::PrimaryDeviceAttribute)
|
TerminalQuery::PrimaryDeviceAttribute(None)
|
||||||
};
|
});
|
||||||
RefCell::new(query)
|
RefCell::new(query)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type UserQuery = Box<dyn FnOnce(&mut Option<TerminalQuery>) -> ControlFlow<()>>;
|
||||||
|
|
||||||
|
pub(crate) fn querying_allowed() -> bool {
|
||||||
|
!(is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load())
|
||||||
|
}
|
||||||
|
|
||||||
/// The stack of current interactive reading contexts.
|
/// The stack of current interactive reading contexts.
|
||||||
fn reader_data_stack() -> &'static mut Vec<Pin<Box<ReaderData>>> {
|
fn reader_data_stack() -> &'static mut Vec<Pin<Box<ReaderData>>> {
|
||||||
struct ReaderDataStack(UnsafeCell<Vec<Pin<Box<ReaderData>>>>);
|
struct ReaderDataStack(UnsafeCell<Vec<Pin<Box<ReaderData>>>>);
|
||||||
@@ -273,7 +272,7 @@ pub fn reader_in_interactive_read() -> bool {
|
|||||||
reader_data_stack()
|
reader_data_stack()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.any(|reader| reader.conf.exit_on_interrupt)
|
.any(|reader| reader.conf.in_builtin_read)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the top level reader data.
|
/// Access the top level reader data.
|
||||||
@@ -338,6 +337,9 @@ pub struct ReaderConfig {
|
|||||||
/// Whether to allow autosuggestions.
|
/// Whether to allow autosuggestions.
|
||||||
pub autosuggest_ok: bool,
|
pub autosuggest_ok: bool,
|
||||||
|
|
||||||
|
/// Whether to show a prompt.
|
||||||
|
pub prompt_ok: bool,
|
||||||
|
|
||||||
/// Whether to reexecute prompt function before final rendering.
|
/// Whether to reexecute prompt function before final rendering.
|
||||||
pub transient_prompt: bool,
|
pub transient_prompt: bool,
|
||||||
|
|
||||||
@@ -347,6 +349,9 @@ pub struct ReaderConfig {
|
|||||||
/// Whether to exit on interrupt (^C).
|
/// Whether to exit on interrupt (^C).
|
||||||
pub exit_on_interrupt: bool,
|
pub exit_on_interrupt: bool,
|
||||||
|
|
||||||
|
/// Whether we are in builtin read.
|
||||||
|
pub in_builtin_read: bool,
|
||||||
|
|
||||||
/// If set, do not show what is typed.
|
/// If set, do not show what is typed.
|
||||||
pub in_silent_mode: bool,
|
pub in_silent_mode: bool,
|
||||||
|
|
||||||
@@ -691,6 +696,7 @@ fn read_i(parser: &Parser) {
|
|||||||
conf.syntax_check_ok = true;
|
conf.syntax_check_ok = true;
|
||||||
conf.expand_abbrev_ok = true;
|
conf.expand_abbrev_ok = true;
|
||||||
conf.autosuggest_ok = check_bool_var(parser.vars(), L!("fish_autosuggestion_enabled"), true);
|
conf.autosuggest_ok = check_bool_var(parser.vars(), L!("fish_autosuggestion_enabled"), true);
|
||||||
|
conf.prompt_ok = true;
|
||||||
conf.transient_prompt = check_bool_var(parser.vars(), L!("fish_transient_prompt"), false);
|
conf.transient_prompt = check_bool_var(parser.vars(), L!("fish_transient_prompt"), false);
|
||||||
|
|
||||||
if parser.is_breakpoint() && function::exists(DEBUG_PROMPT_FUNCTION_NAME, parser) {
|
if parser.is_breakpoint() && function::exists(DEBUG_PROMPT_FUNCTION_NAME, parser) {
|
||||||
@@ -882,6 +888,8 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
|||||||
term_donate(/*quiet=*/ true);
|
term_donate(/*quiet=*/ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
terminal_protocol_hacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reader_deinit(in_signal_handler: bool, restore_foreground_pgroup: bool) {
|
pub fn reader_deinit(in_signal_handler: bool, restore_foreground_pgroup: bool) {
|
||||||
@@ -1061,8 +1069,7 @@ pub fn reader_reading_interrupted(data: &mut ReaderData) -> i32 {
|
|||||||
/// characters even if a full line has not yet been read. Note: the returned value may be longer
|
/// characters even if a full line has not yet been read. Note: the returned value may be longer
|
||||||
/// than nchars if a single keypress resulted in multiple characters being inserted into the
|
/// than nchars if a single keypress resulted in multiple characters being inserted into the
|
||||||
/// commandline.
|
/// commandline.
|
||||||
pub fn reader_readline(parser: &Parser, nchars: usize) -> Option<WString> {
|
pub fn reader_readline(parser: &Parser, nchars: Option<NonZeroUsize>) -> Option<WString> {
|
||||||
let nchars = NonZeroUsize::try_from(nchars).ok();
|
|
||||||
let data = current_data().unwrap();
|
let data = current_data().unwrap();
|
||||||
let mut reader = Reader { parser, data };
|
let mut reader = Reader { parser, data };
|
||||||
reader.readline(nchars)
|
reader.readline(nchars)
|
||||||
@@ -2202,73 +2209,77 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
|
|||||||
initial_query(
|
initial_query(
|
||||||
&self.parser.blocking_query,
|
&self.parser.blocking_query,
|
||||||
&mut BufferedOutputter::new(Outputter::stdoutput()),
|
&mut BufferedOutputter::new(Outputter::stdoutput()),
|
||||||
Some(self.parser.vars()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// HACK: Don't abandon line for the first prompt, because
|
if self.conf.prompt_ok {
|
||||||
// if we're started with the terminal it might not have settled,
|
// HACK: Don't abandon line for the first prompt, because
|
||||||
// so the width is quite likely to be in flight.
|
// if we're started with the terminal it might not have settled,
|
||||||
//
|
// so the width is quite likely to be in flight.
|
||||||
// This means that `printf %s foo; fish` will overwrite the `foo`,
|
//
|
||||||
// but that's a smaller problem than having the omitted newline char
|
// This means that `printf %s foo; fish` will overwrite the `foo`,
|
||||||
// appear constantly.
|
// but that's a smaller problem than having the omitted newline char
|
||||||
//
|
// appear constantly.
|
||||||
// I can't see a good way around this.
|
//
|
||||||
if !self.first_prompt {
|
// I can't see a good way around this.
|
||||||
self.screen
|
if !self.first_prompt {
|
||||||
.reset_abandoning_line(usize::try_from(termsize_last().width).unwrap());
|
self.screen
|
||||||
|
.reset_abandoning_line(usize::try_from(termsize_last().width).unwrap());
|
||||||
|
}
|
||||||
|
self.first_prompt = false;
|
||||||
|
|
||||||
|
if !self.conf.event.is_empty() {
|
||||||
|
event::fire_generic(self.parser, self.conf.event.to_owned(), vec![]);
|
||||||
|
}
|
||||||
|
self.exec_prompt(true, false);
|
||||||
|
|
||||||
|
// Start out as initially dirty.
|
||||||
|
self.force_exec_prompt_and_repaint = true;
|
||||||
}
|
}
|
||||||
self.first_prompt = false;
|
|
||||||
|
|
||||||
if !self.conf.event.is_empty() {
|
|
||||||
event::fire_generic(self.parser, self.conf.event.to_owned(), vec![]);
|
|
||||||
}
|
|
||||||
self.exec_prompt(true, false);
|
|
||||||
|
|
||||||
// Start out as initially dirty.
|
|
||||||
self.force_exec_prompt_and_repaint = true;
|
|
||||||
|
|
||||||
|
self.insert_front(self.parser.pending_keys.take());
|
||||||
while !self.rls().finished && !check_exit_loop_maybe_warning(Some(self)) {
|
while !self.rls().finished && !check_exit_loop_maybe_warning(Some(self)) {
|
||||||
if self.handle_char_event(None).is_break() {
|
if self.handle_char_event(None).is_break() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.conf.transient_prompt {
|
if self.conf.prompt_ok {
|
||||||
self.exec_prompt(true, true);
|
if self.conf.transient_prompt {
|
||||||
}
|
self.exec_prompt(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
// Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the
|
// Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the
|
||||||
// user presses enter.
|
// user presses enter.
|
||||||
if self.is_repaint_needed(None)
|
if self.is_repaint_needed(None)
|
||||||
|| self.screen.scrolled()
|
|| self.screen.scrolled()
|
||||||
|| self.conf.inputfd != STDIN_FILENO
|
|| self.conf.inputfd != STDIN_FILENO
|
||||||
{
|
{
|
||||||
self.layout_and_repaint_before_execution();
|
self.layout_and_repaint_before_execution();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish syntax highlighting (but do not wait forever).
|
// Finish syntax highlighting (but do not wait forever).
|
||||||
if self.rls().finished {
|
if self.rls().finished {
|
||||||
self.finish_highlighting_before_exec();
|
self.finish_highlighting_before_exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit a newline so that the output is on the line after the command.
|
// Emit a newline so that the output is on the line after the command.
|
||||||
// But do not emit a newline if the cursor has wrapped onto a new line all its own - see #6826.
|
// But do not emit a newline if the cursor has wrapped onto a new line all its own - see #6826.
|
||||||
if !self.screen.cursor_is_wrapped_to_own_line() {
|
if !self.screen.cursor_is_wrapped_to_own_line() {
|
||||||
let _ = write_to_fd(b"\n", STDOUT_FILENO);
|
let _ = write_to_fd(b"\n", STDOUT_FILENO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: If stdin isn't the same terminal as stdout, we just moved the cursor.
|
// HACK: If stdin isn't the same terminal as stdout, we just moved the cursor.
|
||||||
// For now, just reset it to the beginning of the line.
|
// For now, just reset it to the beginning of the line.
|
||||||
if self.conf.inputfd != STDIN_FILENO {
|
if self.conf.inputfd != STDIN_FILENO {
|
||||||
let _ = write_loop(&STDOUT_FILENO, b"\r");
|
let _ = write_loop(&STDOUT_FILENO, b"\r");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have no pager contents when we exit.
|
// Ensure we have no pager contents when we exit.
|
||||||
if !self.pager.is_empty() {
|
if !self.pager.is_empty() {
|
||||||
// Clear to end of screen to erase the pager contents.
|
// Clear to end of screen to erase the pager contents.
|
||||||
screen_force_clear_to_end();
|
screen_force_clear_to_end();
|
||||||
self.clear_pager();
|
self.clear_pager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if EXIT_STATE.load(Ordering::Relaxed) != ExitState::FinishedHandlers as _ {
|
if EXIT_STATE.load(Ordering::Relaxed) != ExitState::FinishedHandlers as _ {
|
||||||
@@ -2430,10 +2441,12 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
|
|||||||
});
|
});
|
||||||
|
|
||||||
// If we ran `exit` anywhere, exit.
|
// If we ran `exit` anywhere, exit.
|
||||||
self.exit_loop_requested =
|
self.exit_loop_requested |= self.parser.libdata().exit_current_script;
|
||||||
self.exit_loop_requested || self.parser.libdata().exit_current_script;
|
|
||||||
self.parser.libdata_mut().exit_current_script = false;
|
self.parser.libdata_mut().exit_current_script = false;
|
||||||
if self.exit_loop_requested {
|
if self.exit_loop_requested {
|
||||||
|
if !self.conf.prompt_ok {
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2507,6 +2520,7 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
|
|||||||
ImplicitEvent::Eof => {
|
ImplicitEvent::Eof => {
|
||||||
reader_sighup();
|
reader_sighup();
|
||||||
}
|
}
|
||||||
|
ImplicitEvent::Break => return ControlFlow::Break(()),
|
||||||
ImplicitEvent::CheckExit => (),
|
ImplicitEvent::CheckExit => (),
|
||||||
ImplicitEvent::FocusIn => {
|
ImplicitEvent::FocusIn => {
|
||||||
event::fire_generic(self.parser, L!("fish_focus_in").to_owned(), vec![]);
|
event::fire_generic(self.parser, L!("fish_focus_in").to_owned(), vec![]);
|
||||||
@@ -2531,16 +2545,31 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
|
|||||||
CharEvent::QueryResponse(query_response) => {
|
CharEvent::QueryResponse(query_response) => {
|
||||||
match query_response {
|
match query_response {
|
||||||
QueryResponseEvent::PrimaryDeviceAttribute => {
|
QueryResponseEvent::PrimaryDeviceAttribute => {
|
||||||
if *self.blocking_query() != Some(TerminalQuery::PrimaryDeviceAttribute) {
|
let mut query = self.blocking_query();
|
||||||
|
let Some(TerminalQuery::PrimaryDeviceAttribute(tcap_query)) = &*query
|
||||||
|
else {
|
||||||
// Rogue reply.
|
// Rogue reply.
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
}
|
};
|
||||||
if KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed)
|
if KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed)
|
||||||
== Capability::Unknown as _
|
== Capability::Unknown as _
|
||||||
{
|
{
|
||||||
KITTY_KEYBOARD_SUPPORTED
|
KITTY_KEYBOARD_SUPPORTED
|
||||||
.store(Capability::NotSupported as _, Ordering::Release);
|
.store(Capability::NotSupported as _, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
assert!(
|
||||||
|
tcap_query.is_none()
|
||||||
|
|| self.parser.pending_user_query.borrow().is_none()
|
||||||
|
);
|
||||||
|
if tcap_query.is_some() {
|
||||||
|
stop_query(query);
|
||||||
|
// TODO hack
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
|
if let Some(queued_query) = self.parser.pending_user_query.take() {
|
||||||
|
// TODO hack
|
||||||
|
return (queued_query)(&mut query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QueryResponseEvent::CursorPositionReport(cursor_pos) => {
|
QueryResponseEvent::CursorPositionReport(cursor_pos) => {
|
||||||
let cursor_pos_query = match &*self.blocking_query() {
|
let cursor_pos_query = match &*self.blocking_query() {
|
||||||
@@ -2567,29 +2596,22 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_xtgettcap_query(out: &mut impl Output, cap: &'static str) {
|
pub(crate) fn query_xtgettcap(out: &mut impl Output, cap: &[u8]) {
|
||||||
|
let command = QueryXtgettcap(cap);
|
||||||
if should_flog!(reader) {
|
if should_flog!(reader) {
|
||||||
let mut tmp = Vec::<u8>::new();
|
let mut tmp = Vec::<u8>::new();
|
||||||
tmp.write_command(QueryXtgettcap(cap));
|
tmp.write_command(command.clone());
|
||||||
FLOG!(
|
FLOG!(
|
||||||
reader,
|
reader,
|
||||||
format!("Sending XTGETTCAP request for {}: {:?}", cap, tmp)
|
sprintf!(
|
||||||
|
"Sending XTGETTCAP request for %s: %s",
|
||||||
|
str2wcstring(cap),
|
||||||
|
escape(&str2wcstring(&tmp))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
out.write_command(QueryXtgettcap(cap));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn query_capabilities_via_dcs(out: &mut impl Output, vars: &dyn Environment) {
|
|
||||||
if vars.get_unless_empty(L!("STY")).is_some()
|
|
||||||
|| vars.get_unless_empty(L!("TERM")).is_some_and(|term| {
|
|
||||||
let term = &term.as_list()[0];
|
|
||||||
term == "screen" || term == "screen-256color"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
out.write_command(DecsetAlternateScreenBuffer); // enable alternative screen buffer
|
out.write_command(DecsetAlternateScreenBuffer); // enable alternative screen buffer
|
||||||
send_xtgettcap_query(out, SCROLL_FORWARD_TERMINFO_CODE);
|
out.write_command(command);
|
||||||
out.write_command(DecrstAlternateScreenBuffer); // disable alternative screen buffer
|
out.write_command(DecrstAlternateScreenBuffer); // disable alternative screen buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2688,7 +2710,9 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
|||||||
|
|
||||||
let mut outp = Outputter::stdoutput().borrow_mut();
|
let mut outp = Outputter::stdoutput().borrow_mut();
|
||||||
if let Some(fish_color_cancel) = self.vars().get(L!("fish_color_cancel")) {
|
if let Some(fish_color_cancel) = self.vars().get(L!("fish_color_cancel")) {
|
||||||
outp.set_text_face(parse_text_face_for_highlight(&fish_color_cancel));
|
outp.set_text_face(
|
||||||
|
parse_text_face_for_highlight(&fish_color_cancel).unwrap_or_default(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
outp.write_wstr(L!("^C"));
|
outp.write_wstr(L!("^C"));
|
||||||
outp.reset_text_face();
|
outp.reset_text_face();
|
||||||
@@ -3815,9 +3839,6 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
|||||||
self.clear_screen_and_repaint();
|
self.clear_screen_and_repaint();
|
||||||
}
|
}
|
||||||
rl::ScrollbackPush => {
|
rl::ScrollbackPush => {
|
||||||
if !SCROLL_FORWARD_SUPPORTED.load() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let query = self.blocking_query();
|
let query = self.blocking_query();
|
||||||
let Some(query) = &*query else {
|
let Some(query) = &*query else {
|
||||||
drop(query);
|
drop(query);
|
||||||
@@ -3828,7 +3849,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match query {
|
match query {
|
||||||
TerminalQuery::PrimaryDeviceAttribute => panic!(),
|
TerminalQuery::PrimaryDeviceAttribute(_) => panic!(),
|
||||||
TerminalQuery::CursorPositionReport(_) => {
|
TerminalQuery::CursorPositionReport(_) => {
|
||||||
// TODO: re-queue it I guess.
|
// TODO: re-queue it I guess.
|
||||||
FLOG!(
|
FLOG!(
|
||||||
@@ -4449,8 +4470,6 @@ fn reader_interactive_init(parser: &Parser) {
|
|||||||
parser
|
parser
|
||||||
.vars()
|
.vars()
|
||||||
.set_one(L!("_"), EnvMode::GLOBAL, L!("fish").to_owned());
|
.set_one(L!("_"), EnvMode::GLOBAL, L!("fish").to_owned());
|
||||||
|
|
||||||
terminal_protocol_hacks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy data for interactive use.
|
/// Destroy data for interactive use.
|
||||||
@@ -4528,6 +4547,7 @@ pub fn reader_write_title(
|
|||||||
|
|
||||||
impl<'a> Reader<'a> {
|
impl<'a> Reader<'a> {
|
||||||
fn exec_prompt_cmd(&self, prompt_cmd: &wstr, final_prompt: bool) -> Vec<WString> {
|
fn exec_prompt_cmd(&self, prompt_cmd: &wstr, final_prompt: bool) -> Vec<WString> {
|
||||||
|
assert!(self.conf.prompt_ok);
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let prompt_cmd = if final_prompt && function::exists(prompt_cmd, self.parser) {
|
let prompt_cmd = if final_prompt && function::exists(prompt_cmd, self.parser) {
|
||||||
Cow::Owned(prompt_cmd.to_owned() + L!(" --final-rendering"))
|
Cow::Owned(prompt_cmd.to_owned() + L!(" --final-rendering"))
|
||||||
@@ -4540,6 +4560,7 @@ fn exec_prompt_cmd(&self, prompt_cmd: &wstr, final_prompt: bool) -> Vec<WString>
|
|||||||
|
|
||||||
/// Execute prompt commands based on the provided arguments. The output is inserted into prompt_buff.
|
/// Execute prompt commands based on the provided arguments. The output is inserted into prompt_buff.
|
||||||
fn exec_prompt(&mut self, full_prompt: bool, final_prompt: bool) {
|
fn exec_prompt(&mut self, full_prompt: bool, final_prompt: bool) {
|
||||||
|
assert!(self.conf.prompt_ok);
|
||||||
// Suppress fish_trace while in the prompt.
|
// Suppress fish_trace while in the prompt.
|
||||||
let _suppress_trace = self.parser.push_scope(|s| s.suppress_fish_trace = true);
|
let _suppress_trace = self.parser.push_scope(|s| s.suppress_fish_trace = true);
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
use crate::common::ToCString;
|
use crate::common::ToCString;
|
||||||
use crate::common::{self, escape_string, wcs2string, wcs2string_appending, EscapeStringStyle};
|
use crate::common::{self, escape_string, wcs2string, wcs2string_appending, EscapeStringStyle};
|
||||||
use crate::future_feature_flags::{self, FeatureFlag};
|
use crate::future_feature_flags::{self, FeatureFlag};
|
||||||
use crate::global_safety::RelaxedAtomicBool;
|
|
||||||
use crate::screen::{is_dumb, only_grayscale};
|
use crate::screen::{is_dumb, only_grayscale};
|
||||||
use crate::text_face::{TextFace, TextStyling, UnderlineStyle};
|
use crate::text_face::{TextFace, TextStyling, UnderlineStyle};
|
||||||
use crate::threads::MainThread;
|
use crate::threads::MainThread;
|
||||||
use crate::wchar::prelude::*;
|
use crate::wchar::prelude::*;
|
||||||
use crate::FLOGF;
|
use crate::FLOGF;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
@@ -83,7 +83,7 @@ pub(crate) enum TerminalCommand<'a> {
|
|||||||
// Commands related to querying (used for backwards-incompatible features).
|
// Commands related to querying (used for backwards-incompatible features).
|
||||||
QueryPrimaryDeviceAttribute,
|
QueryPrimaryDeviceAttribute,
|
||||||
QueryXtversion,
|
QueryXtversion,
|
||||||
QueryXtgettcap(&'static str),
|
QueryXtgettcap(&'a [u8]),
|
||||||
|
|
||||||
DecsetAlternateScreenBuffer,
|
DecsetAlternateScreenBuffer,
|
||||||
DecrstAlternateScreenBuffer,
|
DecrstAlternateScreenBuffer,
|
||||||
@@ -227,8 +227,7 @@ pub(crate) enum Capability {
|
|||||||
|
|
||||||
pub(crate) static KITTY_KEYBOARD_SUPPORTED: AtomicU8 = AtomicU8::new(Capability::Unknown as _);
|
pub(crate) static KITTY_KEYBOARD_SUPPORTED: AtomicU8 = AtomicU8::new(Capability::Unknown as _);
|
||||||
|
|
||||||
pub(crate) static SCROLL_FORWARD_SUPPORTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
pub(crate) static XTVERSION: OnceCell<WString> = OnceCell::new();
|
||||||
pub(crate) static SCROLL_FORWARD_TERMINFO_CODE: &str = "indn";
|
|
||||||
|
|
||||||
pub(crate) fn use_terminfo() -> bool {
|
pub(crate) fn use_terminfo() -> bool {
|
||||||
!future_feature_flags::test(FeatureFlag::ignore_terminfo) && TERM.lock().unwrap().is_some()
|
!future_feature_flags::test(FeatureFlag::ignore_terminfo) && TERM.lock().unwrap().is_some()
|
||||||
@@ -349,16 +348,16 @@ fn cursor_move(out: &mut impl Output, direction: CardinalDirection, steps: usize
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_xtgettcap(out: &mut impl Output, cap: &str) -> bool {
|
fn query_xtgettcap(out: &mut impl Output, cap: &[u8]) -> bool {
|
||||||
write_to_output!(out, "\x1bP+q{}\x1b\\", DisplayAsHex(cap));
|
write_to_output!(out, "\x1bP+q{}\x1b\\", DisplayAsHex(cap));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DisplayAsHex<'a>(&'a str);
|
struct DisplayAsHex<'a>(&'a [u8]);
|
||||||
|
|
||||||
impl<'a> std::fmt::Display for DisplayAsHex<'a> {
|
impl<'a> std::fmt::Display for DisplayAsHex<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for byte in self.0.bytes() {
|
for byte in self.0 {
|
||||||
write!(f, "{:x}", byte)?;
|
write!(f, "{:x}", byte)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -399,7 +398,6 @@ fn osc_133_command_finished(out: &mut impl Output, exit_status: libc::c_int) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_forward(out: &mut impl Output, lines: usize) -> bool {
|
fn scroll_forward(out: &mut impl Output, lines: usize) -> bool {
|
||||||
assert!(SCROLL_FORWARD_SUPPORTED.load());
|
|
||||||
write_to_output!(out, "\x1b[{}S", lines);
|
write_to_output!(out, "\x1b[{}S", lines);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ pub(crate) struct TextFace {
|
|||||||
pub(crate) style: TextStyling,
|
pub(crate) style: TextStyling,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TextFace {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TextFace {
|
impl TextFace {
|
||||||
pub const fn default() -> Self {
|
pub const fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -131,12 +137,12 @@ pub fn new(fg: Color, bg: Color, underline_color: Color, style: TextStyling) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Eq, PartialEq)]
|
||||||
pub(crate) struct SpecifiedTextFace {
|
pub(crate) struct SpecifiedTextFace {
|
||||||
pub(crate) fg: Option<Color>,
|
pub(crate) fg: Option<Color>,
|
||||||
pub(crate) bg: Option<Color>,
|
pub(crate) bg: Option<Color>,
|
||||||
pub(crate) underline_color: Option<Color>,
|
pub(crate) underline_color: Option<Color>,
|
||||||
pub(crate) style: TextStyling,
|
pub(crate) style: Option<TextStyling>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_text_face(arguments: &[WString]) -> SpecifiedTextFace {
|
pub(crate) fn parse_text_face(arguments: &[WString]) -> SpecifiedTextFace {
|
||||||
@@ -167,6 +173,7 @@ pub(crate) enum ParsedArgs<'argarray, 'args> {
|
|||||||
|
|
||||||
pub(crate) enum ParseError<'args> {
|
pub(crate) enum ParseError<'args> {
|
||||||
MissingOptArg,
|
MissingOptArg,
|
||||||
|
MultipleTracking,
|
||||||
UnknownColor(&'args wstr),
|
UnknownColor(&'args wstr),
|
||||||
UnknownUnderlineStyle(&'args wstr),
|
UnknownUnderlineStyle(&'args wstr),
|
||||||
UnknownOption(usize),
|
UnknownOption(usize),
|
||||||
@@ -189,6 +196,7 @@ pub(crate) fn parse_text_face_and_options<'argarray, 'args>(
|
|||||||
wopt(L!("reverse"), ArgType::NoArgument, 'r'),
|
wopt(L!("reverse"), ArgType::NoArgument, 'r'),
|
||||||
wopt(L!("help"), ArgType::NoArgument, 'h'),
|
wopt(L!("help"), ArgType::NoArgument, 'h'),
|
||||||
wopt(L!("print-colors"), ArgType::NoArgument, 'c'),
|
wopt(L!("print-colors"), ArgType::NoArgument, 'c'),
|
||||||
|
wopt(L!("track"), ArgType::RequiredArgument, '\x01'),
|
||||||
];
|
];
|
||||||
let long_options = &long_options[..long_options.len() - builtin_extra_args];
|
let long_options = &long_options[..long_options.len() - builtin_extra_args];
|
||||||
|
|
||||||
@@ -210,6 +218,7 @@ pub(crate) fn parse_text_face_and_options<'argarray, 'args>(
|
|||||||
let mut underline_colors = vec![];
|
let mut underline_colors = vec![];
|
||||||
let mut style = TextStyling::default();
|
let mut style = TextStyling::default();
|
||||||
let mut print_color_mode = false;
|
let mut print_color_mode = false;
|
||||||
|
let mut tracking = false;
|
||||||
|
|
||||||
let mut w = WGetopter::new(short_options, long_options, argv);
|
let mut w = WGetopter::new(short_options, long_options, argv);
|
||||||
while let Some(c) = w.next_opt() {
|
while let Some(c) = w.next_opt() {
|
||||||
@@ -219,6 +228,12 @@ pub(crate) fn parse_text_face_and_options<'argarray, 'args>(
|
|||||||
bg_colors.push(bg);
|
bg_colors.push(bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
'\x01' => {
|
||||||
|
if is_builtin && tracking {
|
||||||
|
return Err(MultipleTracking);
|
||||||
|
}
|
||||||
|
tracking = true;
|
||||||
|
}
|
||||||
'\x02' => {
|
'\x02' => {
|
||||||
if let Some(underline_color) = parse_color(w.woptarg.unwrap())? {
|
if let Some(underline_color) = parse_color(w.woptarg.unwrap())? {
|
||||||
underline_colors.push(underline_color);
|
underline_colors.push(underline_color);
|
||||||
@@ -297,6 +312,6 @@ pub(crate) fn parse_text_face_and_options<'argarray, 'args>(
|
|||||||
fg,
|
fg,
|
||||||
bg,
|
bg,
|
||||||
underline_color,
|
underline_color,
|
||||||
style,
|
style: (style != TextStyling::default()).then_some(style),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
51
tests/checks/fish_config.fish
Normal file
51
tests/checks/fish_config.fish
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#RUN: %fish %s
|
||||||
|
|
||||||
|
mkdir $__fish_config_dir/themes
|
||||||
|
echo >$__fish_config_dir/themes/foo.theme '
|
||||||
|
fish_color_normal cyan
|
||||||
|
fish_color_error brred --underline=curly
|
||||||
|
'
|
||||||
|
|
||||||
|
set -g fish_pager_color_secondary_background custom-value
|
||||||
|
fish_config theme choose foo
|
||||||
|
|
||||||
|
set -S fish_color_normal
|
||||||
|
# CHECK: $fish_color_normal: set in global scope, unexported, with 1 elements
|
||||||
|
# CHECK: $fish_color_normal[1]: |cyan|
|
||||||
|
set -S fish_pager_color_secondary_background
|
||||||
|
# CHECK: $fish_pager_color_secondary_background: set in global scope, unexported, with 0 elements
|
||||||
|
|
||||||
|
# Not a default theme, so we allow --underline=curly even if we don't run inside a terminal that
|
||||||
|
# advertises support via XTGETTCAP.
|
||||||
|
set -S fish_color_error
|
||||||
|
# CHECK: $fish_color_error: set in global scope, unexported, with 2 elements
|
||||||
|
# CHECK: $fish_color_error[1]: |brred|
|
||||||
|
# CHECK: $fish_color_error[2]: |--underline=curly|
|
||||||
|
|
||||||
|
|
||||||
|
function change-theme
|
||||||
|
echo >$__fish_config_dir/themes/fake-default.theme 'fish_color_command' $argv
|
||||||
|
end
|
||||||
|
|
||||||
|
change-theme 'green'
|
||||||
|
echo yes | fish_config theme save --track fake-default
|
||||||
|
echo $fish_color_command
|
||||||
|
# CHECK: green --track=fake-default
|
||||||
|
|
||||||
|
change-theme 'green --bold'
|
||||||
|
fish_config theme update
|
||||||
|
|
||||||
|
echo $fish_color_command
|
||||||
|
# CHECK: green --bold --track=fake-default
|
||||||
|
|
||||||
|
# Test that we silently update when there is a shadowing global.
|
||||||
|
change-theme 'green --italics'
|
||||||
|
set -g fish_color_command normal
|
||||||
|
fish_config theme update
|
||||||
|
set -S fish_color_command
|
||||||
|
# CHECK: $fish_color_command: set in global scope, unexported, with 1 elements
|
||||||
|
# CHECK: $fish_color_command[1]: |normal|
|
||||||
|
# CHECK: $fish_color_command: set in universal scope, unexported, with 3 elements
|
||||||
|
# CHECK: $fish_color_command[1]: |green|
|
||||||
|
# CHECK: $fish_color_command[2]: |--italics|
|
||||||
|
# CHECK: $fish_color_command[3]: |--track=fake-default|
|
||||||
@@ -180,7 +180,10 @@ class SpawnedProc(object):
|
|||||||
self.spawn.delaybeforesend = None
|
self.spawn.delaybeforesend = None
|
||||||
self.prompt_counter = 0
|
self.prompt_counter = 0
|
||||||
if env.get("TERM") != "dumb":
|
if env.get("TERM") != "dumb":
|
||||||
self.spawn.send("\x1b[?123c") # Primary Device Attribute
|
self.send_primary_device_attribute()
|
||||||
|
|
||||||
|
def send_primary_device_attribute(self):
|
||||||
|
self.spawn.send("\x1b[?123c")
|
||||||
|
|
||||||
def time_since_first_message(self):
|
def time_since_first_message(self):
|
||||||
"""Return a delta in seconds since the first message, or 0 if this is the first."""
|
"""Return a delta in seconds since the first message, or 0 if this is the first."""
|
||||||
|
|||||||
32
tests/pexpects/query.py
Normal file
32
tests/pexpects/query.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from pexpect_helper import SpawnedProc
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["TERM"] = "foot"
|
||||||
|
|
||||||
|
sp = SpawnedProc(env=env)
|
||||||
|
send, sendline, sleep, expect_prompt, expect_re, expect_str = (
|
||||||
|
sp.send,
|
||||||
|
sp.sendline,
|
||||||
|
sp.sleep,
|
||||||
|
sp.expect_prompt,
|
||||||
|
sp.expect_re,
|
||||||
|
sp.expect_str,
|
||||||
|
)
|
||||||
|
expect_prompt()
|
||||||
|
|
||||||
|
sendline("function check; and echo true; or echo false; end")
|
||||||
|
|
||||||
|
sendline("status xtgettcap am; check")
|
||||||
|
expect_str("\x1bP+q616d\x1b\\") # 616d is "am" in hex
|
||||||
|
send("\x1bP1+r616d\x1b\\") # success
|
||||||
|
sp.send_primary_device_attribute()
|
||||||
|
expect_str("true")
|
||||||
|
|
||||||
|
sendline("status xtgettcap an; check")
|
||||||
|
expect_str("\x1bP+q616e\x1b\\") # 616e is "an" in hex
|
||||||
|
send("\x1bP0+r616e\x1b\\") # failure
|
||||||
|
sp.send_primary_device_attribute()
|
||||||
|
expect_str("false")
|
||||||
Reference in New Issue
Block a user