tabtab
A node package to do some custom command line<tab><tab>
completion for any
system command, for Bash, Zsh, and Fish shells.
Made possible using the same technique as npm (whose completion is quite awesome) relying on a shell script bridge to do the actual completion from node's land.
- Supports zsh, fish and bash
- CLI tool to manage and discover completion.
- Automatic completion from
package.json
config - Or using an EventEmitter based API
- Manual or Automatic installation using system dirs (ex.
/usr/share/bash-completion/completions
for bash). - A way to install completion script for a given shell on
npm install
, gently asking the user for install location.tabtab install
in package.json install script creates the completion file on user system.
Install
npm install tabtab --save
Documentation
API
You can add completion pretty easily in your node cli script:
#! /usr/bin/env node
// Ex. bin/ entry point for a "program" package
var tab = require('tabtab')({
name: 'program'
});
// General handler. Gets called on `program <tab>` and `program stuff ... <tab>`
tab.on('program', function(data, done) {
// General handler
done(null, ['foo', 'bar']);
});
// Specific handler. Gets called on `program list <tab>`
tab.on('list', function(data, done) {
done(null, ['file.js', 'file2.js']);
});
// Start must be called to register the completion commands and listen for
// completion.
tab.start();
These events are emitted whenever the command program completion -- ..
is
triggered, with special COMP_*
environment variables.
tab.start()
will define one command: completion
for your program, which is
used by the Shell completion scripts.
The data
object holds interesting value to drive the output of the
completion:
line
: full command being completedwords
: number of wordpoint
: cursor positionpartial
: tabing in the middle of a word: foo bar baz bar foobarrrrrrrlast
: last word of the linelastPartial
: last partial of the lineprev
: the previous word
Options
var tab = require('tabtab')({
// the String package name being completed, defaults to process.title
// (if not node default) or will attempt to determine parent's
// package.json location and extract the name from it.
name: 'foobar'
// Enable / Disable cache (defaults: true)
cache: true,
// Cache Time To Live duration in ms (default: 5min)
ttl: 1000 * 60 * 5
});
Completion results are cached by default, for a duration of 5 minutes. Caching
is based on the value of the full command being completed (data.line
).
Completion description
Only supported with zsh or fish, tabtab offers the ability to define per command / options description by adding them preceded by a colon, in the form of:
'command:description for command'
This way, you can define descriptions for a specific completion item and tabtab will configure zsh / fish to show them right to the completion item.
Example for zsh
$ program <tab>
command - description for command
Example for fish
$ program <tab>
command (description for command)
package.json
While the EventEmitter API can offer fine control over what gets completed,
completion values can be defined directly in the package.json
file, using the
tabtab
property:
{
"tabtab": {
"nvm": ["help", "use", "install", "uninstall", "run", "current", "ls", "ls-remote"],
"use": ["stable", "default", "iojs", "v5.11.0", "v6.0.0"]
}
}
This still requires to initialize tabtab with:
require('tabtab')().start();
Completions
For any installation method described below, be sure to reload your current
shell configuration file by sourcing it (ex. for bash: source ~/.bashrc
), or
opening a new shell.
Manual Installation
Manually loading the completion for your cli app is done very much like npm does:
. <(tabtab install --stdout --name program)
It'll enables tab-completion for the program
executable. Adding it to your
~/.bashrc or ~/.zshrc will make the completions available everywhere (not only
the current shell).
tabtab install --stdout --name program >> ~/.bashrc # or ~/.zshrc
This requires an additional manual step for the user. Ideally we'd want it to be automatic, and define it at a system-level.
Automatic Installation
For completions to be active for a particular command/program, the user shell (bash, zsh or fish) must load a specific file when the shell starts.
Each shell have its own system, and different loading paths. tabtab
tries to
figure out the most appropriate directory depending on the $SHELL
variable.
- fish Uses
~/.config/fish/completions
- zsh Uses
/usr/local/share/zsh/site-functions
- bash Asks
pkg-config
for completion directories if bash-completion is installed (defaults to/usr/share/bash-completion/completions
and/etc/bash_completion.d
)
tabtab
CLI provides an install
command to ease the process of installing a
completion script when the package is installed on the user system, using npm
script.
npm script:install
Using npm's install/uninstall script, you can automatically manage completion for your program whenever it gets globally installed or removed.
{
"scripts": {
"install": "tabtab install"
}
}
On install, npm will execute the tabtab install
command automatically in the
context of your package.
Ex.
{
"name": "foobar",
"bin": "bin/foobar",
"scripts": {
"install": "tabtab install"
},
"dependencies": {
"tabtab": "^1.0.0"
}
}
Nothing is done's without asking confirmation, tabtab install
looks at the
$SHELL
variable to determine the best possible locations and uses
Inquirer to ask the user what it
should do:
tabtab install --auto
The --auto
flag can be used to bypass prompts and use the SHELL configuration file options by default:
- bash: Will use
~/.bashrc
- zsh: Will use
~/.zshrc
- bash: Will use
~/.config/fish/config.fish
This way, you can silently install / uninstall completion for a specific command without asking user to do so.
tabtab uninstall --auto
The uninstall command can be used to undo what has been done by tabtab install --auto
command.
Completion for other programs
nvm
The --completer
option allows you to delegate the completion part to another
program. Let's take nvm as an example.
The idea is to create a package named nvm-complete
, with an executable that
loads tabtab
and handle the completion output of nvm-complete completion
.
{
"name": "nvm-complete",
"bin": "./index.js",
"scripts": {
"install": "tabtab install --name nvm --completer nvm-complete"
},
"dependencies": {
"tabtab": "^1.0.0"
}
}
// index.js
var tabtab = require('tabtab');
tabtab.on('nvm', function(data, done) {
return done(null, ['ls', 'ls-remote', 'install', 'use', ...]);
});
tabtab.start();
Alternatively, we can use tabtab property in package.json file to define static list of completion results:
{
"tabtab": {
"nvm": ["help", "use", "install", "uninstall", "run", "current", "ls", "ls-remote"],
"use": ["stable", "default", "iojs", "v5.11.0", "v6.0.0"]
}
}
For more control over the completion results, the JS api is useful for
returning specific values depdending on preceding words, like completing each
node versions on nvm install <tab>
.
var exex = require('child_process').exec;
// To cache the list of versions returned by ls-remote
var versions = [];
tabtab.on('install', function(data, done) {
if (versions.length) return done(null, versions);
// Ask nvm the list of remote, and return each as a completion item
exec('nvm ls-remote', function(err, stdout) {
if (err) return done(err);
versions = versions.concat(stdout.split(/\n/));
return done(null, versions);
});
});
On global installation of nvm-complete
, the user will be asked for
installation instruction (output to stdout, write to shell config, or a system
dir). The completion should be active on reload or next login (close / reopen
your terminal).
Bower
Yeoman
Debugging completion
On completion trigger (hitting tab), any STDOUT output is used as a completion results, and STDERR is completely silenced.
To be able to log and debug completion scripts and functions, you can use
TABTAB_DEBUG
environment variable. When defined, tabtab will redirect any
debug
output to the file specified.
export TABTAB_DEBUG=/tmp/tabtab.log
Trigger a completion, and tail -f /tmp/tabtab.log
to see debugging output.
to be able to use the logger in your own completion, you can
require('tabtab/lib/debug')
. it is a thin wrapper on top of the debug module,
and has the same api and behavior, except when process.env.tabtab_debug
is
defined.
const debug = require('tabtab/lib/debug')('tabtab:name');
CLI
tabtab(1) - manage and discover completion on the user system.
it provides utilities for installing a completion file, to discover and enable additional completion etc.
$ tabtab <command> [options]
Options:
-h, --help Show this help output
-v, --version Show package version
--name Binary name being completed
--auto Use default SHELL configuration file
(~/.bashrc, ~/.zshrc or ~/.config/fish/config.fish)
Commands:
install Install and enable completion file on user system
uninstall Undo the install command (only works with --auto)
tabtab install
$ tabtab install --help
options:
--stdout outputs script to console and writes nothing
--name program name to complete
--completer program that drives the completion (default: --name)
triggers the installation process and asks user for install location. --name
if not defined, is determined from package.json
name property. --completer
can be used to delegate the completion to another program. ex.
$ tabtab install --name bower --completer bower-complete
tabtab install
is not meant to be run directly, but rather used with your
package.json
scripts.
tabtab uninstall
$ tabtab uninstall --name binary-name
attemps to uninstall a previous tabtab install by removing lines added by
tabtab install
in the SHELL specific config file (~/.bashrc, ~/.zshrc or
~/.config/fish/config.fish).
Only works with --auto
flag.
credits
npm does pretty amazing stuff with its completion feature. bash and zsh provides command tab-completion, which allow you to complete the names of commands in your $path. usually these functions means bash scripting, and in the case of npm, it is partially true.
there is a special npm completion
command you may want to look around,
if not already.
npm completion
running this should dump this
script
to the console. this script works with both bash/zsh and map the correct
completion functions to the npm executable. these functions takes care
of parsing the comp_*
variables available when hitting tab to complete
a command, set them up as environment variables and run the npm completion
command followed by -- words
where words match value of
the command being completed.
this means that using this technique npm manage to perform bash/zsh completion using node and javascript. actually, the comprehensiveness of npm completion is quite amazing.
this whole package/module is based entirely on npm's code and @isaacs work.