aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--README.adoc120
-rwxr-xr-xsdn-install176
-rw-r--r--sdn.cpp1
4 files changed, 188 insertions, 110 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9c6d06b..e6cba04 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,7 @@ target_compile_definitions (${PROJECT_NAME} PUBLIC
include (GNUInstallDirs)
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
+install (PROGRAMS ${PROJECT_NAME}-install DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Directory navigator")
diff --git a/README.adoc b/README.adoc
index bedacd6..9c162a6 100644
--- a/README.adoc
+++ b/README.adoc
@@ -47,117 +47,17 @@ into the PATH of any machine you want to have 'sdn' on.
Integration
-----------
+The package contains an installation script called 'sdn-install' which will bind
+'sdn' to M-o in your shell's initialisation file. The supported shells are:
-zsh
-~~~
-To start using this navigator, put the following in your '.zshrc':
-
-----
-sdn-navigate () {
- # ... possibly zle-line-finish
- while eval "`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
-
- /bin/sh -c "$helper" </dev/tty || break
- done
- # ... possibly zle-line-init
- zle reset-prompt
-}
-zle -N sdn-navigate
-bindkey '\eo' sdn-navigate
-----
-
-bash
-~~~~
-Here 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 "$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
- /bin/sh -c "$helper" || break
- done
-}
-sdn-restore () {
- READLINE_LINE=$SDN_L READLINE_POINT=$SDN_P
- unset SDN_L SDN_P
-}
-
-bind -x '"\200": sdn-navigate'
-bind -x '"\201": sdn-restore'
-bind '"\eo":"\200\C-m\201"'
-----
-
-fish
-~~~~
-To start using this navigator, put the following in your 'config.fish':
-
-----
-function sdn-navigate
- set --local IFS
- set --local buffer (commandline)
- set --local cursor (commandline --cursor)
- while eval (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
-----
-
-elvish
-~~~~~~
-To start using this navigator, put the following in your 'rc.elv':
-
-----
-use str
-edit:insert:binding[Alt-o] = {
- 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
- local:buffer = $edit:current-command
- local:cursor = (str:to-codepoints $buffer[0..$edit:-dot] | count)
- local:ns = (ns [&])
- while ?(eval ($reesc (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
-}
-----
-
-This shell is absolutely perverse. And so is integrating 'sdn' into it because
-it already includes a custom file manager, bound to Ctrl-N (though I find
-the ranger-like interface confusing and resource-demanding). Version 0.14.1 or
-newer is required.
+ - *zsh*: works well
+ - *bash*: minor issue: exiting the navigator confirms an empty prompt
+ - *fish*: works well
+ - *elvish*: version 0.14.1 and above, an unstable API is used, works well
+
+elvish is absolutely perverse. And so is integrating 'sdn' into it because it
+already includes a custom file manager, bound to Ctrl-N (though I find the
+ranger-like interface confusing and resource-demanding).
Configuration
-------------
diff --git a/sdn-install b/sdn-install
new file mode 100755
index 0000000..954b061
--- /dev/null
+++ b/sdn-install
@@ -0,0 +1,176 @@
+#!/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 () {
+ # ... possibly 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
+
+ /bin/sh -c "$helper" </dev/tty || break
+ done
+ # ... possibly 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
+ /bin/sh -c "$helper" || break
+ done
+}
+
+sdn-restore () {
+ READLINE_LINE=$SDN_L READLINE_POINT=$SDN_P
+ unset SDN_L SDN_P
+}
+
+bind -x '"\200": sdn-navigate'
+bind -x '"\201": sdn-restore'
+bind '"\eo":"\200\C-m\201"'
+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
+ 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)
+if [ -z "$shell" ]
+then
+ if [ "$login" != "$actual" ]
+ then
+ echo "Conflict between login ($actual) 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."
diff --git a/sdn.cpp b/sdn.cpp
index ff1e397..5222df8 100644
--- a/sdn.cpp
+++ b/sdn.cpp
@@ -1717,6 +1717,7 @@ int main (int argc, char *argv[]) {
// We can't portably create a standard stream from an FD, so modify the FD
dup2 (output_fd, STDOUT_FILENO);
+ // TODO: avoid printing any of this unless the SDN envvar is set
if (g.cwd != g.start_dir && !g.no_chdir)
cout << "local cd=" << shell_escape (g.cwd) << endl;
else