Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: strictly literal command lines?



On Nov 4,  1:01am, Richard Hartmann wrote:
}
} While url-quote-magic is extremely handy, a way to force literal
} interpretation could make sense and ease handling, sometimes.

Which is why the shell grammar has quotation marks. ;-)

More seriously, what does "literal interpretation" mean?  If it
includes whitespace, then how do you distinguish words (especially
the command word from the argument words)?  Does it include only
command separators, or is it any non-whitespace?  Do quotes become
literal as well -- in which case whitespace inside what appears to
be a quoted string becomes neither quoted nor literal?  If quotes
don't become literal, what happens to variable references inside
double quotes inside "literal interpretation"?  What happens to
backslashes and the characters following backslashes?

Nevertheless, here's a widget that you might find handy.  I tied it
to the CSH_JUNKIE_QUOTES option because when that option is not set,
an unbalanced quote should continue the word on the next line; but
you can easily remove that dependency if you prefer.

    accept-line-and-close-quote () {
	if [[ -o cshjunkiequotes ]]
	then
	    local L="$({: ${(XQ)BUFFER}} 2>&1)"
	    case $L in
		(* unmatched \") BUFFER+=\"  ;;
		(* unmatched \') BUFFER+=\'  ;;
	    esac
	fi
	zle .accept-line "$@"
    }
    zle -N accept-line accept-line-and-close-quote

This lets you type only the first quote and automatically matches it
when you accept the line.

Or maybe you'd rather type only the second quote ... this does not need
any special options set (though I don't promise that some settings won't
break it).

    autoload match-words-by-style
    magic-matching-quote () {
	setopt localoptions no_ksharrays
	zle .self-insert "$@" || return
	local Q="${LBUFFER[-1]}"
	if [[ "$Q" = [\"\'] ]]
	then
	    local curcontext=":zle:$WIDGET"
	    match-words-by-style
	    (( ${#matched_words[2]} > 1 )) || return 0
	    local L="$({: ${(XQ)matched_words[2]}} 2>&1)"
	    if [[ "$L" = *unmatched* ]]
	    then
		matched_words[2]="$Q$matched_words[2]" 
	    fi
	    LBUFFER="${(j::)matched_words[1,3]}" 
	fi
    }

    zstyle ':zle:magic-matching-quote' word-style whitespace
    bindkey \" magic-matching-quote
    bindkey \' magic-matching-quote

Note that magic-matching-quote works only with "whitespace" or "shell"
word-styles, as the other styles parse out the quotes.  (You may be
able to adjust this with skip-chars and other styles.)  Using this
widget makes it rather difficult to insert quotes in the middles of
strings.

So here's another alternative, which extends magic-space to cause it
to recognize an immediately-preceding unbalanced quote and match it.
This makes it simpler to type mixtures of quotes, but probably still
gets some cases wrong.

    magic-space-match-quote () {
	setopt localoptions no_ksharrays
	local Q="${LBUFFER[-1]}"
	zle .magic-space "$@" || return
        if [[ "$Q" == [\"\'] ]]
	then
	    local curcontext=":zle:$WIDGET"
	    match-words-by-style
	    (( ${#matched_words[2]} > 1 )) || return 0
	    local L="$({: ${(XQ)matched_words[2]}} 2>&1)"
	    if [[ "$L" == *" unmatched $Q" ]]
	    then
		matched_words[2]="$Q$matched_words[2]"
	    fi
	    LBUFFER="${(j::)matched_words[1,3]}"
	fi
    }
    zle -N magic-space magic-space-match-quote
    bindkey ' ' magic-space

    zstyle ':zle:magic-space' word-style whitespace

I don't recommend using accept-line-and-close-quote in combination with
either of these magic-* functions; depending on where on the line your
mismatched quote is, simply closing it at end of line might radically
change your word boundaries.  You can probably figure out how to adapt
magic-space-match-quote to be a stand-in for accept-line.

-- 



Messages sorted by: Reverse Date, Date, Thread, Author