inshellisense Usage Guide

· 4 min read · 758 Words · -Views -Comments

IDE style command line auto complete

Hereafter, inshellisense will be abbreviated as is

Installation

# If Node.js is not installed yet, recommended to install via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

nvm install 18
npm install -g @microsoft/inshellisense
npm install -g node-gyp

Don’t worry about the npm WARN EBADENGINE required: { node: '>=18' } warning during installation - it’s just the Fig specification recommending Node 18+. inshellisense supports Node 16, but higher versions are recommended.

Node <21

Testing revealed that Node 21 installation fails because the dependency node-gyp doesn’t support version 21 yet.

Usage

# Enter completion session, press space after typing commands to trigger completion popup
is

# Use complete subcommand to get JSON completion results (not supported in rc-5 yet)
is complete "git l"
  1. If it doesn’t enter normally, check for any error messages
  2. If it enters without errors but doesn’t trigger completion popup, it’s usually due to PS1 prompt detection issues. Official troubleshooting documentation is not yet available.
    • Personally encountered issues with powerlevel10k/powerlevel9k themes in ZSH. Current solution is to switch themes.

Key Bindings

Currently, inshellisense does not support custom key bindings

ActionKeybinding
Accept Current Suggestiontab
View Next Suggestion
View Previous Suggestion
Dismiss Suggestionsesc

Supported Shells

export enum Shell {
  Bash = "bash",
  Powershell = "powershell",
  Pwsh = "pwsh",
  Zsh = "zsh",
  Fish = "fish",
  Cmd = "cmd",
}

Project Overview

https://github.com/microsoft/inshellisense

Tech Stack

"@withfig/autocomplete": "^2.633.0", # Fig completion specification
"node-pty": "^1.0.0", # Fork pseudoterminals in Node.JS
"xterm-headless": "^5.3.0" # xterm.js headless terminal

Project Structure

├── shell
   ├── bash-preexec.sh # for bash, extended function hook support
   ├── shellIntegration-env.zsh # zsh
   ├── shellIntegration-login.zsh
   ├── shellIntegration-profile.zsh
   ├── shellIntegration-rc.zsh
   ├── shellIntegration.bash # bash
   ├── shellIntegration.fish # fish
   └── shellIntegration.ps1 # for Windows Powershell/Pwsh
├── src
   ├── commands # is command definitions
   ├── index.ts # entry point
   ├── isterm # terminal emulator for is
   ├── runtime # load specifications, get suggestions based on input
   ├── tests # tests
   ├── ui # terminal rendering and interaction (completion/uninstall prompts, etc.)
   └── utils

Program Logic

process(<=>pty<=>xtermjs)

  1. User enters command is to enter auto-completion mode

  2. Load is configuration files

  3. Determine shell type

    • User can specify shell in is command, otherwise automatically determined from SHELL environment variable
  4. Initialize shell configuration

    • For Zsh, add the following files to temporary directory:
    - shellIntegration-env.zsh
    - shellIntegration-login.zsh
    - shellIntegration-profile.zsh
    - shellIntegration-rc.zsh
    
    • For Bash, add bash-preexec.sh to user home directory
  5. Load full Fig specification in is

  6. Set process.stdin.setRawMode to true to ensure each keypress triggers data events

  7. Execute clear screen

  8. node-pty opens a pseudo terminal, loads corresponding shell configuration, and creates xterm headless terminal client

  9. process.stdin.on(“data”) listens for user input and continuously writes to pty

  10. pty listens for input, displays echo data, and writes to xterm

  11. xterm listens for written data

    • Calculate prompt start/end positions based on OSC in data. Command manager synchronizes terminal state

    • Completion manager calculates completions and determines whether to display them

    • Completion command module performs lexical analysis of commands, determines command context, and generates completions

    • Completion generation depends on 3 factors: input, cwd, shell

      // input is current command: this.#term.getCommandState().commandText, process.cwd() is current path
      getSuggestions(input, process.cwd()) 
      
    • Key bindings are also handled during character listening, e.g., tab writes completed command:

  12. process.stdout writes to terminal

Completion Calculation Logic

  1. Analyze command text to determine token array

    const lex = (command: string): CommandToken[] => {
      ...
      return tokens;
    };
    
  2. First token in array is rootToken, used to fetch completion files

    const loadSpec = async (cmd: CommandToken[]): Promise<Fig.Spec | undefined> => {
      const rootToken = cmd.at(0);
      if (!rootToken?.complete) {
        return;
      }
    
      if (loadedSpecs[rootToken.token]) {
        return loadedSpecs[rootToken.token];
      }
      if (specSet[rootToken.token]) {
        const spec = (await import(specSet[rootToken.token])).default;
        loadedSpecs[rootToken.token] = spec;
        return spec;
      }
    };
    
  3. Determine if last token in array is a path based on whether it contains /

  4. Calculate completions starting from second token in array

  5. Completion filtering

Suggestion Data Definition

export type Suggestion = {
  name: string;
  allNames: string[]; // command parameters in Fig can have multiple names, e.g., ["-p", "--paginate"] in git
  description?: string;
  icon: string; // emoji font
  priority: number; // priority, descending order in is
  insertValue?: string; // actual value inserted into terminal
};

Debugging

  1. Directly execute npm ru debug, then enable Attach to Node Process ⌘ in VSCode

  2. Check logs at ~/.inshellisense/inshellisense.log

Final Thoughts

  1. Personally, I think the performance of this design approach for is is acceptable, but drawing completions directly in the terminal brings issues: if position calculation is incorrect, the cursor position can easily become misaligned
  2. Looking forward to is being officially released soon
Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover