tabtab

repository·master·Indexed 18 days ago

https://github.com/mklabs/tabtab

A Node.js library for implementing custom command-line tab-completion for Bash, Zsh, and Fish shells. It allows Node.js logic to drive the completion engine via a shell script bridge, providing utilities to install/uninstall completions, parse shell environment variables in "plumbing mode," and log completion candidates. Windows is not supported.

Tokens
5.4K
Snippets
22
Records
27
Agent score
63%

What's inside tabtab

  1. How tabtab manages shell completion scripts

    master

    Tabtab uses a multi-layered approach to manage shell completions:

    1. SHELL Config: A single line is added to your shell's configuration file (like ~/.zshrc) to source the tabtab internal router.
    2. Internal Router Script: Located in ~/.config/tabtab, this script acts as a frontend router. Every installed completion is registered here.
    3. Completion Scripts: Individual scripts are created in ~/.config/tabtab for each package. These are shell-specific (e.g., a different script for Bash vs. Fish) and are called by the router to provide the actual completion logic.

    This architecture allows tabtab to manage multiple completions through a single entry point in your shell configuration.

  2. Understand the inspiration for tabtab's completion mechanism

    master

    tabtab's completion mechanism is inspired by how npm implements command-line completion for bash and zsh using Node.js and JavaScript.

    Instead of relying solely on complex shell scripts, npm uses a shell script to intercept tab-completion events, map the comp_* variables to environment variables, and then invoke the npm completion -- words command. This allows the actual logic of parsing and providing completions to be handled within the Node.js runtime, enabling more comprehensive and maintainable completion logic.

  3. How to implement tabtab command completion

    master

    Implementing completion is a two-step process: Installation and Logging.

    1. Installation: You must call tabtab.install() (typically via a CLI command like my-app install-completion) to enable completion on the user's system (Bash, Zsh, or Fish).
    2. Logging: When the shell triggers completion, your program must run in a special "plumbing mode". You detect this using tabtab.parseEnv(process.env), check if env.complete is true, and then use tabtab.log() to output the available completion candidates to stdout.

    Note: Windows is not supported.

    const tabtab = require('tabtab');
    
    // 1. The 'plumbing' logic triggered by the shell
    const completion = env => {
      if (!env.complete) return;
    
      if (env.prev === '--loglevel') {
        return tabtab.log(['error', 'warn', 'info']);
      }
    
      return tabtab.log(['--help', '--version', 'foo', 'bar']);
    };
    
    // 2. The CLI entrypoint logic
    const run = async () => {
      const cmd = process.argv[2];
    
      if (cmd === 'install-completion') {
        await tabtab.install({ name: 'my-app', completer: 'my-app' });
        return;
      }
    
      if (cmd === 'completion') {
        const env = tabtab.parseEnv(process.env);
        return completion(env);
      }
    };
    
    run();
  4. Configure shell environments for testing completions

    master

    When testing completions with tabtab, the tool relies on the $SHELL environment variable to identify the active shell. If you are not using your system's default shell, you must manually set the SHELL variable to ensure tabtab detects the correct environment.

    Bash

    Ensure $SHELL is set to bash or /bin/bash.

    Zsh

    1. Install zsh.
    2. If your default shell is bash, spawn a new session by typing zsh.
    3. Set the environment variable: SHELL=zsh.

    Fish

    1. Install fish.
    2. Spawn a new session by typing fish.
    3. Set the environment variable: set SHELL fish.

    Note: These steps are unnecessary if you are testing against your primary system shell (e.g., configured via chsh).

  5. Debug tabtab completions

    master

    Since writing to stdout or stderr during completion can interfere with the shell's ability to display results, use the TABTAB_DEBUG environment variable to redirect internal logs to a file.

    Usage:

    1. Set the environment variable to a file path.
    2. Run your command with <tab>.
    3. Monitor the log file.
    export TABTAB_DEBUG="/tmp/tabtab.log"
    tail -f /tmp/tabtab.log
    
    # In another terminal
    my-app <tab>
  6. Log completion candidates with tabtab.log()

    master

    Use tabtab.log() to output the list of strings or objects that should appear in the shell's completion menu.

    Supported Formats:

    • Simple strings: tabtab.log(['foo', 'bar'])
    • Strings with descriptions (colon syntax): tabtab.log(['command:Description for command'])
    • Objects (recommended for complex names): tabtab.log([{ name: 'cmd', description: 'desc' }])
    tabbab.log([
      '--help',
      '--version',
      'command:A description for command',
      { name: 'other', description: 'A description for other' }
    ]);
  7. Install completion for a program

    master

    Use tabtab.install() to add the necessary shell scripts to the user's configuration files (~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish). This method returns a Promise.

    Options:

    • name: The name of the program you want to provide completion for.
    • completer: The name of the program that handles the completion logic (can be the same as name).
    tabbab.install({
      name: 'tabtab-test',
      completer: 'tabtab-test'
    })
    .then(() => console.log('Completion installed'))
    .catch(err => console.error(err));
  8. Log completion items with log()

    master

    The log(Arguments) function is the main utility for passing completion items back to the shell. It logs items to stdout, with each item separated by a new line.

    Usage Notes

    • Arguments: An Array of items to log. Items can be Strings or Objects containing name and description properties.
    • Shell Compatibility: While zsh and fish handle the output directly, Bash requires additional filtering of the arguments for the completion to function correctly.
    // Logging strings
    tabtab.log(['arg1', 'arg2']);
    
    // Logging objects with name and description
    tabtab.log([
      { name: 'help', description: 'Show help' },
      { name: 'version', description: 'Show version' }
    ]);
  9. Install shell completion with install()

    master

    Use the install(Options) function to enable shell completion on the user's system. When called, the process will prompt the user for:

    • The shell being used (bash, zsh, or fish).
    • The path to the shell script (sensible defaults are provided).

    Options

    The Options object accepts:

    • name: The name of the command.
    • completer: The logic used for completion.
    tabtab.install({
      name: 'my-cli',
      completer: myCompleterFunction
    });
  10. Uninstall completion for a program

    master

    To remove the completion configuration for a specific program, use tabtab.uninstall(). This method returns a Promise.

    Options:

    • name: The name of the program to uninstall.
    tabbab.uninstall({
      name: 'tabtab-test'
    })
    .catch(err => console.error('UNINSTALL ERROR', err));