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

window-tabpage.mddocs/

Window and Tabpage Operations

The Window and Tabpage classes represent Neovim windows and tabpages, providing methods for layout management, cursor positioning, dimensions, and scoped variables and options.

Capabilities

Window Class

/**
 * 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}`);
}

Window Variables and Options

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');

Floating Windows

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);

Window Layout Management

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 window

Window Cursor Management

const 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

Tabpage Class

/**
 * 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 error

Tabpage Management

const 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}`);
  }
}

Practical Examples

Window Navigator

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];
}

Split View Manager

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`);
}

Floating Window Menu

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;
}

Multi-Tabpage Project Layout

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');
}

Window Size Monitor

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);
}

Important Notes

Windows

  • Window IDs are persistent within a Vim session
  • Window numbers can change when windows are opened/closed
  • Cursor position: row is 1-indexed, column is 0-indexed
  • Window dimensions are in character cells (not pixels)
  • Floating windows require Neovim 0.4+
  • Use config() to reconfigure floating windows after creation
  • Window variables (w:) are window-local, not buffer-local
  • Window options affect display of buffer in that window
  • Some options are window-local, others are buffer-local
  • Closing a window doesn't close the buffer

Tabpages

  • Tabpages do NOT support options (getOption/setOption log errors)
  • Tabpage numbers can change when tabpages are opened/closed
  • Each tabpage has its own set of windows
  • Tabpage variables (t:) are tabpage-local
  • Current window is per-tabpage (each tabpage remembers its current window)
  • Closing a tabpage closes all its windows
  • At least one tabpage must always exist

Best Practices

  • Cache window/tabpage references when doing multiple operations
  • Use valid property to check if window/tabpage still exists
  • Balance window sizes with wincmd = after resizing
  • Use tabpage/window variables to store metadata
  • Set window options (not buffer options) for display preferences
  • Use floating windows for temporary UI elements
  • Always handle the case where windows/tabpages may have been closed