Skip to content

Commit

Permalink
Merge pull request #8 from European-XFEL/shell-completions
Browse files Browse the repository at this point in the history
Tab completion in bash & zsh
  • Loading branch information
takluyver authored Feb 23, 2019
2 parents 89d5d71 + 10d3816 commit d22fb37
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ completion::
$ h5glance sample.h5 -
Object path: sample.h5/ # try tapping tab

Or run ``python -m h5glance.completer`` to install tab completion hooks for bash
and zsh.

### HTML interface

The HTML interface lets you inspect HDF5 files in a Jupyter Notebook.
`Demo.ipynb <https://nbviewer.jupyter.org/github/European-XFEL/h5glance/blob/master/Demo.ipynb>`_
shows how to use it.
Expand Down
40 changes: 40 additions & 0 deletions h5glance/completer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Shell integration to tab complete paths inside an HDF5 file"""
import os
import shutil
import subprocess

pjoin = os.path.join
pkgdir = os.path.dirname(os.path.abspath(__file__))

def install_hooks():
"""Install hooks for bash & zsh to complete paths in files"""
data_home = os.environ.get('XDG_DATA_HOME', '') \
or os.path.expanduser('~/.local/share')

# Bash
if shutil.which('bash'):
src = pjoin(pkgdir, 'completion.bash')
dst_dir = pjoin(data_home, 'bash-completion/completions')
os.makedirs(dst_dir, exist_ok=True)
dst = pjoin(dst_dir, 'h5glance')
shutil.copy(src, dst)
print("Copied", dst)

# Zsh
if shutil.which('zsh'):
src = pjoin(pkgdir, 'completion.zsh')
dst_dir = pjoin(data_home, 'zsh-completions')
os.makedirs(dst_dir, exist_ok=True)
dst = pjoin(dst_dir, '_h5glance')
shutil.copy(src, dst)
print("Copied", dst)

stdout = subprocess.check_output(['zsh', '-i', '-c', 'echo $FPATH'])
if dst_dir not in stdout.decode('utf-8').split(':'):
with open(os.path.expanduser('~/.zshrc'), 'a') as f:
f.write('\nfpath=("{}" $fpath)\ncompinit\n'.format(dst_dir))
print("Added {} to fpath in ~/.zshrc".format(dst_dir))


if __name__ == '__main__':
install_hooks()
37 changes: 37 additions & 0 deletions h5glance/completion.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash

_h5glance()
{
local cur prev opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"

# Complete options
if [[ ${cur} = -* ]]; then
opts="-h --help --version --attrs"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi

# Complete paths inside file
if [[ -f ${prev} ]]; then
local grouppath=""
if [[ ${cur} =~ / ]]; then
# Hack: 'dirname a/b/' returns 'a'. The trailing x makes it 'a/b'.
grouppath=$(dirname "${cur}x")/
fi

# List entries in the group, add the group path and a / suffix for
# subgroups, and case-insensitively filter them against the text entered.
COMPREPLY=($(h5ls --simple "${prev}/${grouppath}" \
| awk -v g="${grouppath}" \
'{sfx=" "; if ($2 == "Group") sfx="/"; print g $1 sfx}' \
| awk -v IGNORECASE=1 -v p="${cur}" \
'p==substr($0,0,length(p))' \
) )
return 0
fi
}

complete -o default -o nospace -F _h5glance h5glance
49 changes: 49 additions & 0 deletions h5glance/completion.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#compdef _h5glance h5glance

function _h5glance {
local curcontext="$curcontext"
local context state state_descr line
typeset -A opt_args

_arguments -C \
"-h[Show help information]" \
"--help[Show help information]" \
"--version[Show version number]" \
"--attrs[Show attributes of groups]" \
":HDF5 file:_files" \
":path in file:->infile"

case "$state" in
infile)
declare -a matches
local grouppath=""
if [[ ${line[2]} =~ / ]]; then
# Hack: 'dirname a/b/' returns 'a'. The trailing x makes it 'a/b'.
grouppath=$(dirname "${line[2]}x")/
fi

# List entries in the group, add the group path and a / suffix for
# subgroups, and case-insensitively filter them against the text entered.
matches=($(h5ls --simple "${line[1]}/${grouppath}" \
| awk -v g="${grouppath}" \
'{s=""; if ($2 == "Group") s="/"; print g $1 s}' \
| awk -v IGNORECASE=1 -v p="${line[2]}" \
'p==substr($0,0,length(p))' ))

# Code below by Xavier Delaruelle, on StackOverflow.
# https://stackoverflow.com/a/53907053/434217
# Used under SO's default CC-BY-SA-3.0 license.
local suffix=' ';
# do not append space to word completed if it is a directory (ends with /)
for val in $matches; do
if [ "${val: -1:1}" = '/' ]; then
suffix=''
break
fi
done

# The -M match-spec argument allows case-insensitive matches
compadd -S "$suffix" -M 'm:{a-zA-Z}={A-Za-z}' -a matches
;;
esac
}
2 changes: 1 addition & 1 deletion h5glance/copypath.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
}

function enable_copylinks(parent) {
let links = parent.querySelectorAll(".h5glance-dataset-copylink")
let links = parent.querySelectorAll(".h5glance-dataset-copylink");
links.forEach(function (link) {
link.addEventListener("click", copy_event_handler);
});
Expand Down

0 comments on commit d22fb37

Please sign in to comment.