# -*- mode: sh -*- # # git-escape-magic - zle tweak for git command line arguments # # Copyright (c) 2011, 2012, 2014 Akinori MUSHA # Licensed under the 2-clause BSD license. # # This tweak eliminates the need for manually escaping shell # meta-characters such as [~^{}] that are used for specifying a git # object (commit or tree). Every time you type one of these # characters on a git command line, it is automatically escaped with a # backslash as necessary and as appropriate. # # If you want to use this with url-quote-magic, make sure to enable it # first. # # Usage: # autoload -Uz git-escape-magic # git-escape-magic # git-escape-magic.self-insert() { emulate -L zsh setopt extendedglob local self_insert_function zstyle -s ':git-escape-magic' self-insert-function self_insert_function if [[ "$KEYS" == [{}~^]* ]] && { local qkey="${(q)KEYS}" [[ "$KEYS" != "$qkey" ]] } && { local lbuf="$LBUFFER$qkey" [[ "${(Q)LBUFFER}$KEYS" == "${(Q)lbuf}" ]] } && { local -a words words=("${(@Q)${(z)lbuf}}") [[ "$words[(i)(*/|)git(|-[^/]##)]" -le $#words ]] } then local i i="$words[(I)([;(){\&]|\&[\&\!]|\|\||[=<>]\(*)]" if [[ $i -gt 0 ]]; then shift $((i-1)) words if [[ "$words[1]" == [\=\<\>]\(* ]]; then words[1]="${words[1]#[=<>]\(}" else [[ "$words[1]" == \; && $words[2] == (then|else|elif|do) ]] && shift words shift words fi fi while [[ "$words[1]" == (if|while|until|\!) ]]; do shift words done while [[ "$words[1]" == [A-Za-z_][A-Za-z0-9_]#=* ]]; do shift words done [[ "$words[1]" == (*/|)git(|-[^/]##) ]] && { local subcommand subcommand="${words[1]##*/git-}" if [[ -z "$subcommand" ]]; then shift words subcommand="$words[1]" fi [[ $#words -ge 2 ]] } && case "$subcommand" in # commands that may take pathspec but never take refspec with [{}~^] (add|rm|am|apply|check-attr|checkout-index|clean|clone|config|diff-files|hash-object|help|index-pack|mailinfo|mailsplit|merge-file|merge-index|mergetool|mktag|mv|pack-objects|pack-redundant|relink|send-email|show-index|show-ref|stage|status|verify-pack) false ;; # commands that may take pathspec but rarely take refspec with [{}~^] (for-each-ref|grep|ls-files|update-index) false ;; (archive|ls-tree) ! [[ $#words -ge 3 && "$words[-2]" == [^-]* ]] ;; (diff-tree) ! [[ $#words -ge 4 && "$words[-2]" == [^-]* && "$words[-3]" == [^-]* ]] ;; (*) [[ $words[(i)--] -gt $#words ]] ;; esac && case "${words[-1]%%"$KEYS"}" in (*[@^]) [[ "$KEYS" == [{~^]* ]] ;; (*[@^]\{[^}]##) [[ "$KEYS" == \}* ]] ;; (?*) [[ "$KEYS" == [~^]* ]] ;; (*) false ;; esac && LBUFFER="$LBUFFER\\" fi zle "$self_insert_function" } git-escape-magic.on() { emulate -L zsh local self_insert_function="${$(zle -lL | awk \ '$1=="zle"&&$2=="-N"&&$3=="self-insert"{print $4;exit}'):-.self-insert}" [[ "$self_insert_function" == git-escape-magic.self-insert ]] && return 0 # For url-quote-magic which does not zle -N itself zle -la "$self_insert_function" || zle -N "$self_insert_function" zstyle ':git-escape-magic' self-insert-function "$self_insert_function" zle -A git-escape-magic.self-insert self-insert return 0 } git-escape-magic.off() { emulate -L zsh local self_insert_function zstyle -s ':git-escape-magic' self-insert-function self_insert_function [[ -n "$self_insert_function" ]] && zle -A "$self_insert_function" self-insert return 0 } zle -N git-escape-magic.self-insert zle -N git-escape-magic.on zle -N git-escape-magic.off git-escape-magic() { git-escape-magic.on } [[ -o kshautoload ]] || git-escape-magic "$@"