or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api-client.mdbuffer.mdconnection.mdindex.mdplugin-development.mdwindow-tabpage.md
tile.json

tessl/npm-neovim

Nvim msgpack API client and remote plugin provider for Node.js

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/neovim@5.4.x

To install, run

npx @tessl/cli install tessl/npm-neovim@5.4.0

index.mddocs/

Neovim Node.js Client

A comprehensive Node.js client library for Neovim that enables programmatic interaction with Neovim instances through msgpack-based RPC communication. This package provides a complete API client for controlling Neovim processes and serves as the official Node.js remote plugin host.

Package Information

  • Package Name: neovim
  • Package Type: npm
  • Language: TypeScript/JavaScript
  • Installation: npm install neovim (for general use) or npm install -g neovim (for remote plugin development)

Core Imports

import { attach, findNvim, Neovim, NeovimClient, Buffer, Window, Tabpage } from 'neovim';

For plugin development:

import { Plugin, Command, Function, Autocmd, NvimPlugin, loadPlugin } from 'neovim';

For buffer events (Symbol imports):

import { ATTACH, DETACH } from 'neovim/lib/api/Buffer';

CommonJS:

const { attach, findNvim, Neovim } = require('neovim');

Basic Usage

import * as child_process from 'node:child_process';
import { attach, findNvim } from 'neovim';

// Find and spawn Neovim
const found = findNvim({ minVersion: '0.9.0' });
const nvim_proc = child_process.spawn(found.matches[0].path, ['--embed'], {});

// Attach to the Neovim process
const nvim = attach({ proc: nvim_proc });

// Execute commands
await nvim.command('vsp');

// Access windows and buffers
const windows = await nvim.windows;
const currentBuffer = await nvim.buffer;

// Manipulate buffer content
const lines = await currentBuffer.lines;
await currentBuffer.replace(['Hello from Node.js!'], 0);

// Clean up
nvim.quit();

Architecture

The neovim package is organized around several key components:

  • Connection Layer: attach() and findNvim() handle discovering and connecting to Neovim instances via various transport methods (stdio, sockets, pipes)
  • API Client: Neovim and NeovimClient classes provide the main interface to all Neovim API operations
  • Entity Classes: Buffer, Window, and Tabpage represent Neovim entities with their specific operations
  • Plugin System: Decorator-based (@Plugin, @Command, etc.) and class-based (NvimPlugin) approaches for creating remote plugins
  • Event System: Buffer event listeners for reacting to buffer changes in real-time
  • Logging: Winston-based logger that monkey-patches console to avoid breaking stdio RPC channels

Capabilities

Connection and Discovery

Core functionality for locating Neovim executables and establishing connections to Neovim instances via various transport methods.

function attach(options: Attach): NeovimClient;

interface Attach {
  reader?: NodeJS.ReadableStream;
  writer?: NodeJS.WritableStream;
  proc?: NodeJS.Process | child_process.ChildProcess;
  socket?: string;
  options?: { logger?: any };
}

function findNvim(opt?: FindNvimOptions): Readonly<FindNvimResult>;

interface FindNvimOptions {
  minVersion?: string;
  orderBy?: 'desc' | 'none';
  firstMatch?: boolean;
  paths?: string[];
  dirs?: string[];
}

interface FindNvimResult {
  matches: ReadonlyArray<NvimVersion>;
  invalid: ReadonlyArray<NvimVersion>;
}

interface NvimVersion {
  path: string;
  nvimVersion?: string;
  buildType?: string;
  luaJitVersion?: string;
  error?: Readonly<Error>;
}

Connection and Discovery

Neovim API Client

Complete access to all Neovim API operations including command execution, evaluation, buffer/window/tabpage management, UI operations, and event subscriptions.

class Neovim {
  // Properties
  static Buffer: typeof Buffer;
  static Window: typeof Window;
  static Tabpage: typeof Tabpage;

  // Key getters/setters
  apiInfo: Promise<[number, ApiInfo]>;
  buffers: Promise<Buffer[]>;
  buffer: AsyncBuffer;
  windows: Promise<Window[]>;
  window: AsyncWindow;
  tabpages: Promise<Tabpage[]>;
  tabpage: AsyncTabpage;
  line: string | Promise<string>;
  mode: Promise<{ mode: string; blocking: boolean }>;

  // Command execution
  command(arg: string): Promise<any>;
  commandOutput(arg: string): Promise<string>;

  // Evaluation
  eval(expr: string): Promise<VimValue>;
  lua(code: string, args?: VimValue[]): Promise<VimValue>;
  call(fname: string, args?: VimValue | VimValue[]): Promise<any>;
  callAtomic(calls: VimValue[]): Promise<[any[], boolean]>;

  // Window/buffer creation
  createBuffer(listed: boolean, scratch: boolean): Promise<Buffer | number>;
  openWindow(buffer: Buffer, enter: boolean, options: OpenWindowOptions): Promise<Window | number>;

  // Event management
  subscribe(event: string): Promise<void>;
  unsubscribe(event: string): Promise<void>;

  // UI operations
  uiAttach(width: number, height: number, options: UiAttachOptions): Promise<void>;
  uiDetach(): Promise<void>;

  // Process control
  quit(): void;
}

class NeovimClient extends Neovim {
  channelId: Promise<number>;
  isApiReady: boolean;

  attach({ reader, writer }: { reader: NodeJS.ReadableStream; writer: NodeJS.WritableStream }): void;
  close(): Promise<void>;
  [Symbol.asyncDispose](): Promise<void>;
}

type VimValue = number | boolean | string | number[] | { [key: string]: any };

type AsyncBuffer = Promise<Buffer> & Omit<Buffer, 'id' | 'isAttached'>;
type AsyncWindow = Promise<Window> & Omit<Window, 'id'>;
type AsyncTabpage = Promise<Tabpage>;

Neovim API Client

Buffer Operations

Buffer manipulation including line operations, highlights, virtual text, buffer events, and buffer-scoped variables and options.

class Buffer {
  id: number;
  isAttached: boolean;

  // Key properties
  length: Promise<number>;
  lines: Promise<string[]>;
  name: string | Promise<string>;
  changedtick: Promise<number>;
  valid: Promise<boolean>;
  loaded: Promise<boolean>;

  // Line operations
  getLines(options?: { start?: number; end?: number; strictIndexing?: boolean }): Promise<string[]>;
  setLines(lines: string | string[], options?: BufferSetLines): Promise<void>;
  insert(lines: string[] | string, start: number): Promise<void>;
  replace(lines: string[] | string, start: number): Promise<void>;
  remove(start: number, end: number, strictIndexing?: boolean): Promise<void>;
  append(lines: string[] | string): Promise<void>;

  // Highlights and virtual text
  addHighlight(options: BufferHighlight): Promise<number>;
  clearNamespace(args: BufferClearNamespace): void;
  setVirtualText(nsId: number, line: number, chunks: VirtualTextChunk[], opts?: object): Promise<number>;

  // Event handling
  listen(eventName: string, cb: Function): Function;
  unlisten(eventName: string, cb: Function): void;

  // Variables and options (inherited from BaseApi)
  getVar(name: string): Promise<VimValue>;
  setVar(name: string, value: VimValue): Promise<void>;
  getOption(name: string): Promise<VimValue>;
  setOption(name: string, value: VimValue): Promise<void>;
}

interface BufferSetLines {
  start?: number;
  end?: number;
  strictIndexing?: boolean;
}

interface BufferHighlight {
  hlGroup: string;
  line: number;
  colStart?: number;
  colEnd?: number;
  srcId?: number;
}

interface BufferClearNamespace {
  nsId: number;
  lineStart?: number;
  lineEnd?: number;
}

type VirtualTextChunk = [string, string];

Buffer Operations

Window and Tabpage Operations

Window and tabpage management including cursor positioning, dimensions, and window/tabpage-scoped variables.

class Window {
  id: number;

  // Properties
  buffer: AsyncBuffer;
  tabpage: AsyncTabpage;
  cursor: [number, number] | Promise<[number, number]>;
  height: number | Promise<number>;
  width: number | Promise<number>;
  position: Promise<[number, number]>;
  row: Promise<number>;
  col: Promise<number>;
  valid: Promise<boolean>;
  number: Promise<number>;

  // Methods
  close(force?: boolean): Promise<void>;
  config(options?: object): any;

  // Variables and options (inherited from BaseApi)
  getVar(name: string): Promise<VimValue>;
  setVar(name: string, value: VimValue): Promise<void>;
  getOption(name: string): Promise<VimValue>;
  setOption(name: string, value: VimValue): Promise<void>;
}

class Tabpage {
  // Properties
  windows: Promise<Window[]>;
  window: AsyncWindow;
  valid: Promise<boolean>;
  number: Promise<number>;

  // Variables (inherited from BaseApi)
  getVar(name: string): Promise<VimValue>;
  setVar(name: string, value: VimValue): Promise<void>;
}

Window and Tabpage Operations

Plugin Development

Decorator-based and class-based approaches for creating Neovim remote plugins with commands, functions, and autocommands.

// Decorator-based approach
function Plugin(target: any): any;
function Plugin(options: PluginDecoratorOptions): (target: any) => any;

function Command(name: string, options?: CommandOptions): MethodDecorator;
function Function(name: string, options?: NvimFunctionOptions): MethodDecorator;
function Autocmd(name: string, options: AutocmdOptions): MethodDecorator;

interface PluginDecoratorOptions {
  dev?: boolean;
}

interface CommandOptions {
  sync?: boolean;
  range?: string;
  nargs?: string;
  complete?: string;
}

interface NvimFunctionOptions {
  sync?: boolean;
  range?: [number, number];
  eval?: string;
}

interface AutocmdOptions {
  pattern: string;
  eval?: string;
  sync?: boolean;
}

// Class-based approach
class NvimPlugin {
  filename: string;
  nvim: Neovim;
  instance: any;
  dev: boolean;
  alwaysInit: boolean;

  constructor(filename: string, plugin: any, nvim: Neovim);

  setOptions(options: NvimPluginOptions): void;
  registerAutocmd(name: string, fn: Function | [any, Function], options: AutocmdOptions): void;
  registerCommand(name: string, fn: Function | [any, Function], options?: CommandOptions): void;
  registerFunction(name: string, fn: Function | [any, Function], options?: NvimFunctionOptions): void;
  handleRequest(name: string, type: string, args: any[]): Promise<any>;
}

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

function loadPlugin(filename: string, nvim: Neovim, options?: LoadPluginOptions): NvimPlugin | null;

interface LoadPluginOptions {
  cache?: boolean;
}

Plugin Development

Types

Core Value Type

type VimValue = number | boolean | string | number[] | { [key: string]: any };

API Information Types

interface ApiInfo {
  // Neovim API metadata (version, functions, ui_events, etc.)
  [key: string]: any;
}

interface Channel {
  // Channel information
  [key: string]: any;
}

type Ui = UiAttachOptions & {
  height: number;
  width: number;
  chan?: number;
}

interface Proc {
  // Process information
  [key: string]: any;
}

interface Command {
  name: string;
  nargs: string;
  range: string;
  bang: boolean;
  bar: boolean;
  register: boolean;
  definition: string;
  script_id: number;
  complete?: string | null;
  addr?: any;
  count?: any;
  complete_arg?: any;
}

Window Options

interface OpenWindowOptions {
  external?: boolean;
  focusable?: boolean;
  width?: number;
  height?: number;
  bufpos?: [number, number];
  row?: number;
  col?: number;
  anchor?: string;
  relative?: string;
  style?: string;
  border?: string | string[];
}

interface UiAttachOptions {
  rgb?: boolean;
  override?: boolean;
  ext_cmdline?: boolean;
  ext_hlstate?: boolean;
  ext_linegrid?: boolean;
  ext_messages?: boolean;
  ext_multigrid?: boolean;
  ext_popupmenu?: boolean;
  ext_tabline?: boolean;
  ext_termcolors?: boolean;
  ext_wildmenu?: boolean;
}

Logging

The package uses Winston for logging and monkey-patches console to prevent logs from breaking stdio RPC channels.

Environment Variables:

  • NVIM_NODE_LOG_FILE - Path to log file
  • NVIM_NODE_LOG_LEVEL - Log level (error, warn, info, verbose, debug, silly)
  • ALLOW_CONSOLE - Allow console output (breaks stdio RPC)
  • NVIM_NODE_HOST_DEBUG - Enable debugger for plugin host

Custom Logger:

const nvim = attach({
  proc: nvim_proc,
  options: { logger: customLogger }
});

Important Notes

  • All API methods return Promises and should be awaited
  • The package monkey-patches console by default - use the logger from NeovimClient or pass a custom logger
  • For remote plugins, install globally: npm install -g neovim
  • Buffer events require explicit attachment via buffer.listen() or buffer[ATTACH]()
  • The ATTACH and DETACH symbols must be imported from neovim/lib/api/Buffer (not from main package)
  • Plugin handlers are async by default; use sync: true for blocking behavior
  • UI attachment is for building custom Neovim UIs
  • Use $NVIM_LISTEN_ADDRESS to connect to existing Neovim instances via socket