summaryrefslogtreecommitdiff
path: root/plugins/git-escape-magic/git-escape-magic
blob: 94a8d7b0fc0862ec9f364d4fbbba8d4022ba348c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# -*- 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 "$@"