The Buffer class represents a Neovim buffer and provides methods for manipulating buffer content, managing highlights and virtual text, handling buffer events, and accessing buffer-scoped variables and options.
/**
* Represents a Neovim buffer.
* Extends BaseApi and provides buffer-specific operations.
*/
class Buffer {
/** Buffer number (bufnr) - unique identifier that doesn't change */
id: number;
/** Whether buffer has event listeners attached */
isAttached: boolean;
// Properties
/** Total number of lines in buffer */
length: Promise<number>;
/** All buffer lines */
lines: Promise<string[]>;
/** Buffer name (full file path or empty for unnamed buffers) */
name: string | Promise<string>;
/** Buffer change tick (increments on each change) */
changedtick: Promise<number>;
/** Whether buffer handle is valid */
valid: Promise<boolean>;
/** Whether buffer is loaded in memory */
loaded: Promise<boolean>;
/** Buffer-local commands */
commands: Promise<Record<string, any>>;
// Inherited from BaseApi
/**
* Get buffer variable (b:var).
*
* @param name - Variable name
* @returns Variable value
*/
getVar(name: string): Promise<VimValue>;
/**
* Set buffer variable (b:var).
*
* @param name - Variable name
* @param value - Variable value
*/
setVar(name: string, value: VimValue): Promise<void>;
/**
* Delete buffer variable (b:var).
*
* @param name - Variable name
*/
deleteVar(name: string): Promise<void>;
/**
* Get buffer option.
*
* @param name - Option name
* @returns Option value
*/
getOption(name: string): Promise<VimValue>;
/**
* Set buffer option.
*
* @param name - Option name
* @param value - Option value
*/
setOption(name: string, value: VimValue): Promise<void>;
/**
* Compare with another buffer.
*
* @param other - Object to compare
* @returns true if buffers are equal
*/
equals(other: any): boolean;
/**
* Send RPC request to Neovim for this buffer.
*
* @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 buffer (non-blocking).
*
* @param name - Notification name
* @param args - Notification arguments
*/
notify(name: string, args: any[]): void;
}
type VimValue = number | boolean | string | number[] | { [key: string]: any };Reading and modifying buffer lines.
/**
* Get lines from buffer within a range.
*
* @param options - Range options
* @param options.start - Start line index (0-based, default: 0)
* @param options.end - End line index (exclusive, default: -1 for end of buffer)
* @param options.strictIndexing - Throw error if range is invalid (default: true)
* @returns Array of line strings
*/
getLines(options?: { start?: number; end?: number; strictIndexing?: boolean }): Promise<string[]>;
/**
* Set lines in buffer within a range.
* Replaces lines from start to end (exclusive) with provided lines.
*
* @param lines - Lines to set (string or array of strings)
* @param options - Range options
* @param options.start - Start line index (default: 0)
* @param options.end - End line index (exclusive, default: -1)
* @param options.strictIndexing - Throw error if range is invalid (default: true)
*/
setLines(lines: string | string[], options?: BufferSetLines): Promise<void>;
/**
* Insert lines at a specific index.
* Existing lines at and after the index are shifted down.
*
* @param lines - Lines to insert
* @param start - Index to insert at (0-based)
*/
insert(lines: string[] | string, start: number): Promise<void>;
/**
* Replace lines starting at index.
* Replaces as many lines as provided in the array.
*
* @param lines - Lines to set
* @param start - Starting index (0-based)
*/
replace(lines: string[] | string, start: number): Promise<void>;
/**
* Remove lines from buffer.
*
* @param start - Start line index (0-based, inclusive)
* @param end - End line index (exclusive)
* @param strictIndexing - Throw error if range is invalid
*/
remove(start: number, end: number, strictIndexing?: boolean): Promise<void>;
/**
* Append lines to end of buffer.
*
* @param lines - Lines to append
*/
append(lines: string[] | string): Promise<void>;
interface BufferSetLines {
start?: number;
end?: number;
strictIndexing?: boolean;
}Usage Examples:
import { attach } from 'neovim';
const nvim = attach({ proc: nvim_proc });
const buf = await nvim.buffer;
// Get all lines
const allLines = await buf.lines;
console.log('Lines:', allLines);
// Get specific range
const firstFiveLines = await buf.getLines({ start: 0, end: 5 });
console.log('First 5:', firstFiveLines);
// Get from line 10 to end
const fromTen = await buf.getLines({ start: 10, end: -1 });
// Set all lines
await buf.setLines(['Line 1', 'Line 2', 'Line 3']);
// Set specific range (replace lines 0-2)
await buf.setLines(['New line 1', 'New line 2'], { start: 0, end: 2 });
// Set single line
await buf.setLines('Single line', { start: 0, end: 1 });
// Insert at beginning
await buf.insert(['Header line'], 0);
// Insert at line 5
await buf.insert(['Inserted line'], 5);
// Replace from line 2
await buf.replace(['Replacement 1', 'Replacement 2'], 2);
// Remove lines 5-10
await buf.remove(5, 10);
// Remove all lines
const lineCount = await buf.length;
await buf.remove(0, lineCount);
// Append to end
await buf.append(['Appended line 1', 'Appended line 2']);
// Append single line
await buf.append('Single appended line');
// Get line count
const count = await buf.length;
console.log('Line count:', count);
// Working with large buffers efficiently
const chunkSize = 1000;
for (let i = 0; i < 10000; i += chunkSize) {
const lines = await buf.getLines({
start: i,
end: Math.min(i + chunkSize, await buf.length)
});
// Process chunk
}Querying buffer metadata and properties.
/**
* Get buffer-local commands.
*
* @param options - Optional filter options
* @returns Map of command name to command info
*/
getCommands(options?: object): Promise<Record<string, any>>;
/**
* Get position of a mark in buffer.
*
* @param name - Mark name (single character)
* @returns [row, col] position (1-indexed row, 0-indexed col)
*/
mark(name: string): Promise<[number, number]>;
/**
* Get buffer-local key mappings for a mode.
*
* @param mode - Mode ('n', 'v', 'i', 'x', etc.)
* @returns Array of mapping objects
*/
getKeymap(mode: string): Promise<object[]>;
/**
* Get byte offset of a line.
*
* @param index - Line index (0-based)
* @returns Byte offset from start of buffer
*/
getOffset(index: number): Promise<number>;Usage Examples:
const buf = await nvim.buffer;
// Get buffer name
const name = await buf.name;
console.log('Buffer name:', name);
// Set buffer name
buf.name = '/path/to/file.txt';
// Check if buffer is valid
const isValid = await buf.valid;
if (!isValid) {
console.log('Buffer was deleted');
}
// Check if loaded
const isLoaded = await buf.loaded;
console.log('Buffer loaded:', isLoaded);
// Get change tick
const tick = await buf.changedtick;
console.log('Change tick:', tick);
// Get commands
const commands = await buf.commands;
console.log('Buffer commands:', Object.keys(commands));
const allCommands = await buf.getCommands();
console.log('All buffer commands:', allCommands);
// Get mark position
const markPos = await buf.mark('a');
console.log('Mark a at:', markPos); // [row, col]
// Get keymaps
const normalMaps = await buf.getKeymap('n');
console.log('Normal mode buffer mappings:', normalMaps);
// Get byte offset
const offset = await buf.getOffset(10);
console.log('Line 10 starts at byte:', offset);
// Buffer ID
console.log('Buffer number:', buf.id);Adding and managing buffer highlights.
/**
* Add a highlight to buffer.
* Returns highlight ID that can be used to clear the highlight.
*
* @param options - Highlight options
* @param options.hlGroup - Highlight group name
* @param options.line - Line number (0-based)
* @param options.colStart - Start column (0-based, default: 0)
* @param options.colEnd - End column (exclusive, default: -1 for end of line)
* @param options.srcId - Source/namespace ID (default: -1 for new namespace)
* @returns Highlight ID
*/
addHighlight(options: BufferHighlight): Promise<number>;
/**
* Clear highlights in namespace.
*
* @param args - Clear options
* @param args.nsId - Namespace ID
* @param args.lineStart - Start line (default: 0)
* @param args.lineEnd - End line (exclusive, default: -1 for end)
*/
clearNamespace(args: BufferClearNamespace): void;
/**
* Clear highlights (deprecated - use clearNamespace instead).
*
* @param args - Clear options
* @param args.srcId - Source ID
* @param args.lineStart - Start line
* @param args.lineEnd - End line
*/
clearHighlight(args?: BufferClearHighlight): Promise<void>;
interface BufferHighlight {
hlGroup: string;
line: number;
colStart?: number;
colEnd?: number;
srcId?: number;
}
interface BufferClearNamespace {
nsId: number;
lineStart?: number;
lineEnd?: number;
}
interface BufferClearHighlight {
srcId?: number;
lineStart?: number;
lineEnd?: number;
}Usage Examples:
const buf = await nvim.buffer;
// Create namespace
const ns = await nvim.createNamespace('my-highlights');
// Add highlight to entire line
const hlId1 = await buf.addHighlight({
hlGroup: 'Error',
line: 0,
srcId: ns
});
// Add highlight to specific columns
const hlId2 = await buf.addHighlight({
hlGroup: 'Search',
line: 5,
colStart: 10,
colEnd: 20,
srcId: ns
});
// Highlight multiple lines
for (let i = 0; i < 10; i++) {
await buf.addHighlight({
hlGroup: 'Comment',
line: i,
colStart: 0,
colEnd: -1, // End of line
srcId: ns
});
}
// Clear all highlights in namespace
await buf.clearNamespace({ nsId: ns });
// Clear highlights in line range
await buf.clearNamespace({
nsId: ns,
lineStart: 0,
lineEnd: 10
});
// Highlight search results
const lines = await buf.lines;
const searchTerm = 'TODO';
for (let i = 0; i < lines.length; i++) {
let index = 0;
while ((index = lines[i].indexOf(searchTerm, index)) !== -1) {
await buf.addHighlight({
hlGroup: 'Todo',
line: i,
colStart: index,
colEnd: index + searchTerm.length,
srcId: ns
});
index += searchTerm.length;
}
}
// Clear old highlights (deprecated method)
await buf.clearHighlight({ srcId: ns });Adding virtual text (text displayed but not in buffer).
/**
* Set virtual text on a line.
* Virtual text appears after the line content but is not part of the buffer.
*
* @param nsId - Namespace ID
* @param line - Line number (0-based)
* @param chunks - Array of [text, highlight] tuples
* @param opts - Optional configuration
* @returns ID of the virtual text
*/
setVirtualText(nsId: number, line: number, chunks: VirtualTextChunk[], opts?: object): Promise<number>;
type VirtualTextChunk = [string, string]; // [text, highlight_group]Usage Examples:
const buf = await nvim.buffer;
const ns = await nvim.createNamespace('my-virtual-text');
// Add simple virtual text
await buf.setVirtualText(ns, 0, [['← This is virtual text', 'Comment']]);
// Add multi-styled virtual text
await buf.setVirtualText(ns, 5, [
['Error: ', 'ErrorMsg'],
['Line too long', 'WarningMsg']
]);
// Add diagnostic messages
const diagnostics = [
{ line: 10, message: 'Unused variable', severity: 'Warning' },
{ line: 25, message: 'Syntax error', severity: 'Error' }
];
for (const diag of diagnostics) {
const hlGroup = diag.severity === 'Error' ? 'ErrorMsg' : 'WarningMsg';
await buf.setVirtualText(ns, diag.line, [
[` ${diag.message}`, hlGroup]
]);
}
// Clear virtual text (clear entire namespace)
await buf.clearNamespace({ nsId: ns });
// Add inline hints
const lines = await buf.lines;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Simple type inference example
if (line.includes('const')) {
await buf.setVirtualText(ns, i, [
[' : string', 'Type']
]);
}
}Listening to buffer events in real-time.
/**
* Listen to buffer events.
* Available events: 'lines', 'changedtick', 'detach', 'reload'
*
* @param eventName - Event name
* @param cb - Callback function
* @returns Detach function
*/
listen(eventName: string, cb: Function): Function;
/**
* Stop listening to buffer events.
*
* @param eventName - Event name
* @param cb - Callback function
*/
unlisten(eventName: string, cb: Function): void;
/**
* Attach to buffer events (Symbol method).
* Must be called before listen() will work.
*
* @param sendBuffer - Whether to send buffer in events
* @param options - Attachment options
* @returns true if successfully attached
*/
\[ATTACH\]\(sendBuffer?: boolean, options?: object\): Promise<boolean>;
/**
* Detach from buffer events (Symbol method).
*/
[DETACH](): void;
// Symbol constants
const ATTACH: unique symbol;
const DETACH: unique symbol;Usage Examples:
import { attach } from 'neovim';
import { ATTACH, DETACH } from 'neovim/lib/api/Buffer';
const nvim = attach({ proc: nvim_proc });
const buf = await nvim.buffer;
// Attach to buffer events (required before listening)
await buf[ATTACH]();
// Listen to line changes
const detachLines = buf.listen('lines', (
buffer: Buffer,
tick: number,
firstLine: number,
lastLine: number,
lineData: string[],
more: boolean
) => {
console.log('Lines changed:', {
tick,
firstLine,
lastLine,
newLines: lineData,
more
});
});
// Listen to change tick updates
buf.listen('changedtick', (buffer: Buffer, tick: number) => {
console.log('Buffer changed, tick:', tick);
});
// Listen to buffer detach
buf.listen('detach', (buffer: Buffer) => {
console.log('Buffer detached');
});
// Listen to buffer reload
buf.listen('reload', (buffer: Buffer) => {
console.log('Buffer reloaded');
});
// Stop listening to specific event
buf.unlisten('lines', detachLines);
// Or call the detach function returned by listen()
detachLines();
// Detach from all buffer events
buf[DETACH]();
// Check if attached
console.log('Buffer attached:', buf.isAttached);
// Example: Auto-save on changes
await buf[ATTACH]();
let saveTimer: NodeJS.Timeout;
buf.listen('lines', () => {
// Debounce save
clearTimeout(saveTimer);
saveTimer = setTimeout(async () => {
await nvim.command('write');
console.log('Auto-saved');
}, 1000);
});
// Example: Track changes for undo
const changes: any[] = [];
await buf[ATTACH]();
buf.listen('lines', (buffer, tick, firstLine, lastLine, lineData) => {
changes.push({
timestamp: Date.now(),
tick,
range: [firstLine, lastLine],
lines: lineData
});
});Managing buffer-scoped variables and options.
// Inherited from BaseApi
getVar(name: string): Promise<VimValue>;
setVar(name: string, value: VimValue): Promise<void>;
deleteVar(name: string): Promise<void>;
getOption(name: string): Promise<VimValue>;
setOption(name: string, value: VimValue): Promise<void>;Usage Examples:
const buf = await nvim.buffer;
// Buffer variables (b:)
await buf.setVar('my_buffer_var', 'hello');
const myVar = await buf.getVar('my_buffer_var');
console.log('Buffer var:', myVar);
await buf.setVar('line_count', await buf.length);
await buf.deleteVar('my_buffer_var');
// Buffer options
await buf.setOption('filetype', 'javascript');
await buf.setOption('expandtab', true);
await buf.setOption('tabstop', 2);
const ft = await buf.getOption('filetype');
console.log('Filetype:', ft);
const modified = await buf.getOption('modified');
console.log('Buffer modified:', modified);
// Common buffer options
await buf.setOption('readonly', false);
await buf.setOption('modifiable', true);
await buf.setOption('buftype', 'nofile');
await buf.setOption('swapfile', false);
// Check various options
const options = ['filetype', 'fileencoding', 'fileformat', 'modified', 'modifiable'];
for (const opt of options) {
const value = await buf.getOption(opt);
console.log(`${opt}:`, value);
}const buf = await nvim.buffer;
const ns = await nvim.createNamespace('syntax');
const lines = await buf.lines;
// Simple keyword highlighting
const keywords = ['function', 'const', 'let', 'var', 'if', 'else', 'return'];
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
const line = lines[lineNum];
for (const keyword of keywords) {
let index = 0;
while ((index = line.indexOf(keyword, index)) !== -1) {
await buf.addHighlight({
hlGroup: 'Keyword',
line: lineNum,
colStart: index,
colEnd: index + keyword.length,
srcId: ns
});
index += keyword.length;
}
}
}const buf = await nvim.buffer;
const diagNs = await nvim.createNamespace('diagnostics');
const vtextNs = await nvim.createNamespace('diagnostics-vtext');
async function updateDiagnostics() {
// Clear old diagnostics
await buf.clearNamespace({ nsId: diagNs });
await buf.clearNamespace({ nsId: vtextNs });
const lines = await buf.lines;
// Run linter/checker
const diagnostics = await runLinter(lines);
for (const diag of diagnostics) {
// Add highlight
await buf.addHighlight({
hlGroup: diag.severity === 'error' ? 'ErrorMsg' : 'WarningMsg',
line: diag.line,
colStart: diag.colStart,
colEnd: diag.colEnd,
srcId: diagNs
});
// Add virtual text
await buf.setVirtualText(vtextNs, diag.line, [
[` ${diag.message}`, diag.severity === 'error' ? 'ErrorMsg' : 'WarningMsg']
]);
}
}
// Update on changes
await buf[ATTACH]();
buf.listen('lines', () => {
updateDiagnostics();
});const buf1 = await nvim.createBuffer(true, false);
const buf2 = await nvim.createBuffer(true, false);
await buf1.setLines(['line 1', 'line 2', 'line 3']);
await buf2.setLines(['line 1', 'line 2 modified', 'line 3']);
// Compare buffers
const lines1 = await buf1.lines;
const lines2 = await buf2.lines;
const diffNs = await nvim.createNamespace('diff');
for (let i = 0; i < Math.max(lines1.length, lines2.length); i++) {
if (lines1[i] !== lines2[i]) {
// Highlight differences
await buf1.addHighlight({
hlGroup: 'DiffDelete',
line: i,
srcId: diffNs
});
await buf2.addHighlight({
hlGroup: 'DiffAdd',
line: i,
srcId: diffNs
});
}
}buf[ATTACH]()ATTACH and DETACH symbols must be imported separatelylines, name, length return Promisesname = "file" trigger async operationsclearNamespace() instead of deprecated clearHighlight()