aboutsummaryrefslogtreecommitdiff
path: root/sdn-install
blob: 788c8bf8823324955e5c6fd0a1d002f2ec95c9b0 (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/bin/sh -e
# sdn-install: integrate sdn with the shell, binding to M-o
# vim: set sw=2 ts=2 sts=2 et tw=80:

zsh() {
cat <<'EOF'
sdn-navigate () {
  # optionally: zle zle-line-finish
  while eval "`SDN=1 sdn "$BUFFER" "$CURSOR"`"
  do
    [ -z "$cd" ] || cd "$cd"
    [ -z "$insert" ] || LBUFFER="$LBUFFER$insert "
    [ -z "$helper" ] && break

    # Workaround for "zsh: suspended (tty output)" when invoking
    # helpers after the terminal has been resized while running sdn
    command true

    # Add to history, see https://www.zsh.org/mla/workers/2020/msg00633.html
    fc -R =(print -- "$helper")

    /bin/sh -c "$helper" </dev/tty || break
  done
  # optionally: zle zle-line-init
  zle reset-prompt
}

zle -N sdn-navigate
bindkey '\eo' sdn-navigate
EOF
}

bash() {
cat <<'EOF'
# We can't make the shell update the prompt on directory changes
# since there's no way to invoke `prompt_again()` from a `bind -x`
# handler but we can work around it by submitting a blank line.
sdn-cursor () {
  if [[ $BASH_VERSINFO -lt 5 ]]
  then echo -n "$SDN_L" | wc -m
  else echo "$SDN_P"
  fi
}

sdn-navigate () {
  SDN_L=$READLINE_LINE SDN_P=$READLINE_POINT
  READLINE_LINE=

  while eval "`SDN=1 sdn "$SDN_L" "$(sdn-cursor)"`"
  do
    [[ -z $cd ]] || cd "$cd"
    [[ -z $insert ]] || {
      SDN_L="${SDN_L:0:$SDN_P}$insert ${SDN_L:$SDN_P}"
      ((SDN_P=SDN_P+${#insert}+1))
    }
    [[ -z $helper ]] && break
    history -s -- "$helper"
    /bin/sh -c "$helper" || break
  done
}

sdn-restore () {
  READLINE_LINE=$SDN_L READLINE_POINT=$SDN_P
  unset SDN_L SDN_P
}

# These never occur in UTF-8: \300-\301 \365-\367 \370-\377
bind -x '"\300": sdn-navigate'
bind -x '"\301": sdn-restore'
bind '"\eo": "\300\C-m\301"'
EOF
}

fish() {
cat <<'EOF'
function sdn-navigate
  set --local IFS
  set --local buffer (commandline)
  set --local cursor (commandline --cursor)
  while eval (SDN=1 sdn $buffer $cursor | \
    string replace -ar '^(.*?)=' 'set --$1 ')
    test -z "$cd" || cd "$cd"
    test -z "$insert" || commandline --insert "$insert "
    test -z "$helper" && break
    /bin/sh -c "$helper" || break
  end
  commandline --function repaint
end
bind \eo sdn-navigate
EOF
}

elvish() {
cat <<'EOF'
edit:insert:binding[Alt-o] = {
  use str
  local:reesc = [posix]{ str:replace "'\\''"  "''" $posix }
  local:posix = [cmd]{ /bin/sh -c $cmd </dev/tty >/dev/tty 2>&1 }

  # XXX: the -dot is not a stable API, and may hence break soon
  # https://elv.sh/ref/builtin.html#do-not-use-functions-and-variables
  local:buffer = $edit:current-command
  local:cursor = (str:to-codepoints $buffer[0..$edit:-dot] | count)
  local:ns = (ns [&])
  while ?(eval ($reesc (E:SDN=1 sdn $buffer $cursor |
    sed 's/^local //' | slurp)) &ns=$ns) {
    if (not-eq $ns[cd] "") { cd $ns[cd] }
    if (not-eq $ns[insert] "") { edit:insert-at-dot $ns[insert]" " }
    if (or (eq $ns[helper] "") (not ?($posix $ns[helper]))) { break }
  }
  edit:redraw &full=$true
}
EOF
}

shell= path=
while getopts s:f:h opt
do
  case $opt in
  s) shell=$OPTARG;;
  f) path=$OPTARG;;
  *) echo "Usage: $0 [-s SHELL] [-f RCPATH | -]"; exit 2
  esac
done

# Figure out the shell to integrate with
login=$(basename "$SHELL")
actual=$(ps -p $$ -o ppid= | xargs ps -o comm= -p | sed 's/^-//')
if [ -z "$shell" ]
then
  if [ "$login" != "$actual" ]
  then
    echo "Conflict between login ($login) and invoking ($actual) shell."
    echo "Specify the shell with the -s option."
    exit 1
  fi
  shell=$actual
fi

# Figure out the default initialisation file
case "$shell" in
zsh|bash)
  rc=~/.${shell}rc;;
fish)
  rc=${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/sdn.fish;;
elvish)
  rc=~/.elvish/rc.elv;;
*)
  echo "$shell is not supported."
  exit 1
esac

# Just print out the snippet if requested
if [ "$path" = "-" ]
then
  $shell
  exit 0
fi

# Finally append to or update the appropriate file
b="# sdn-install begin"
e="# sdn-install end"
[ -z "$path" ] && path=$rc
mkdir -p "$(dirname "$path")"
touch "$path"

if ! grep -q "^$b" "$path" 2>/dev/null || ! grep -q "^$e" "$path" 2>/dev/null
then
  printf "\n$b\n%s\n$e\n" "$($shell)" >> "$path"
  echo "The snippet has been added to $path"
  exit 0
fi

# POSIX-compliant in-place sed, trying to retain permissions here
temp=$path.sdn.new
cp -p -- "$path" "$temp"
sed < "$path" > "$temp" "/^$b/,/^$e/c\\
$b\\
$($shell | sed 's/\\/&&/g; s/$/\\/')
$e"
mv -- "$temp" "$path"
echo "The snippet in $path has been updated."