neovim

Neovim node.js client

CI (node >= 14, Linux/macOS/Windows) Coverage npm
ci Coverage Badge npm version

Install

For "remote plugins", the Nvim Node.js provider expects the neovim package to be globally installed:

npm install -g neovim

Or for non-plugin purposes, neovim works like any other NPM package. See below for a quickstart example that you can copy and run immediately.

Usage

The neovim package exposes these functions:

  • findNvim: Tries to find a usable nvim binary on the current system.
  • attach: The primary interface. Takes a process, socket, or pair of write/read streams and returns a NeovimClient connected to an nvim process.

Quickstart: connect to Nvim

Following is a complete, working example.

  1. Install the neovim package locally (i.e. without -g. Node throws ERR_MODULE_NOT_FOUND if a script imports a globally installed package).
    npm install neovim
    
  2. Paste the script below into a demo.mjs file and run it!
    node demo.mjs
    
import * as child_process from 'node:child_process'
import * as assert from 'node:assert'
import { attach, findNvim } from 'neovim'

// Find `nvim` on the system and open a channel to it.
(async function() {
const found = findNvim({ orderBy: 'desc', minVersion: '0.9.0' })
console.log(found);
const nvim_proc = child_process.spawn(found.matches[0].path, ['--clean', '--embed'], {});

const nvim = attach({ proc: nvim_proc });
nvim.command('vsp | vsp | vsp');

const windows = await nvim.windows;
assert.deepStrictEqual(windows.length, 4);
assert.ok(windows[0] instanceof nvim.Window);

nvim.window = windows[2];
const win = await nvim.window;
assert.ok(win.id !== windows[0].id);
assert.deepStrictEqual(win.id, windows[2].id);

const buf = await nvim.buffer;
assert.ok(buf instanceof nvim.Buffer);
const lines = await buf.lines;
assert.deepStrictEqual(lines, []);

await buf.replace(['line1', 'line2'], 0);
const newLines = await buf.lines;
assert.deepStrictEqual(newLines, ['line1', 'line2']);

// console.log('%O', nvim_proc);
if (nvim_proc.disconnect) {
nvim_proc.disconnect();
}
nvim.quit();
while (nvim_proc.exitCode === null) {
await new Promise(resolve => setTimeout(resolve, 100))
console.log('waiting for Nvim (pid %d) to exit', nvim_proc.pid);
}
console.log('Nvim exit code: %d', nvim_proc.exitCode);
})();

Create a remote plugin

Neovim supports remote plugins, which are plugins implemented as Nvim API clients. This package contains both the "API client" (which talks to nvim) and "remote plugin host" (which discovers and runs Nvim node.js remote plugins).

You can define a remote plugin as a file or folder in an rplugin/node/ directory on Nvim's 'runtimepath'. If the plugin is a folder, the main script from package.json will be loaded.

The plugin must export a function which takes a NvimPlugin object as its only parameter. You may then register autocmds, commands and functions by calling methods on the NvimPlugin object. Avoid heavy initialisation or async functions at this stage, because Nvim may only be collecting information about your plugin without wishing to actually use it. Instead, wait for one of your autocmds, commands or functions to be called before starting any processing.

The host replaces console with a winston interface, so console.log will call winston.info.

Remote plugin examples

See examples/ for remote plugin examples.

Remote plugin API

  NvimPlugin.nvim

This is the nvim api object you can use to send commands from your plugin to nvim.

  NvimPlugin.setOptions(options: NvimPluginOptions);

interface NvimPluginOptions {
dev?: boolean;
alwaysInit?: boolean;
}

Set your plugin to dev mode, which will cause the module to be reloaded on each invocation. alwaysInit will always attempt to attempt to re-instantiate the plugin. e.g. your plugin class will always get called on each invocation of your plugin's command.

  NvimPlugin.registerAutocmd(name: string, fn: Function, options: AutocmdOptions): void;
NvimPlugin.registerAutocmd(name: string, fn: [any, Function], options: AutocmdOptions): void;

interface AutocmdOptions {
pattern: string; // See `:help autocmd-pattern`.
eval?: string; // Vimscript expression evaluated by the Nvim peer.
sync?: boolean; // Force blocking (non-async) behavior.
}

Registers an autocmd for the event name, calling your function fn with options. Pattern is the only required option. If you wish to call a method on an object you may pass fn as an array of [object, object.method].

By default autocmds, commands and functions are all treated as asynchronous and should return Promises (or should be async functions).

  NvimPlugin.registerCommand(name: string, fn: Function, options?: CommandOptions): void;
NvimPlugin.registerCommand(name: string, fn: [any, Function], options?: CommandOptions): void;

interface CommandOptions {
sync?: boolean; // Force blocking (non-async) behavior.
range?: string; // See `:help :range`.
nargs?: string; // See `:help :command-nargs`.
}

Registers a command named by name, calling function fn with options. This will be invoked from nvim by entering :name in normal mode.

  NvimPlugin.registerFunction(name: string, fn: Function, options?: NvimFunctionOptions): void;
NvimPlugin.registerFunction(name: string, fn: [any, Function], options?: NvimFunctionOptions): void;

interface NvimFunctionOptions {
sync?: boolean; // Force blocking (non-async) behavior.
range?: string; // See `:help :range`.
eval?: string; // Vimscript expression evaluated by the Nvim peer.
}

Registers a function with name name, calling function fn with options. This will be invoked from nvim by entering eg :call name() in normal mode.

Debug / troubleshoot

For debugging and configuring logging, you can set the following environment variables which are used by the neovim package (or nvim itself where noted):

  • NVIM_NODE_HOST_DEBUG: Spawns the node process that calls neovim-client-host with --inspect-brk so you can have a debugger. Pair that with this Node Inspector Manager Chrome plugin
  • Logging: Logging is done using winston through the logger module. Plugins have console replaced with this interface.
    • NVIM_NODE_LOG_LEVEL: Sets the logging level for winston. Default is debug. Available levels: { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }
    • NVIM_NODE_LOG_FILE: Sets the log file path.
  • Usage through node REPL
    • NVIM_LISTEN_ADDRESS:
      1. Start Nvim with a known address (or use the $NVIM_LISTEN_ADDRESS of a running instance):
        $ NVIM_LISTEN_ADDRESS=/tmp/nvim nvim
        
      2. In another terminal, connect a node REPL to Nvim
        // `scripts/nvim` will detect if `NVIM_LISTEN_ADDRESS` is set and use that unix socket
        // Otherwise will create an embedded `nvim` instance
        require('neovim/scripts/nvim').then((nvim) => {
        nvim.command('vsp');
        });

See the tests and scripts for more examples.

Maintain

Release

Only maintainers of the neovim NPM package can publish a release. Follow these steps to publish a release:

  1. Update CHANGELOG.md.
  2. Update version. Build and publish the package. Tag the release and push.
    # Choose major/minor/patch as needed.
    npm version -w packages/neovim/ patch
    # Note: this copies the top-level README.md to packages/neovim.
    npm run publish:neovim
    export _VERSION=$(grep -o 'version": "[^"]\+' packages/neovim/package.json | sed 's/.*"//')
    git tag "v${_VERSION}"
    git push --follow-tags
  3. Post-release tasks:
    cd packages/neovim/
    npm version -w packages/neovim/ --no-git-tag-version prerelease --preid dev
    git add package*.json && git commit -m bump
    git push

Regenerate documentation website

The docs website is currently not automated. Follow these steps to regenerate it:

npm run doc -w packages/neovim
git checkout gh-pages
mv -f packages/neovim/doc/assets/* assets/
mv -f packages/neovim/doc/classes/* classes/
mv -f packages/neovim/doc/functions/* functions/
mv -f packages/neovim/doc/types/* types/
mv packages/neovim/doc/* .
rm -r packages/
git add *
git commit
git push origin HEAD:gh-pages

Contributing

After cloning the repo, run npm install to install dev dependencies. The main neovim library is in packages/neovim.

Contributors

Generated using TypeDoc