From 5146f7ba338801bff9bef9c4c89ffb59c5aca291 Mon Sep 17 00:00:00 2001 From: Olivier Perret Date: Fri, 21 Dec 2018 18:14:28 +0100 Subject: [PATCH] Rework 'terminal' commands * Arguments of the kakoune command are now preserved in the shell call * Escaping logic is moved from 'new' to 'terminal' --- rc/base/new-client.kak | 6 +--- rc/base/screen.kak | 35 ++++++++++++++------- rc/base/tmux.kak | 31 +++++++++--------- rc/base/x11.kak | 28 ++++++++++++++--- rc/extra/iterm.kak | 71 +++++++++++++++++++++++++++++++----------- rc/extra/kitty.kak | 16 +++++----- 6 files changed, 125 insertions(+), 62 deletions(-) diff --git a/rc/base/new-client.kak b/rc/base/new-client.kak index 4413d6e2..6ec538a9 100644 --- a/rc/base/new-client.kak +++ b/rc/base/new-client.kak @@ -4,11 +4,7 @@ The ''terminal'' alias is being used to determine the user''s preferred terminal The optional arguments are passed as commands to the new client' \ %{ try %{ - terminal %sh{ - # double-up single-quotes - escaped=$(printf %s "$*" | sed -e "s|'|''|g") - printf "kak -c '%s' -e '%s'" "$kak_session" "$escaped" - } + terminal kak -c %val{session} -e "%arg{@}" } catch %{ fail "The 'terminal' alias must be defined to use this command" } diff --git a/rc/base/screen.kak b/rc/base/screen.kak index dca8c5be..e4ddd2e9 100644 --- a/rc/base/screen.kak +++ b/rc/base/screen.kak @@ -9,35 +9,46 @@ hook -group GNUscreen global KakBegin .* %sh{ " } -define-command screen-terminal-impl -hidden -params 3 %{ +define-command screen-terminal-impl -hidden -params 3.. %{ nop %sh{ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)" screen -X eval "$1" "$2" - screen -X screen sh -c "$3; screen -X remove" < "/dev/$tty" + shift 2 + # see x11.kak for what this achieves + args=$( + for i in "$@"; do + if [ "$i" = '' ]; then + printf "'' " + else + printf %s "$i" | sed -e "s|'|'\\\\''|g; s|^|'|; s|$|' |" + fi + done + ) + screen -X screen sh -c "${args} ; screen -X remove" < "/dev/$tty" } } -define-command screen-terminal-vertical -params 1 -shell-completion -docstring ' -screen-terminal-vertical : create a new terminal as a screen pane +define-command screen-terminal-vertical -params 1.. -shell-completion -docstring ' +screen-terminal-vertical [] []: create a new terminal as a screen pane The current pane is split into two, left and right -The shell program passed as argument will be executed in the new terminal' \ +The program passed as argument will be executed in the new terminal' \ %{ screen-terminal-impl 'split -v' 'focus right' %arg{@} } -define-command screen-terminal-horizontal -params 1 -shell-completion -docstring ' -screen-terminal-horizontal : create a new terminal as a screen pane +define-command screen-terminal-horizontal -params 1.. -shell-completion -docstring ' +screen-terminal-horizontal []: create a new terminal as a screen pane The current pane is split into two, top and bottom -The shell program passed as argument will be executed in the new terminal' \ +The program passed as argument will be executed in the new terminal' \ %{ screen-terminal-impl 'split -h' 'focus down' %arg{@} } -define-command screen-terminal-window -params 1 -shell-completion -docstring ' -screen-terminal-window : create a new terminal as a screen window -The shell program passed as argument will be executed in the new terminal' \ +define-command screen-terminal-window -params 1.. -shell-completion -docstring ' +screen-terminal-window []: create a new terminal as a screen window +The program passed as argument will be executed in the new terminal' \ %{ nop %sh{ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)" - screen -X screen sh -c "$*" < "/dev/$tty" + screen -X screen "$@" < "/dev/$tty" } } diff --git a/rc/base/tmux.kak b/rc/base/tmux.kak index 23a381e2..acc5c097 100644 --- a/rc/base/tmux.kak +++ b/rc/base/tmux.kak @@ -11,7 +11,7 @@ hook global KakBegin .* %sh{ fi } -define-command -hidden -params 2 tmux-terminal-impl %{ +define-command -hidden -params 2.. tmux-terminal-impl %{ evaluate-commands %sh{ tmux=${kak_client_env_TMUX:-$TMUX} if [ -z "$tmux" ]; then @@ -19,29 +19,32 @@ define-command -hidden -params 2 tmux-terminal-impl %{ exit fi tmux_args="$1" - TMUX=$tmux tmux $tmux_args env TMPDIR="$TMPDIR" sh -c "$2" < /dev/null > /dev/null 2>&1 & + shift + # ideally we should escape single ';' to stop tmux from interpreting it as a new command + # but that's probably too rare to care + TMUX=$tmux tmux $tmux_args env TMPDIR="$TMPDIR" "$@" < /dev/null > /dev/null 2>&1 & } } -define-command tmux-terminal-vertical -params 1 -shell-completion -docstring ' -tmux-terminal-vertical : create a new terminal as a tmux pane +define-command tmux-terminal-vertical -params 1.. -shell-completion -docstring ' +tmux-terminal-vertical []: create a new terminal as a tmux pane The current pane is split into two, top and bottom -The shell program passed as argument will be executed in the new terminal' \ +The program passed as argument will be executed in the new terminal' \ %{ - tmux-terminal-impl 'split-window -v' %arg{1} + tmux-terminal-impl 'split-window -v' %arg{@} } -define-command tmux-terminal-horizontal -params 1 -shell-completion -docstring ' -tmux-terminal-horizontal : create a new terminal as a tmux pane +define-command tmux-terminal-horizontal -params 1.. -shell-completion -docstring ' +tmux-terminal-horizontal []: create a new terminal as a tmux pane The current pane is split into two, left and right -The shell program passed as argument will be executed in the new terminal' \ +The program passed as argument will be executed in the new terminal' \ %{ - tmux-terminal-impl 'split-window -h' %arg{1} + tmux-terminal-impl 'split-window -h' %arg{@} } -define-command tmux-terminal-window -params 1 -shell-completion -docstring ' -tmux-terminal-window []: create a new terminal as a tmux window -The shell program passed as argument will be executed in the new terminal' \ +define-command tmux-terminal-window -params 1.. -shell-completion -docstring ' +tmux-terminal-window [] []: create a new terminal as a tmux window +The program passed as argument will be executed in the new terminal' \ %{ - tmux-terminal-impl 'new-window' %arg{1} + tmux-terminal-impl 'new-window' %arg{@} } define-command tmux-focus -params ..1 -client-completion -docstring ' diff --git a/rc/base/x11.kak b/rc/base/x11.kak index fea97bbd..9885aa9b 100644 --- a/rc/base/x11.kak +++ b/rc/base/x11.kak @@ -22,16 +22,36 @@ A shell command is appended to the one set in this option at runtime} \ done } -define-command x11-terminal -params 1 -shell-completion -docstring ' -x11-terminal : create a new terminal as an x11 window -The shell program passed as argument will be executed in the new terminal' \ +define-command x11-terminal -params 1.. -shell-completion -docstring ' +x11-terminal []: create a new terminal as an x11 window +The program passed as argument will be executed in the new terminal' \ %{ evaluate-commands %sh{ if [ -z "${kak_opt_termcmd}" ]; then echo "fail 'termcmd option is not set'" exit fi - setsid ${kak_opt_termcmd} "$1" < /dev/null > /dev/null 2>&1 & + # join arguments into a single string, in which they're delimited + # by single quotes, and with single quotes inside transformed to '\'' + # so that sh -c "$args" will re-split the arguments properly + # example: + # $1 = ab + # $2 = foo bar + # $3 = + # $4 = foo'bar + # $args = 'ab' 'foo bar' '' 'foo'\''bar' + # would be nicer to do in a single sed/awk call but that's difficult + args=$( + for i in "$@"; do + # special case to preserve empty variables as sed won't touch these + if [ "$i" = '' ]; then + printf "'' " + else + printf %s "$i" | sed -e "s|'|'\\\\''|g; s|^|'|; s|$|' |" + fi + done + ) + setsid ${kak_opt_termcmd} "$args" < /dev/null > /dev/null 2>&1 & } } diff --git a/rc/extra/iterm.kak b/rc/extra/iterm.kak index 65fdc3dc..c7e7b961 100644 --- a/rc/extra/iterm.kak +++ b/rc/extra/iterm.kak @@ -11,12 +11,25 @@ hook global KakBegin .* %sh{ fi } -define-command -hidden -params 2 iterm-terminal-split-impl %{ +define-command -hidden -params 2.. iterm-terminal-split-impl %{ nop %sh{ - # replace ' with '\\'' in the command - escaped=$(printf %s "$2" | sed -e "s|'|'\\\\\\\\''|g") direction="$1" - cmd="env PATH='${PATH}' TMPDIR='${TMPDIR}' sh -c '$escaped'" + shift + # join the arguments as one string for the shell execution (see x11.kak) + args=$( + for i in "$@"; do + if [ "$i" = '' ]; then + printf "'' " + else + printf %s "$i" | sed -e "s|'|'\\\\''|g; s|^|'|; s|$|' |" + fi + done + ) + # go through another round of escaping for osascript + # \ -> \\ + # " -> \" + escaped=$(printf %s "$args" | sed -e 's|\|\\\\|g; s|"|\\"|g') + cmd="env PATH='${PATH}' TMPDIR='${TMPDIR}' $escaped" osascript \ -e "tell application \"iTerm\"" \ -e " tell current session of current window" \ @@ -26,28 +39,38 @@ define-command -hidden -params 2 iterm-terminal-split-impl %{ } } -define-command iterm-terminal-vertical -params 1 -shell-completion -docstring ' -iterm-terminal-vertical : create a new terminal as an iterm pane +define-command iterm-terminal-vertical -params 1.. -shell-completion -docstring ' +iterm-terminal-vertical []: create a new terminal as an iterm pane The current pane is split into two, top and bottom -The shell program passed as argument will be executed in the new terminal'\ +The program passed as argument will be executed in the new terminal'\ %{ iterm-terminal-split-impl 'vertically' %arg{1} } -define-command iterm-terminal-horizontal -params 1 -shell-completion -docstring ' -iterm-terminal-horizontal : create a new terminal as an iterm pane +define-command iterm-terminal-horizontal -params 1.. -shell-completion -docstring ' +iterm-terminal-horizontal []: create a new terminal as an iterm pane The current pane is split into two, left and right -The shell program passed as argument will be executed in the new terminal'\ +The program passed as argument will be executed in the new terminal'\ %{ iterm-terminal-split-impl 'horizontally' %arg{1} } -define-command iterm-terminal-tab -params 1 -shell-completion -docstring ' -iterm-terminal-tab : create a new terminal as an iterm tab -The shell program passed as argument will be executed in the new terminal'\ +define-command iterm-terminal-tab -params 1.. -shell-completion -docstring ' +iterm-terminal-tab []: create a new terminal as an iterm tab +The program passed as argument will be executed in the new terminal'\ %{ nop %sh{ - escaped=$(printf %s "$1" | sed -e "s|'|'\\\\\\\\''|g") - cmd="env PATH='${PATH}' TMPDIR='${TMPDIR}' sh -c '$escaped'" + # see above + args=$( + for i in "$@"; do + if [ "$i" = '' ]; then + printf "'' " + else + printf %s "$i" | sed -e "s|'|'\\\\''|g; s|^|'|; s|$|' |" + fi + done + ) + escaped=$(printf %s "$args" | sed -e 's|\|\\\\|g; s|"|\\"|g') + cmd="env PATH='${PATH}' TMPDIR='${TMPDIR}' $escaped" osascript \ -e "tell application \"iTerm\"" \ -e " tell current window" \ @@ -58,12 +81,22 @@ The shell program passed as argument will be executed in the new terminal'\ } define-command iterm-terminal-window -params 1 -shell-completion -docstring ' -iterm-terminal-window : create a new terminal as an iterm window -The shell program passed as argument will be executed in the new terminal'\ +iterm-terminal-window []: create a new terminal as an iterm window +The program passed as argument will be executed in the new terminal'\ %{ nop %sh{ - escaped=$(printf %s "$1" | sed -e "s|'|'\\\\\\\\''|g") - cmd="env PATH='${PATH}' TMPDIR='${TMPDIR}' sh -c '$escaped'" + # see above + args=$( + for i in "$@"; do + if [ "$i" = '' ]; then + printf "'' " + else + printf %s "$i" | sed -e "s|'|'\\\\''|g; s|^|'|; s|$|' |" + fi + done + ) + escaped=$(printf %s "$args" | sed -e 's|\|\\\\|g; s|"|\\"|g') + cmd="env PATH='${PATH}' TMPDIR='${TMPDIR}' $escaped" osascript \ -e "tell application \"iTerm\"" \ -e " create window with default profile command \"${cmd}\"" \ diff --git a/rc/extra/kitty.kak b/rc/extra/kitty.kak index 56931423..79583945 100644 --- a/rc/extra/kitty.kak +++ b/rc/extra/kitty.kak @@ -12,21 +12,21 @@ hook -group kitty-hooks global KakBegin .* %sh{ fi } -define-command kitty-terminal -params 1 -shell-completion -docstring ' -kitty-terminal : create a new terminal as a kitty window -The shell program passed as argument will be executed in the new terminal' \ +define-command kitty-terminal -params 1.. -shell-completion -docstring ' +kitty-terminal []: create a new terminal as a kitty window +The program passed as argument will be executed in the new terminal' \ %{ nop %sh{ - kitty @ new-window --no-response --window-type $kak_opt_kitty_window_type sh -c "$1" + kitty @ new-window --no-response --window-type $kak_opt_kitty_window_type "$@" } } -define-command kitty-terminal-tab -params 1 -shell-completion -docstring ' -kitty-terminal-tab : create a new terminal as kitty tab -The shell program passed as argument will be executed in the new terminal' \ +define-command kitty-terminal-tab -params 1.. -shell-completion -docstring ' +kitty-terminal-tab []: create a new terminal as kitty tab +The program passed as argument will be executed in the new terminal' \ %{ nop %sh{ - kitty @ new-window --no-response --new-tab sh -c "$1" + kitty @ new-window --no-response --new-tab "$@" } }