CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-neovim

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

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/neovim@5.4.x
Publish Source
CLI
Badge
tessl/npm-neovim badge