mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 12:23:09 +00:00
Auto-escape pastes inside single-quotes
This is to make pasting literals easier. When a user pastes something, we normally take it as-is. The exception is when a single-quote is open, e.g. the current token is foo'bar When something is pasted here, we escape single-quotes (`'`) and backslashes (`\\`), so typing a `'` after it will turn it into a literal token. Fixes #967.
This commit is contained in:
parent
84cf391faa
commit
99e87dded3
3 changed files with 109 additions and 7 deletions
60
share/functions/__fish_commandline_is_singlequoted.fish
Normal file
60
share/functions/__fish_commandline_is_singlequoted.fish
Normal file
|
@ -0,0 +1,60 @@
|
|||
function __fish_commandline_is_singlequoted --description "Return 0 if the current token has an open single-quote"
|
||||
# Go through the token char-by-char in a state machine.
|
||||
# The states are:
|
||||
# - normal - no quoting is active (the starting state)
|
||||
# - single - open single-quote
|
||||
# - double - open double
|
||||
# - escaped - open \\ - the next character is non-special
|
||||
# - single-escaped - open \\ inside single-quotes
|
||||
# - double-escaped - open \\ inside double-quotes
|
||||
|
||||
set -l state normal
|
||||
for char in (commandline -ct | string split "")
|
||||
switch $char
|
||||
case "'" # single-quote
|
||||
switch $state
|
||||
case normal single-escaped
|
||||
set state single
|
||||
case single
|
||||
set state normal
|
||||
end
|
||||
case '"' # double-quote
|
||||
switch $state
|
||||
case normal double-escaped
|
||||
set state double
|
||||
case double
|
||||
set state normal
|
||||
end
|
||||
case \\ # backslash escapes the next character
|
||||
switch $state
|
||||
case double
|
||||
set state double-escaped
|
||||
case double-escaped
|
||||
set state double
|
||||
case single
|
||||
set state single-escaped
|
||||
case single-escaped
|
||||
set state single
|
||||
case normal
|
||||
set state escaped
|
||||
case escaped
|
||||
set state normal
|
||||
end
|
||||
case "*" # Any other character
|
||||
switch $state
|
||||
case escaped
|
||||
set state normal
|
||||
case single-escaped
|
||||
set state single
|
||||
case double-escaped
|
||||
set state double
|
||||
end
|
||||
end
|
||||
end
|
||||
# TODO: Should "single-escaped" also be a success?
|
||||
if contains -- $state single single-escaped
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
|
@ -130,16 +130,47 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod
|
|||
# Re-running `bind` multiple times per mode is still faster than trying to make the list unique,
|
||||
# even without calling `sort -u` or `uniq`, for the vi-bindings.
|
||||
# TODO: This can be solved better once #3872 is implemented.
|
||||
|
||||
# We usually just pass the text through as-is to facilitate pasting code,
|
||||
# but when the current token contains an unbalanced single-quote (`'`),
|
||||
# we escape all single-quotes and backslashes, effectively turning the paste
|
||||
# into one literal token, to facilitate pasting non-code (e.g. markdown or git commitishes)
|
||||
set -l allmodes default
|
||||
set allmodes $allmodes (bind -a | string match -r -- '-M \w+' | string replace -- '-M ' '')
|
||||
for mode in $allmodes
|
||||
bind -M $mode -m paste \e\[200~ 'set -g __fish_last_bind_mode $fish_bind_mode'
|
||||
bind -M $mode -m paste \e\[200~ '__fish_start_bracketed_paste'
|
||||
end
|
||||
# This sequence ends paste-mode and returns to the previous mode we have saved before.
|
||||
bind -M paste \e\[201~ 'set fish_bind_mode $__fish_last_bind_mode; commandline -f force-repaint'
|
||||
bind -M paste \e\[201~ '__fish_stop_bracketed_paste'
|
||||
# In paste-mode, everything self-inserts except for the sequence to get out of it
|
||||
bind -M paste "" self-insert
|
||||
# Without this, a \r will overwrite the other text, rendering it invisible - which makes the exercise kinda pointless.
|
||||
# TODO: Test this in windows (\r\n line endings)
|
||||
bind -M paste \r "commandline -i \n"
|
||||
bind -M paste "'" "__fish_commandline_insert_escaped ' \$__fish_paste_quoted"
|
||||
bind -M paste \\ "__fish_commandline_insert_escaped \\ \$__fish_paste_quoted"
|
||||
end
|
||||
|
||||
function __fish_commandline_insert_escaped --description 'Insert the first arg escaped if a second arg is given'
|
||||
if set -q argv[2]
|
||||
commandline -i \\$argv[1]
|
||||
else
|
||||
commandline -i $argv[1]
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_start_bracketed_paste
|
||||
# Save the last bind mode so we can restore it.
|
||||
set -g __fish_last_bind_mode $fish_bind_mode
|
||||
# If the token is currently single-quoted,
|
||||
# we escape single-quotes (and backslashes).
|
||||
__fish_commandline_is_singlequoted
|
||||
and set -g __fish_paste_quoted 1
|
||||
end
|
||||
|
||||
function __fish_stop_bracketed_paste
|
||||
# Restore the last bind mode.
|
||||
set fish_bind_mode $__fish_last_bind_mode
|
||||
set -e __fish_paste_quoted
|
||||
commandline -f force-repaint
|
||||
end
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
function fish_clipboard_paste
|
||||
set -l data
|
||||
if type -q pbpaste
|
||||
commandline -i -- (pbpaste)
|
||||
set data (pbpaste)
|
||||
else if type -q xsel
|
||||
# Only run `commandline` if `xsel` succeeded.
|
||||
# Return if `xsel` failed.
|
||||
# That way any xsel error is printed (to show e.g. a non-functioning X connection),
|
||||
# but we don't print the redundant (and overly verbose for this) commandline error.
|
||||
# Also require non-empty contents to not clear the buffer.
|
||||
if set -l data (xsel --clipboard)
|
||||
and test -n "$data"
|
||||
commandline -i -- $data
|
||||
if not set data (xsel --clipboard)
|
||||
return 1
|
||||
end
|
||||
end
|
||||
# If the current token has an unmatched single-quote,
|
||||
# escape all single-quotes (and backslashes) in the paste,
|
||||
# in order to turn it into a single literal token.
|
||||
#
|
||||
# This eases pasting non-code (e.g. markdown or git commitishes).
|
||||
if __fish_commandline_is_singlequoted
|
||||
set data (string replace -ra "(['\\\])" '\\\\\\\$1' -- $data)
|
||||
end
|
||||
if test -n "$data"
|
||||
commandline -i -- "$data"
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue