Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

tabtab

mklabs1.7mMIT3.0.2TypeScript support: definitely-typed

tab completion helpers, for node cli programs. Inspired by npm completion.

terminal, tab, unix, console, complete, completion

readme

tabtab

Build Status Coverage Status

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.

tabtab

Warning / Breaking changes

  • Windows is not supported
  • Cache has been removed
  • Now only support node > 7.10.1, for previous version with support for node 6 be sure to use tabtab 2.2.x

Table of Contents

Goal of this 3.0.0 version

Simplify everything, major overhaul, rewrite from scratch.

Functional, less abstraction, clearer documentation, good test coverage, support for node 10 without babel.

Up to date dependencies, easier to debug, easier to test.

Should still support bash, zsh and fish but bash is the primary focus of this alpha version.

No binary file anymore, just a library (still debating with myself)

The goal of this rewrite is two-folded:

  • Integrate nicely with yo (Yeoman)
  • Have a robust and fast enough library for yarn-completions

Installation

npm install tabtab

Usage

Writing completion is a two-step process: Installation and Logging. Tabtab provides just that.

Here is a basic example using minimist to parse arguments.

#! /usr/bin/env node

const tabtab = require('tabtab');
const opts = require('minimist')(process.argv.slice(2), {
  string: ['foo', 'bar'],
  boolean: ['help', 'version', 'loglevel']
});

const args = opts._;
const completion = env => {
  if (!env.complete) return;

  // Write your completions there

  if (env.prev === 'foo') {
    return tabtab.log(['is', 'this', 'the', 'real', 'life']);
  }

  if (env.prev === 'bar') {
    return tabtab.log(['is', 'this', 'just', 'fantasy']);
  }

  if (env.prev === '--loglevel') {
    return tabtab.log(['error', 'warn', 'info', 'notice', 'verbose']);
  }

  return tabtab.log([
    '--help',
    '--version',
    '--loglevel',
    'foo',
    'bar',
    'install-completion',
    'completion',
    'someCommand:someCommand is some kind of command with a description',
    {
      name: 'someOtherCommand:hey',
      description: 'You must add a description for items with ":" in them'
    },
    'anotherOne'
  ]);
};

const run = async () => {
  const cmd = args[0];

  // Write your CLI there

  // Here we install for the program `tabtab-test` (this file), with
  // completer being the same program. Sometimes, you want to complete
  // another program that's where the `completer` option might come handy.
  if (cmd === 'install-completion') {
    await tabtab
      .install({
        name: 'tabtab-test',
        completer: 'tabtab-test'
      })
      .catch(err => console.error('INSTALL ERROR', err));

    return;
  }

  if (cmd === 'uninstall-completion') {
    // Here we uninstall for the program `tabtab-test` (this file).
    await tabtab
      .uninstall({
        name: 'tabtab-test'
      })
      .catch(err => console.error('UNINSTALL ERROR', err));

    return;
  }

  // The completion command is added automatically by tabtab when the program
  // is completed.
  if (cmd === 'completion') {
    const env = tabtab.parseEnv(process.env);
    return completion(env);
  }
};

run();

Please refer to the examples/tabtab-test-complete package for a working example. The following usage documentation is based on it.

1. Install completion

To enable completion for a given program or package, you must enable the completion on your or user's system. This is done by calling tabtab.install() usually behind a program install-completion command or something similar.

// Here we install for the program `tabtab-test`, with completer being the same
// program. Sometimes, you want to complete another program that's where the
// `completer` option might come handy.
tabtab.install({
  name: 'tabtab-test',
  completer: 'tabtab-test'
})
  .then(() => console.log('Completion installed'))
  .catch(err => console.error(err))

The method returns a promise, so await / async usage is possible. It takes an options parameter, with:

  • name: The program to complete
  • completer: The program that does the completion (can be the same program).

tabtab.install() will ask the user which SHELL to use, and optionally a path to write to. This will add a new line to either ~/.bashrc, ~/.zshrc or ~/.config/fish/config.fish file to source tabtab completion script.

Only one line will be added, even if it is called multiple times.

2. Log completion

Once the completion is enabled and active, you can write completions for the program (here, in this example tabtab-test). Briefly, adding completions is as simple as logging output to stdout, with a few particularities (namely on Bash, and for descriptions), but this is taken care of by tabtab.log().

tabtab.log([
  '--help',
  '--version',
  'command'
  'command-two'
]);

This is the simplest way of adding completions. You can also use an object, instead of a simple string, with { name, description } property if you want to add descriptions for each completion item, for the shells that support them (like Zsh or Fish). Or use the simpler name:description form.

tabtab.log([
  { name: 'command', description: 'Description for command' },
  'command-two:Description for command-two'
]);

The { name, description } approach is preferable in case you have completion items with : in them.

Note that you can call tabtab.log() multiple times if you prefer to do so, it simply logs to the console in sequence.

3. Parsing env

If you ever want to add more intelligent completion, you'll need to check and see what is the last or previous word in the completed line, so that you can add options for a specific command or flag (such as loglevels for --loglevel for instance).

Tabtab adds a few environment variables for you to inspect and use, this is done by calling tabtab.parseEnv() method.

const env = tabtab.parseEnv(process.env);
// env:
//
// - complete    A Boolean indicating whether we act in "plumbing mode" or not
// - words       The Number of words in the completed line
// - point       A Number indicating cursor position
// - line        The String input line
// - partial     The String part of line preceding cursor position
// - last        The last String word of the line
// - lastPartial The last word String of partial
// - prev        The String word preceding last

Usually, you'll want to check against env.last or env.prev.

if (env.prev === '--loglevel') {
  tabtab.log(['error', 'warn', 'info', 'notice', 'verbose']);
}

Completion mechanism

Feel free to browse the scripts directory to inspect the various template files used when creating a completion with tabtab.install().

Here is a Bash completion snippet created by tabtab.

###-begin-tabtab-test-completion-###
if type complete &>/dev/null; then
  _tabtab-test_completion () {
    local words cword
    if type _get_comp_words_by_ref &>/dev/null; then
      _get_comp_words_by_ref -n = -n @ -n : -w words -i cword
    else
      cword="$COMP_CWORD"
      words=("${COMP_WORDS[@]}")
    fi

    local si="$IFS"
    IFS=$'\n' COMPREPLY=($(COMP_CWORD="$cword" \
                           COMP_LINE="$COMP_LINE" \
                           COMP_POINT="$COMP_POINT" \
                           tabtab-test completion -- "${words[@]}" \
                           2>/dev/null)) || return $?
    IFS="$si"
    if type __ltrim_colon_completions &>/dev/null; then
      __ltrim_colon_completions "${words[cword]}"
    fi
  }
  complete -o default -F _tabtab-test_completion tabtab-test
fi
###-end-tabtab-test-completion-###

The system is quite simple (though hard to nail it down, thank you npm). A new Bash function is created, which is invoked whenever tabtab-test is tab completed. This function then invokes the completer tabtab-test completion with COMP_CWORD, COMP_LINE and COMP_POINT environment variables (which is parsed by tabtab.parseEnv()).

The same mechanism can be applied to Zsh and Fish.

Completion install

As described in the Usage > Install Completion section, installing a completion involves adding a new line to source in either ~/.bashrc, ~/.zshrc or ~/.config/fish/config.fish file.

In the 3.0.0 version, it has been improved to only add a single line instead of multiple ones, one for each completion package installed on the system.

This way, a single line is added to enable the completion of for various programs without cluttering the Shell configuration file.

Example for ~/.bashrc

# tabtab source for packages
# uninstall by removing these lines
[ -f ~/.config/tabtab/__tabtab.bash ] && . ~/.config/tabtab/__tabtab.bash || true

It'll load a file __tabtab.bash, created in the ~/.config/tabtab directory, which will hold all the source lines for each tabtab packages defining a completion.

# tabtab source for foo package
# uninstall by removing these lines
[ -f ~/.config/tabtab/foo.bash ] && . ~/.config/tabtab/foo.bash || true

# tabtab source for tabtab-test package
# uninstall by removing these lines
[ -f ~/.config/tabtab/tabtab-test.bash ] && . ~/.config/tabtab/tabtab-test.bash || true

Completion uninstall

You can follow the file added in your SHELL configuration file and disable a completion by removing the above lines.

Or simply disable tabtab by removing the line in your SHELL configuration file.

Or, you can use tabtab.uninstall() to do this for you.

if (cmd === 'uninstall-completion') {
  // Here we uninstall for the program `tabtab-test`
  await tabtab
    .uninstall({
      name: 'tabtab-test'
    })
    .catch(err => console.error('UNINSTALL ERROR', err));

  return;
}

Debugging

tabtab internally logs a lot of things, using the debug package.

When testing a completion, it can be useful to see those logs, but writing to stdout or stderr while completing something can be troublesome.

You can use the TABTAB_DEBUG environment variable to specify a file to log to instead.

export TABTAB_DEBUG="/tmp/tabtab.log"
tail -f /tmp/tabtab.log

# in another shell
tabtab-test <tab>

See tabtabDebug.js file for details.

API Documentation

Please refer to api directory to see generated documentation (using jsdoc2md)

Changelog

Please refer to CHANGELOG file to see all possible changes to this project.

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.


mit · > mklabs.github.io · > @mklabs

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

Generated by auto-changelog.

Unreleased

Commits

  • feat: add tabtab.uninstall() 23907cd

v3.0.1-beta - 2018-10-05

Fixed

  • Fixing check of filename when installing #47

Commits

  • Begin to write uninstall method e143f80
  • npm: remove assert-rejects 399a3da
  • example: adding uninstall-completion to example c48223a

v3.0.0-beta - 2018-10-04

Merged

  • installer: ensure successful status/$? #38
  • Remove npmlog. #31
  • Fix wrong shell reference #35

Commits

  • npm: setup nyc 1f86b2a
  • first cleanup c89f237
  • setup npm-watch a47d73a
  • npm: remove reactify, reset babel da65059
  • npm: update dependencies dd19437
  • eslint: setup eslint and config 64f3364
  • npm: update dependencies 63cd043
  • ci: setup nyc and coverage scripts e6fe55a
  • feat: tabtab.install() with prompt (wip) f599724
  • ci: disable nyc for now c1e1806
  • log: better handling of descriptions 12d5897
  • es6: replace vars by const/let 71ad75a
  • feat: avoid adding multiple lines to SHELL scripts 036d9c0
  • feat: add necessary completion lines in shell config 2030676
  • log: fix TABTAB_DEBUG and use it in tests dbd6ffb
  • examples: add tabtab-test-complete to serve as completion tests c0423dc
  • cli: test out completions c861535
  • eslint: fix all eslint errros and update config bbf2a5b
  • fix: ensure fileformat is unix and remove code related to bash-completion 9b83d76
  • npm: setup prettier a2cd163
  • node: support for version 10, 8 and 7 c01b443
  • example: remove completion script, all in one fae0553
  • completion: now emits the whole line (breaking change) 75c7b6a
  • log: slight change to tabtab.log 1aa7d1a
  • eslint: include test dir as well 345d191
  • babel: remove babel continuation 6f279be
  • npm: remove prettier / es6-promisify b5ab126
  • babel: start to remove babel 7f19043
  • bash: handle semicolon 51f1de7
  • fix: a bit more debugging and fix entry point 4875c90
  • debug: use tabtab debug in all files e81aa93
  • Add .babelrc with preset env 5f0eaf1
  • complete: always trigger original event based on options.name ca0ab39
  • debug: dont log into console db192d7
  • parseEnv: change signature to only take environment 3e60094
  • ci: remove npm prune 7a319e2
  • debug: change debug for parseEnv, too verbose 627ef79
  • ci(package.json): run nyc on tests eff6e95
  • mocha: increase timeout to avoid failure on node 6 fc97626
  • Do NOT test with cache for the moment 3cf9115
  • ci: forget about node 6 for the moment aa7ade3
  • commands: remove options.auto mandatory in uninstall 36dce25
  • uninstall: use default options on uninstall 7e179b6
  • fix: package.json syntax 0a0238b
  • Update package.json f3a9580
  • add .gitattributes to force unix line endings 9a7440e

v2.2.2 - 2017-01-06

Merged

  • fix(win32): fix usage of SHELL environment variable when it is not set #30

v2.2.1 - 2016-10-13

Commits

  • fix: create duplicate-free version of completion items accross evt listeners dc8b587

v2.2.0 - 2016-10-11

Commits

  • feat(fish): handle description by adding a tab character between name and description 9290dcc

v2.1.1 - 2016-10-09

Commits

  • fix(zsh): fix uninstall typo in zshrc (instead of zshhrc) 3d29317

v2.1.0 - 2016-10-09

Commits

  • fix(fish): Disable description in fish completion per command / options 1f04613
  • fix(fish): fix COMP_LINE by appending a space so that prev is correctly positioned 861f8ef
  • feat(fish): prevent filenames from being completed 282b941

v2.0.2 - 2016-10-06

Commits

  • fix: have output done after recv to handle async completion handler d8596ed
  • Remove bake from package.json c306bce

v2.0.1 - 2016-10-06

Commits

  • Remove src/ folder and babel compiled files 8531a62
  • rm Makefile d717594
  • fix: have uninstall command working as expected by fixing regexp 21e2de6

v2.0.0 - 2016-09-30

v1.4.3 - 2016-09-30

Merged

  • allow installing on a windows system when running in a git bash #27
  • add $CURSOR for position in zsh.sh script #24

Fixed

  • add $cursor for position in zsh.sh script #23

Commits

  • add babel'ed files to src folder so that you can directly install it from github 4cd37ec
  • src: update build 1c5619d

v1.4.2 - 2016-05-21

Commits

  • fix(babel): remove transform-runtime plugin 845eb54

v1.4.1 - 2016-05-21

v1.4.0 - 2016-05-21

Fixed

  • feat(description): Handle zsh description using _describe fn #19

Commits

  • rework cache, fix bash completion handling b7cecf7
  • feat(uninstall): Implement uninstall command and --auto flag de37993
  • fix(completion): gather results and write only once to STDOUT b928bc9
  • Fix zsh template script a22e6b0
  • zsh: check for compdef f216888
  • fix: Skip completion install for win32 platform or unknown shell c4f6073
  • babel: add plugin default transform 1dcc302
  • fix(bash): Silently fail if pkg-config bash-completion exists with non 0 0765749
  • feat(debug): automatically JSON.stringify non string objects e4423f8

v1.3.0 - 2016-05-08

Fixed

  • feat(cache): Implement cache TTL (default: 5 min) #20
  • feat(cache): Add option to enable / disable cache #20

v1.2.1 - 2016-05-08

v1.2.0 - 2016-05-08

Commits

  • feat: implement a basic cache mechanism bb4216c
  • fix: Use Object.assign polyfill to run on older version of node 157057a

v1.1.1 - 2016-05-01

Commits

  • fix: more generic assert on prompt bbcd350

v1.1.0 - 2016-05-01

Commits

  • example: have yo-complete based on yeoman-environment and parse-help 115fdae
  • Add notes on debug and log output e42149a
  • feat(completion): Enhance package.json completion to support last word ce794d4
  • feat(completion): Emit completion events along package.json results 2ed8ef5
  • fish - set default description to package name 9f8e934
  • fix(fish): Better handling of description 779a188

v1.0.5 - 2016-04-30

Commits

  • release: git push tags && npm publish 29035d8

v1.0.4 - 2016-04-30

Commits

  • Change standard-version msg d1d19f6

v1.0.3 - 2016-04-30

Commits

  • chore(release): 1.0.3 dbbdcac
  • fix(babel): Add babel as prepublish step 97fc9ce

v1.0.1 - 2016-04-29

Merged

  • fix: zsh (on osx anyway) seems to require a space before the ]] #16

Commits

  • examples: add yo-complete example 1a18381
  • fix: fix fish shell script to properly escape variables 6f9664e
  • bash: apply same spacing before closing ] 50f0340
  • zsh (on osx anyway seems to require a space before the ]] 1f9f983

v1.0.0 - 2016-04-26

Commits

  • Check in examples 82de5ef
  • fix: check in example and fix bower-complete package.json c46185f

v1.0.0-pre - 2016-04-26

Commits

  • Move old stuff a53de4a
  • Move old stuff to .old 94369a0
  • Main API and plumbing system done c3cba1d
  • Cleanup old dir 45b09af
  • Prompt user for completion script installation method 73f6090
  • rm old completion file cef3c00
  • Update docs, less verbose debug output 927e08c
  • Init v1 3314024
  • TomDocify 9587418
  • Init command plumbing system 0361905
  • Implement fish bridge, template system depending on $SHELL 1823230
  • Shell adapters, handle bash / zsh / fish ab90a1a
  • More docs a483822
  • install - check for existing content before writing 2250e08
  • More docs 731222e
  • Implement json based completion results 5421395
  • Support completion item description for fish, still need work to do on zsh 5dfc6f0
  • wip install / uninstall 16cdf73
  • Handle permission issue c44ef31
  • Ensure directory exists before writing bed76b3
  • Event chaining, walking up the line untill it find a listener 3c4241c
  • API example 4c4d86c
  • docs task 305b0b4
  • ghpages task 62c4362
  • travis 5fe6b73
  • doc -wrong prefix 2c8b91a
  • badge version 751af46
  • travis - run mocha with babel-node 962127c
  • travis - babel compile before test ddac422
  • travis - add babelrc file 8a2a29b
  • Check in screenshots b7e3724
  • init completion directory registry 3f92281

v0.0.4 - 2015-06-06

Merged

  • Issues with tabtab in zsh. #10
  • Fix typo #11

Commits

  • Updated the completion script to match current npm output. be1c512
  • Added default filesystem matching. f57a254
  • :book: Fix typo 45c6ead
  • Didn't realize the line had {completer} before. Changing back. 10f3472
  • Added back new line. c74f7ab

v0.0.3 - 2015-01-26

Merged

  • Allow completing long options #5
  • Catching EPIPE error caused by source closing file descriptor before reading it #4

Fixed

  • Fix #3 - Add license info #3

Commits

  • rm old .pkgrc file 42bcf50
  • Catching error caused by source closing file argument before reading from it. 4fca6aa
  • travis - node 0.10 e13de5b

v0.0.2 - 2012-02-08

Commits

  • tidy up the whole mess. remove unused / unnecessary code 6a1e9c3
  • add missing devDependency fab4faf
  • bumping version cd56910
  • correct abbrev with - in it 0b51ad8

v0.0.1 - 2011-11-11

Commits

  • edit package.json 9be6eba
  • return warn messages as state 8da7d5b
  • warn without exiting with error, and ensure numbers on parsed env 34a2ede
  • rm gendoc script 06d3a7a
  • add gendoc script dbd4739
  • package.json: specify directories for the docs task 08a25ef
  • add some completion install/uninstall docs 46d324a
  • rename to tabtab and edit test assert to use dynamic path 061a357
  • add vows test suite for completion output and install/uninstall cmd 029de43
  • edit docs.js comments and rm lib/cli.js (was empty anyway) 4abc675
  • add pkgrc help command fff228f
  • add install/uninstall helper 6cfb0ee
  • some docs, have more to write 9ccd0d7
  • add play-complete script, completion from play help output f8347bb
  • Use readline's default filename completion if no matches. 5ea2d4c
  • log instruction on examples when not called within completion context bfc6ad0
  • parse ` and ~~~~ special code marker in markdowns 31ee00f
  • add help module, takes a file input (md, js or cs) and man a generated manpage 11d5d70
  • add basic script for vagrant completion 5a8fd4d
  • move helper functions to completion module 5fc9fa0
  • add cake/rake completion, very similar 92f125f
  • add completer options, decouple completed process from completer process c864c9d
  • completion: add cakefile completion, testing options/tasks completion 33c272b
  • completion: add optimist completion, have to parse out the help output 6c1b1bb
  • completion: add basic abbrev support and test with nopt/commander opt a857dd2
  • played a little with nopt/commander options and basic completion c6fa6de
  • add prev to options parsed from compgen cfb2894
  • add some commander/optimist/nopt examples script 22e0681
  • completion - install instruction and simple line parsing/callback api ce1f1f3
  • completion start 94b103f
  • initial config work, merge of global/local rc file 64a0f7a
  • a start a46ca29