From d4256853bf5f98fb6196dba9ec3a5b7878988674 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 20 Feb 2019 16:41:22 +0000 Subject: [PATCH 01/14] Add missing semicolon --- h5glance/copypath.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5glance/copypath.js b/h5glance/copypath.js index 8f58160..38e1938 100644 --- a/h5glance/copypath.js +++ b/h5glance/copypath.js @@ -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); }); From c6cd536cba75b2a57fb6c15f413b0a798dd20614 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 20 Feb 2019 17:45:13 +0000 Subject: [PATCH 02/14] Tab completions in the shell for bash --- h5glance/completer.py | 62 ++++++++++++++++++++++++++++++++++++++++ h5glance/completion.bash | 24 ++++++++++++++++ h5glance/terminal.py | 34 ++-------------------- pyproject.toml | 1 + 4 files changed, 90 insertions(+), 31 deletions(-) create mode 100644 h5glance/completer.py create mode 100755 h5glance/completion.bash diff --git a/h5glance/completer.py b/h5glance/completer.py new file mode 100644 index 0000000..206b582 --- /dev/null +++ b/h5glance/completer.py @@ -0,0 +1,62 @@ +"""Tab completion of paths inside an HDF5 file""" +import h5py +import sys + +class H5Completer: + """Readline tab completion for paths inside an HDF5 file""" + def __init__(self, file: h5py.File): + self.file = file + self.cache = (None, []) + + def completions(self, text: str): + if text == self.cache[0]: + return self.cache[1] + prev_path, _, prefix = text.rpartition('/') + group = self.file + if prev_path: + group = self.file[prev_path] + prev_path += '/' + res = [prev_path + k + ('/' if isinstance(v, h5py.Group) else '') + for (k, v) in group.items() + if k.lower().startswith(prefix.lower())] + self.cache = (text, res) + return res + + def rlcomplete(self, text: str, state: int): + # print(repr(text), state) + try: + res = self.completions(text) + except Exception as e: + #print(e) + return None + # print(repr(text), len(res)) + if state >= len(res): + return None + return res[state] + +def main(): + """Called by shells to generate completions""" + filename, prefix = sys.argv[1:] + f = h5py.File(filename, 'r') + for completion in H5Completer(f).completions(prefix): + print(completion) + +def install_hooks(): + """Install hooks for bash to complete paths in files""" + import os, shutil + pjoin = os.path.join + pkgdir = os.path.dirname(os.path.abspath(__file__)) + data_home = os.environ.get('XDG_DATA_HOME', '') \ + or os.path.expanduser('~/.local/share') + + # 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) + + +if __name__ == '__main__': + install_hooks() diff --git a/h5glance/completion.bash b/h5glance/completion.bash new file mode 100755 index 0000000..4874c1a --- /dev/null +++ b/h5glance/completion.bash @@ -0,0 +1,24 @@ +#!/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 + COMPREPLY=( $(_h5glance_complete_infile "${prev}" "${cur}") ) + return 0 + fi +} + +complete -o default -o nospace -F _h5glance h5glance diff --git a/h5glance/terminal.py b/h5glance/terminal.py index 57d8573..4fcc957 100644 --- a/h5glance/terminal.py +++ b/h5glance/terminal.py @@ -12,6 +12,8 @@ from subprocess import run import sys +from .completer import H5Completer + def fmt_dtype(dtype): if dtype.metadata and 'vlen' in dtype.metadata: base_dtype = dtype.metadata['vlen'] @@ -231,37 +233,7 @@ def display_h5_obj(file: h5py.File, path=None, expand_attrs=False): print(output) -class H5Completer: - """Readline tab completion for paths inside an HDF5 file""" - def __init__(self, file: h5py.File): - self.file = file - self.cache = (None, []) - - def completions(self, text: str): - if text == self.cache[0]: - return self.cache[1] - prev_path, _, prefix = text.rpartition('/') - group = self.file - if prev_path: - group = self.file[prev_path] - prev_path += '/' - res = [prev_path + k + ('/' if isinstance(v, h5py.Group) else '') - for (k, v) in group.items() - if k.lower().startswith(prefix.lower())] - self.cache = (text, res) - return res - - def rlcomplete(self, text: str, state: int): - # print(repr(text), state) - try: - res = self.completions(text) - except Exception as e: - #print(e) - return None - # print(repr(text), len(res)) - if state >= len(res): - return None - return res[state] + def prompt_for_path(filename): """Prompt the user for a path inside the HDF5 file""" diff --git a/pyproject.toml b/pyproject.toml index 7aba7b5..1c268da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,3 +18,4 @@ classifiers = ["License :: OSI Approved :: BSD License"] [tool.flit.scripts] h5glance = "h5glance.terminal:main" h5glance-html = "h5glance.html_cli:main" +_h5glance_complete_infile = "h5glance.completer:main" From d93155137c058002c031f67c9db8f4f80a41ee5e Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 20 Feb 2019 19:39:53 +0000 Subject: [PATCH 03/14] First try at zsh completion --- h5glance/completer.py | 30 +++++++++++++++++++++++------- h5glance/completion.zsh | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 h5glance/completion.zsh diff --git a/h5glance/completer.py b/h5glance/completer.py index 206b582..3890bf5 100644 --- a/h5glance/completer.py +++ b/h5glance/completer.py @@ -43,19 +43,35 @@ def main(): def install_hooks(): """Install hooks for bash to complete paths in files""" - import os, shutil + import os, shutil, subprocess pjoin = os.path.join pkgdir = os.path.dirname(os.path.abspath(__file__)) data_home = os.environ.get('XDG_DATA_HOME', '') \ or os.path.expanduser('~/.local/share') # 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) + 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.bash') + 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)\n'.format(dst_dir)) + print("Added {} to fpath in ~/.zshrc".format(dst_dir)) if __name__ == '__main__': diff --git a/h5glance/completion.zsh b/h5glance/completion.zsh new file mode 100644 index 0000000..33d0929 --- /dev/null +++ b/h5glance/completion.zsh @@ -0,0 +1,28 @@ +#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 matching_paths + matching_paths=($(_h5glance_complete_infile "${line[1]}" "${line[2]}")) + _describe "paths in file" matching_paths + ;; + ecas +} + +function _h5glance_infile { + +} From 66001f63e354add868b9680bf3d9c033a18a099b Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 20 Feb 2019 19:44:12 +0000 Subject: [PATCH 04/14] Fix path to zsh completer --- h5glance/completer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5glance/completer.py b/h5glance/completer.py index 3890bf5..7745eb2 100644 --- a/h5glance/completer.py +++ b/h5glance/completer.py @@ -60,7 +60,7 @@ def install_hooks(): # Zsh if shutil.which('zsh'): - src = pjoin(pkgdir, 'completion.bash') + src = pjoin(pkgdir, 'completion.zsh') dst_dir = pjoin(data_home, 'zsh-completions') os.makedirs(dst_dir, exist_ok=True) dst = pjoin(dst_dir, '_h5glance') From b7e05d9b7b842cd30e3abf03fb39cbb8280a6666 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 20 Feb 2019 19:45:57 +0000 Subject: [PATCH 05/14] Remove empty function --- h5glance/completion.zsh | 3 --- 1 file changed, 3 deletions(-) diff --git a/h5glance/completion.zsh b/h5glance/completion.zsh index 33d0929..8c8682d 100644 --- a/h5glance/completion.zsh +++ b/h5glance/completion.zsh @@ -23,6 +23,3 @@ function _h5glance { ecas } -function _h5glance_infile { - -} From 26d2cc41c562a05be0c45c0bd434b9cc048b1315 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 10:00:08 +0000 Subject: [PATCH 06/14] Fix case syntax --- h5glance/completion.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5glance/completion.zsh b/h5glance/completion.zsh index 8c8682d..8dbc7ba 100644 --- a/h5glance/completion.zsh +++ b/h5glance/completion.zsh @@ -20,6 +20,6 @@ function _h5glance { matching_paths=($(_h5glance_complete_infile "${line[1]}" "${line[2]}")) _describe "paths in file" matching_paths ;; - ecas + esac } From d316064a8b455bbc83e698771bce63f3bc26caed Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 10:03:12 +0000 Subject: [PATCH 07/14] Re-initialise zsh completions after adding directory to fpath --- h5glance/completer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5glance/completer.py b/h5glance/completer.py index 7745eb2..53341d2 100644 --- a/h5glance/completer.py +++ b/h5glance/completer.py @@ -70,7 +70,7 @@ def install_hooks(): 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)\n'.format(dst_dir)) + f.write('\nfpath=("{}" $fpath)\ncompinit\n'.format(dst_dir)) print("Added {} to fpath in ~/.zshrc".format(dst_dir)) From 991be5e0e9a04e325129598a43d61ea79e6d66c6 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 12:51:54 +0000 Subject: [PATCH 08/14] Case insensitive completions in zsh --- h5glance/completion.zsh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/h5glance/completion.zsh b/h5glance/completion.zsh index 8dbc7ba..064357b 100644 --- a/h5glance/completion.zsh +++ b/h5glance/completion.zsh @@ -17,9 +17,10 @@ function _h5glance { case "$state" in infile) declare -a matching_paths + # Case insensitive matching: + zstyle ":completion::complete:h5glance:argument-2:*" matcher 'm:{a-z}={A-Z}' matching_paths=($(_h5glance_complete_infile "${line[1]}" "${line[2]}")) _describe "paths in file" matching_paths ;; esac } - From 3c81c4a6ef8e1649bd4ebc1d7022596e8f6917c3 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 13:20:54 +0000 Subject: [PATCH 09/14] Fix spurious spaces after HDF5 group names --- h5glance/completion.zsh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/h5glance/completion.zsh b/h5glance/completion.zsh index 064357b..e6b2956 100644 --- a/h5glance/completion.zsh +++ b/h5glance/completion.zsh @@ -5,7 +5,6 @@ function _h5glance { local context state state_descr line typeset -A opt_args - _arguments -C \ "-h[Show help information]" \ "--help[Show help information]" \ @@ -17,10 +16,22 @@ function _h5glance { case "$state" in infile) declare -a matching_paths - # Case insensitive matching: - zstyle ":completion::complete:h5glance:argument-2:*" matcher 'm:{a-z}={A-Z}' matching_paths=($(_h5glance_complete_infile "${line[1]}" "${line[2]}")) - _describe "paths in file" matching_paths + + # 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 $matching_paths; 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 matching_paths ;; esac } From 0d3b6e3d9eb3844b1e51842019bd574ee11b804b Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 16:08:45 +0000 Subject: [PATCH 10/14] Use h5ls for faster completions --- h5glance/completion.bash | 15 +++++++++++++- h5glance/completion.zsh | 44 +++++++++++++++++++++++++--------------- pyproject.toml | 1 - 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/h5glance/completion.bash b/h5glance/completion.bash index 4874c1a..cc05903 100755 --- a/h5glance/completion.bash +++ b/h5glance/completion.bash @@ -16,7 +16,20 @@ _h5glance() # Complete paths inside file if [[ -f ${prev} ]]; then - COMPREPLY=( $(_h5glance_complete_infile "${prev}" "${cur}") ) + 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 } diff --git a/h5glance/completion.zsh b/h5glance/completion.zsh index e6b2956..394afbf 100644 --- a/h5glance/completion.zsh +++ b/h5glance/completion.zsh @@ -15,23 +15,35 @@ function _h5glance { case "$state" in infile) - declare -a matching_paths - matching_paths=($(_h5glance_complete_infile "${line[1]}" "${line[2]}")) + 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 - # 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 $matching_paths; do - if [ "${val: -1:1}" = '/' ]; then - suffix='' - break - fi - done + # 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))' )) - # The -M match-spec argument allows case-insensitive matches - compadd -S "$suffix" -M 'm:{a-zA-Z}={A-Za-z}' -a matching_paths - ;; + # 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 } diff --git a/pyproject.toml b/pyproject.toml index 1c268da..7aba7b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,4 +18,3 @@ classifiers = ["License :: OSI Approved :: BSD License"] [tool.flit.scripts] h5glance = "h5glance.terminal:main" h5glance-html = "h5glance.html_cli:main" -_h5glance_complete_infile = "h5glance.completer:main" From 3aaf43aedb9dcf2fcba24703cd70dc03fff790e4 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 16:40:17 +0000 Subject: [PATCH 11/14] Remove unused completion entry point --- h5glance/completer.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/h5glance/completer.py b/h5glance/completer.py index 53341d2..5e86541 100644 --- a/h5glance/completer.py +++ b/h5glance/completer.py @@ -34,12 +34,6 @@ def rlcomplete(self, text: str, state: int): return None return res[state] -def main(): - """Called by shells to generate completions""" - filename, prefix = sys.argv[1:] - f = h5py.File(filename, 'r') - for completion in H5Completer(f).completions(prefix): - print(completion) def install_hooks(): """Install hooks for bash to complete paths in files""" From b0930238432626dd6b99b08bde26f836bd8b8227 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 21 Feb 2019 16:43:00 +0000 Subject: [PATCH 12/14] Move H5Completer class back into h5glance.terminal module --- h5glance/completer.py | 46 +++++++------------------------------------ h5glance/terminal.py | 34 +++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/h5glance/completer.py b/h5glance/completer.py index 5e86541..2c44afa 100644 --- a/h5glance/completer.py +++ b/h5glance/completer.py @@ -1,45 +1,13 @@ -"""Tab completion of paths inside an HDF5 file""" -import h5py -import sys - -class H5Completer: - """Readline tab completion for paths inside an HDF5 file""" - def __init__(self, file: h5py.File): - self.file = file - self.cache = (None, []) - - def completions(self, text: str): - if text == self.cache[0]: - return self.cache[1] - prev_path, _, prefix = text.rpartition('/') - group = self.file - if prev_path: - group = self.file[prev_path] - prev_path += '/' - res = [prev_path + k + ('/' if isinstance(v, h5py.Group) else '') - for (k, v) in group.items() - if k.lower().startswith(prefix.lower())] - self.cache = (text, res) - return res - - def rlcomplete(self, text: str, state: int): - # print(repr(text), state) - try: - res = self.completions(text) - except Exception as e: - #print(e) - return None - # print(repr(text), len(res)) - if state >= len(res): - return None - return res[state] +"""Shell integreation 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 to complete paths in files""" - import os, shutil, subprocess - pjoin = os.path.join - pkgdir = os.path.dirname(os.path.abspath(__file__)) + """Install hooks for bash & zsh to complete paths in files""" data_home = os.environ.get('XDG_DATA_HOME', '') \ or os.path.expanduser('~/.local/share') diff --git a/h5glance/terminal.py b/h5glance/terminal.py index 4fcc957..57d8573 100644 --- a/h5glance/terminal.py +++ b/h5glance/terminal.py @@ -12,8 +12,6 @@ from subprocess import run import sys -from .completer import H5Completer - def fmt_dtype(dtype): if dtype.metadata and 'vlen' in dtype.metadata: base_dtype = dtype.metadata['vlen'] @@ -233,7 +231,37 @@ def display_h5_obj(file: h5py.File, path=None, expand_attrs=False): print(output) - +class H5Completer: + """Readline tab completion for paths inside an HDF5 file""" + def __init__(self, file: h5py.File): + self.file = file + self.cache = (None, []) + + def completions(self, text: str): + if text == self.cache[0]: + return self.cache[1] + prev_path, _, prefix = text.rpartition('/') + group = self.file + if prev_path: + group = self.file[prev_path] + prev_path += '/' + res = [prev_path + k + ('/' if isinstance(v, h5py.Group) else '') + for (k, v) in group.items() + if k.lower().startswith(prefix.lower())] + self.cache = (text, res) + return res + + def rlcomplete(self, text: str, state: int): + # print(repr(text), state) + try: + res = self.completions(text) + except Exception as e: + #print(e) + return None + # print(repr(text), len(res)) + if state >= len(res): + return None + return res[state] def prompt_for_path(filename): """Prompt the user for a path inside the HDF5 file""" From 880c26e077dc7b37094aa449ff7f432a56e1935f Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 22 Feb 2019 20:50:30 +0000 Subject: [PATCH 13/14] Fix typo --- h5glance/completer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5glance/completer.py b/h5glance/completer.py index 2c44afa..a1af101 100644 --- a/h5glance/completer.py +++ b/h5glance/completer.py @@ -1,4 +1,4 @@ -"""Shell integreation to tab complete paths inside an HDF5 file""" +"""Shell integration to tab complete paths inside an HDF5 file""" import os import shutil import subprocess From 10d381616eacea12b8dff24631bca15ebba1ebcf Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 22 Feb 2019 20:51:55 +0000 Subject: [PATCH 14/14] Mention installing completer hooks in README --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 764727d..f798044 100644 --- a/README.rst +++ b/README.rst @@ -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 `_ shows how to use it.