183 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/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."
 |