Nvim msgpack API client and remote plugin provider for Node.js
The Window and Tabpage classes represent Neovim windows and tabpages, providing methods for layout management, cursor positioning, dimensions, and scoped variables and options.
/**
* Represents a Neovim window.
* Extends BaseApi and provides window-specific operations.
*/
class Window {
/** Window ID - unique identifier that doesn't change within Vim session */
id: number;
// Properties
/** Buffer displayed in this window */
buffer: AsyncBuffer;
/** Tabpage containing this window */
tabpage: AsyncTabpage;
/** Cursor position [row, col] (1-indexed row, 0-indexed col) */
cursor: [number, number] | Promise<[number, number]>;
/** Window height in rows */
height: number | Promise<number>;
/** Window width in columns */
width: number | Promise<number>;
/** Window position [row, col] */
position: Promise<[number, number]>;
/** Window row position */
row: Promise<number>;
/** Window column position */
col: Promise<number>;
/** Whether window is valid */
valid: Promise<boolean>;
/** Window number */
number: Promise<number>;
/**
* Close the window.
*
* @param force - Force close even if buffer has unsaved changes
*/
close(force?: boolean): Promise<void>;
/**
* Configure window (for floating/external windows).
* Sets window layout and appearance options.
*
* @param options - Window configuration
* @returns Configuration result
*/
config(options?: object): any;
// Inherited from BaseApi
/**
* Get window variable (w:var).
*
* @param name - Variable name
* @returns Variable value
*/
getVar(name: string): Promise<VimValue>;
/**
* Set window variable (w:var).
*
* @param name - Variable name
* @param value - Variable value
*/
setVar(name: string, value: VimValue): Promise<void>;
/**
* Delete window variable (w:var).
*
* @param name - Variable name
*/
deleteVar(name: string): Promise<void>;
/**
* Get window option.
*
* @param name - Option name
* @returns Option value
*/
getOption(name: string): Promise<VimValue>;
/**
* Set window option.
*
* @param name - Option name
* @param value - Option value
*/
setOption(name: string, value: VimValue): Promise<void>;
/**
* Compare with another window.
*
* @param other - Object to compare
* @returns true if windows are equal
*/
equals(other: any): boolean;
/**
* Send RPC request to Neovim for this window.
*
* @param name - API method name
* @param args - Method arguments
* @returns Response from Neovim
*/
request(name: string, args?: any[]): Promise<any>;
/**
* Send notification to Neovim for this window (non-blocking).
*
* @param name - Notification name
* @param args - Notification arguments
*/
notify(name: string, args: any[]): void;
}
type AsyncBuffer = Promise<Buffer> & Omit<Buffer, 'id' | 'isAttached'>;
type AsyncTabpage = Promise<Tabpage>;
type VimValue = number | boolean | string | number[] | { [key: string]: any };Usage Examples:
import { attach } from 'neovim';
const nvim = attach({ proc: nvim_proc });
// Get all windows
const windows = await nvim.windows;
console.log('Window count:', windows.length);
// Get current window
const win = await nvim.window;
// Window ID
console.log('Window ID:', win.id);
// Get window buffer
const buf = await win.buffer;
console.log('Buffer in window:', buf.id);
// Get window dimensions
const height = await win.height;
const width = await win.width;
console.log(`Window size: ${width}x${height}`);
// Set window dimensions
win.height = 20;
win.width = 80;
// Get cursor position
const [row, col] = await win.cursor;
console.log(`Cursor at: ${row}:${col}`);
// Set cursor position (row is 1-indexed, col is 0-indexed)
win.cursor = [10, 5];
// Get window position
const [winRow, winCol] = await win.position;
console.log(`Window at: ${winRow}, ${winCol}`);
// Get window number
const winNum = await win.number;
console.log('Window number:', winNum);
// Check if window is valid
const isValid = await win.valid;
console.log('Window valid:', isValid);
// Close window
await win.close(false); // Close if no unsaved changes
await win.close(true); // Force close
// Set current window
nvim.window = windows[0];
// Iterate through windows
for (const w of windows) {
const num = await w.number;
const buf = await w.buffer;
const bufName = await buf.name;
console.log(`Window ${num}: ${bufName}`);
}const win = await nvim.window;
// Window variables (w:)
await win.setVar('my_window_var', 'value');
const winVar = await win.getVar('my_window_var');
console.log('Window var:', winVar);
await win.deleteVar('my_window_var');
// Window options
await win.setOption('number', true);
await win.setOption('relativenumber', true);
await win.setOption('wrap', false);
await win.setOption('cursorline', true);
const hasNumbers = await win.getOption('number');
console.log('Line numbers:', hasNumbers);
// Common window options
await win.setOption('list', true);
await win.setOption('spell', true);
await win.setOption('foldmethod', 'indent');
await win.setOption('colorcolumn', '80');
// Window-local buffer options
await win.setOption('syntax', 'javascript');
await win.setOption('filetype', 'javascript');const nvim = attach({ proc: nvim_proc });
// Create buffer for floating window
const buf = await nvim.createBuffer(false, true);
await buf.setLines(['Floating window content', 'Line 2', 'Line 3']);
// Open floating window
const floatWin = await nvim.openWindow(buf, true, {
relative: 'editor', // Position relative to editor
width: 50,
height: 10,
row: 5,
col: 10,
anchor: 'NW', // Anchor to NW corner
style: 'minimal', // Minimal UI
border: 'rounded' // Border style
});
// Configure floating window
await floatWin.config({
relative: 'editor',
width: 60,
height: 15,
row: 10,
col: 20,
anchor: 'NW',
style: 'minimal',
border: ['╔', '═', '╗', '║', '╝', '═', '╚', '║']
});
// Position relative to cursor
const cursorFloatWin = await nvim.openWindow(buf, false, {
relative: 'cursor',
width: 30,
height: 5,
row: 1,
col: 0,
anchor: 'NW',
style: 'minimal'
});
// Centered floating window
const [editorHeight, editorWidth] = [50, 150]; // Get from UI
const floatHeight = 20;
const floatWidth = 80;
const centerWin = await nvim.openWindow(buf, true, {
relative: 'editor',
width: floatWidth,
height: floatHeight,
row: Math.floor((editorHeight - floatHeight) / 2),
col: Math.floor((editorWidth - floatWidth) / 2),
anchor: 'NW',
style: 'minimal',
border: 'single'
});
// Close floating window
await floatWin.close(true);const nvim = attach({ proc: nvim_proc });
// Create splits
await nvim.command('vsp'); // Vertical split
await nvim.command('sp'); // Horizontal split
// Get all windows after splits
const windows = await nvim.windows;
console.log('Windows after splits:', windows.length);
// Resize windows
const [win1, win2, win3] = windows;
win1.height = 20;
win1.width = 40;
win2.height = 15;
win3.width = 60;
// Balance window sizes
await nvim.command('wincmd =');
// Navigate between windows
await nvim.command('wincmd w'); // Next window
await nvim.command('wincmd h'); // Left window
await nvim.command('wincmd j'); // Down window
await nvim.command('wincmd k'); // Up window
await nvim.command('wincmd l'); // Right window
// Move windows
await nvim.command('wincmd H'); // Move to far left
await nvim.command('wincmd J'); // Move to far bottom
await nvim.command('wincmd K'); // Move to far top
await nvim.command('wincmd L'); // Move to far right
// Close windows
await win1.close(false);
await nvim.command('only'); // Close all but current windowconst win = await nvim.window;
const buf = await win.buffer;
// Get current cursor position
const [row, col] = await win.cursor;
console.log(`Cursor: line ${row}, column ${col}`);
// Move cursor to specific position
win.cursor = [1, 0]; // Top of file
win.cursor = [10, 5]; // Line 10, column 5
// Move cursor to end of file
const lineCount = await buf.length;
win.cursor = [lineCount, 0];
// Move cursor to end of line
const lines = await buf.lines;
const currentLine = await nvim.call('line', ['.']);
const lineLength = lines[currentLine - 1].length;
win.cursor = [currentLine, lineLength];
// Save and restore cursor position
const savedCursor = await win.cursor;
// ... do operations ...
win.cursor = savedCursor;
// Center cursor in window
await nvim.command('normal! zz');
// Scroll window
await nvim.command('normal! \\<C-d>'); // Scroll down
await nvim.command('normal! \\<C-u>'); // Scroll up
await nvim.command('normal! \\<C-f>'); // Page down
await nvim.command('normal! \\<C-b>'); // Page up/**
* Represents a Neovim tabpage.
* Extends BaseApi but does not support options (getOption/setOption will log errors).
*/
class Tabpage {
// Properties
/** All windows in this tabpage */
windows: Promise<Window[]>;
/** Current window of this tabpage */
window: AsyncWindow;
/** Whether tabpage is valid */
valid: Promise<boolean>;
/** Tabpage number */
number: Promise<number>;
/**
* Get option (not supported - logs error).
* Tabpages do not have options.
*/
getOption(): void;
/**
* Set option (not supported - logs error).
* Tabpages do not have options.
*/
setOption(): void;
// Inherited from BaseApi
/**
* Get tabpage variable (t:var).
*
* @param name - Variable name
* @returns Variable value
*/
getVar(name: string): Promise<VimValue>;
/**
* Set tabpage variable (t:var).
*
* @param name - Variable name
* @param value - Variable value
*/
setVar(name: string, value: VimValue): Promise<void>;
/**
* Delete tabpage variable (t:var).
*
* @param name - Variable name
*/
deleteVar(name: string): Promise<void>;
/**
* Compare with another tabpage.
*
* @param other - Object to compare
* @returns true if tabpages are equal
*/
equals(other: any): boolean;
/**
* Send RPC request to Neovim for this tabpage.
*
* @param name - API method name
* @param args - Method arguments
* @returns Response from Neovim
*/
request(name: string, args?: any[]): Promise<any>;
/**
* Send notification to Neovim for this tabpage (non-blocking).
*
* @param name - Notification name
* @param args - Notification arguments
*/
notify(name: string, args: any[]): void;
}
type AsyncWindow = Promise<Window> & Omit<Window, 'id'>;Usage Examples:
import { attach } from 'neovim';
const nvim = attach({ proc: nvim_proc });
// Get all tabpages
const tabpages = await nvim.tabpages;
console.log('Tabpage count:', tabpages.length);
// Get current tabpage
const tab = await nvim.tabpage;
// Get tabpage number
const tabNum = await tab.number;
console.log('Current tabpage:', tabNum);
// Check if valid
const isValid = await tab.valid;
console.log('Tabpage valid:', isValid);
// Get windows in tabpage
const windows = await tab.windows;
console.log('Windows in tabpage:', windows.length);
// Get current window of tabpage
const currentWin = await tab.window;
console.log('Current window:', currentWin.id);
// Set current tabpage
nvim.tabpage = tabpages[0];
// Iterate through tabpages
for (const t of tabpages) {
const num = await t.number;
const wins = await t.windows;
console.log(`Tabpage ${num}: ${wins.length} windows`);
}
// Tabpage variables (t:)
await tab.setVar('my_tab_var', 'tabpage value');
const tabVar = await tab.getVar('my_tab_var');
console.log('Tab var:', tabVar);
await tab.deleteVar('my_tab_var');
// Note: Tabpages don't support options
// tab.getOption('name') // Will log error
// tab.setOption('name', 'value') // Will log errorconst nvim = attach({ proc: nvim_proc });
// Create new tabpage
await nvim.command('tabnew');
await nvim.command('tabnew file.txt');
// Get tabpages after creation
const tabs = await nvim.tabpages;
console.log('Tabpage count:', tabs.length);
// Switch between tabpages
await nvim.command('tabn'); // Next tabpage
await nvim.command('tabp'); // Previous tabpage
await nvim.command('tabfirst'); // First tabpage
await nvim.command('tablast'); // Last tabpage
await nvim.command('tabn 3'); // Go to tabpage 3
// Set current tabpage
nvim.tabpage = tabs[1];
// Close tabpages
await nvim.command('tabclose'); // Close current
await nvim.command('tabclose 2'); // Close tabpage 2
await nvim.command('tabonly'); // Close all but current
// Move tabpage
await nvim.command('tabmove 0'); // Move to first
await nvim.command('tabmove $'); // Move to last
await nvim.command('tabmove +1'); // Move one right
await nvim.command('tabmove -1'); // Move one left
// Get info about all tabpages
for (const tab of tabs) {
const num = await tab.number;
const windows = await tab.windows;
const currentWin = await tab.window;
console.log(`Tabpage ${num}:`);
console.log(` Windows: ${windows.length}`);
console.log(` Current window: ${currentWin.id}`);
for (const win of windows) {
const buf = await win.buffer;
const bufName = await buf.name;
console.log(` Window ${win.id}: ${bufName}`);
}
}async function navigateWindows(nvim: NeovimClient) {
const windows = await nvim.windows;
console.log('Available windows:');
for (let i = 0; i < windows.length; i++) {
const win = windows[i];
const buf = await win.buffer;
const name = await buf.name || '[No Name]';
const [h, w] = await Promise.all([win.height, win.width]);
console.log(`${i}: ${name} (${w}x${h})`);
}
// Switch to window by index
nvim.window = windows[2];
}async function setupSplitView(nvim: NeovimClient, files: string[]) {
// Close all windows
await nvim.command('only');
// Open files in splits
for (let i = 0; i < files.length; i++) {
if (i > 0) {
await nvim.command('vsp');
}
await nvim.command(`e ${files[i]}`);
}
// Balance window sizes
await nvim.command('wincmd =');
const windows = await nvim.windows;
console.log(`Created ${windows.length} split windows`);
}async function showMenu(nvim: NeovimClient, items: string[]) {
// Create buffer with menu items
const buf = await nvim.createBuffer(false, true);
await buf.setLines(items);
// Calculate dimensions
const maxWidth = Math.max(...items.map(item => item.length));
const height = items.length;
// Create centered floating window
const floatWin = await nvim.openWindow(buf, true, {
relative: 'editor',
width: maxWidth + 4,
height: height + 2,
row: 10,
col: 20,
anchor: 'NW',
style: 'minimal',
border: 'rounded'
});
// Set window options
await floatWin.setOption('cursorline', true);
await floatWin.setOption('number', false);
// Position cursor at first item
floatWin.cursor = [1, 0];
return floatWin;
}async function setupProjectLayout(nvim: NeovimClient) {
// Tab 1: Main code
await nvim.command('tabnew');
await nvim.command('e src/main.ts');
await nvim.command('vsp src/utils.ts');
// Tab 2: Tests
await nvim.command('tabnew');
await nvim.command('e src/main.test.ts');
await nvim.command('sp src/utils.test.ts');
// Tab 3: Documentation
await nvim.command('tabnew');
await nvim.command('e README.md');
// Return to first tab
await nvim.command('tabfirst');
const tabs = await nvim.tabpages;
console.log(`Created ${tabs.length} tabpages`);
// Set tab variables for identification
await tabs[0].setVar('tab_name', 'Code');
await tabs[1].setVar('tab_name', 'Tests');
await tabs[2].setVar('tab_name', 'Docs');
}async function monitorWindowSizes(nvim: NeovimClient) {
setInterval(async () => {
const windows = await nvim.windows;
console.log('Window sizes:');
for (const win of windows) {
const [h, w] = await Promise.all([win.height, win.width]);
const num = await win.number;
console.log(` Window ${num}: ${w}x${h}`);
}
}, 5000);
}config() to reconfigure floating windows after creationvalid property to check if window/tabpage still existswincmd = after resizingInstall with Tessl CLI
npx tessl i tessl/npm-neovim