foxshell/fn/z4h-quote-prev-zword
2025-07-04 11:48:40 -05:00

73 lines
2.6 KiB
Bash

#!/usr/bin/env zsh
#
# Quote the shell word to the left of the cursor with double or single quotes.
# Prefer quotes with the shorter result or double quotes if there is no
# difference. If already quoted, change quotes. If there is an unterminated
# quote, terminate it and quote the resulting word with the same kind of quotes.
# Keep the cursor on the content it was pointing to before the widget was
# invoked.
#
# Here are a few examples. The caret shows the position of the cursor.
# *Initial* is the content of the buffer before the widget is executed.
# *Once* is the content of the buffer after the widget is executed.
# *Twice* is the content of the buffer after the widget is executed twice.
#
# | Initial | Once | Twice | Note
# |----------|------------|------------|-------------------
# | foo | "foo" | 'foo' | prefer double quotes
# | ^ | ^ | ^ |
# | foo | "foo" | 'foo' | keep cursor on the same word char
# | ^ | ^ | ^ |
# | foo | "foo" | 'foo' |
# | ^ | ^ | ^ |
# | foo bar | "foo" bar | 'foo' bar | cursor on a space => quote prev word
# | ^ | ^ | ^ |
# | foo bar | foo "bar" | foo 'bar' |
# | ^ | ^ | ^ |
# | "foo" | 'foo' | "foo" | replace quotes if already quoted
# | ^ | ^ | ^ |
# | 'foo' | "foo" | 'foo' | replace quotes if already quoted
# | ^ | ^ | ^ |
# | foo\" | 'foo"' | "foo\"" | single quotes because it's shorter
# | ^ | ^ | ^ |
# | "foo | "foo" | 'foo' |
# | ^ | ^ | ^ |
# | 'foo | 'foo' | "foo" | single quotes to match
# | ^ | ^ | ^ |
# | foo" | "foo" | 'foo' |
# | ^ | ^ | ^ |
# | foo' | 'foo' | "foo" |
# | ^ | ^ | ^ |
# | foo" | "foo" | 'foo' |
# | ^ | ^ | ^ |
emulate -L zsh
local reply
-z4h-find-prev-zword || return 0
local -i start='reply[1]'
(( start <= 0 )) && return
local -i end='reply[2]'
local word=$BUFFER[start,end]
local -i q
if -z4h-is-valid-list ": $word'"; then
word+="'"
local -i q=1
elif -z4h-is-valid-list ": $word\""; then
word+='"'
local -i q=2
fi
local unquoted=${(Q)word}
local quoted=(${(qq)unquoted} ${(qqq)unquoted})
if (( ! q )); then
if [[ $quoted[1] != $word ]] && (( $#quoted[1] < $#quoted[2] )) ||
[[ $quoted[2] == $word ]]; then
(( q = 1 ))
else
(( q = 2 ))
fi
fi
-z4h-replace-buf $start $end $quoted[q]