Vimcraft Docs / vim.cursor - Cursor Rendering API

vim.cursor - Cursor Rendering API

Control cursor position and visual rendering. Designed for animated cursor plugins like smear-cursor.

Getting Position

getPosition()

Get the current logical cursor position.

const { row, col } = vim.cursor.getPosition();
console.log(`Cursor at row ${row}, column ${col}`);
// Both row and col are 0-indexed

Render Override

Override the visual cursor position without changing the logical position. Essential for smooth cursor animations.

setRenderPosition(row, col)

Set where the cursor appears visually.

// Display cursor at row 5, column 10 (visual only)
vim.cursor.setRenderPosition(5, 10);

// The actual cursor position remains unchanged
const actual = vim.cursor.getPosition();
// actual might be { row: 0, col: 0 }

clearRenderPosition()

Remove the render override, returning to normal cursor display.

vim.cursor.clearRenderPosition();
// Cursor now renders at its actual position

Smooth Cursor Animation

Example implementation of a smear-cursor effect:

// Track cursor movement for animation
let animationInProgress = false;

function smoothMoveTo(targetRow: number, targetCol: number) {
  if (animationInProgress) return;
  animationInProgress = true;

  const start = vim.cursor.getPosition();
  const duration = 150; // ms
  const startTime = Date.now();

  function animate() {
    const elapsed = Date.now() - startTime;
    const progress = Math.min(elapsed / duration, 1);

    // Ease-out curve
    const eased = 1 - Math.pow(1 - progress, 3);

    const currentRow = Math.round(
      start.row + (targetRow - start.row) * eased
    );
    const currentCol = Math.round(
      start.col + (targetCol - start.col) * eased
    );

    vim.cursor.setRenderPosition(currentRow, currentCol);

    if (progress < 1) {
      setTimeout(animate, 16); // ~60fps
    } else {
      vim.cursor.clearRenderPosition();
      animationInProgress = false;
    }
  }

  animate();
}

Use with vim.motion

Combine with vim.motion for animated movements:

vim.keymap.set('n', 'j', () => {
  const before = vim.cursor.getPosition();
  vim.motion.down();
  const after = vim.cursor.getPosition();

  // Animate from old to new position
  smoothMoveTo(after.row, after.col);
});

Cursor Trail Effect

Create a cursor trail by rendering multiple positions:

const trailPositions: Array<{row: number, col: number}> = [];
const TRAIL_LENGTH = 5;

vim.api.createAutocmd('CursorMoved', {
  callback: () => {
    const pos = vim.cursor.getPosition();

    trailPositions.unshift({ ...pos });
    if (trailPositions.length > TRAIL_LENGTH) {
      trailPositions.pop();
    }

    // Render trail (implementation depends on layer API)
  }
});

API Reference

FunctionReturnsDescription
getPosition(){ row: number, col: number }Get logical cursor position (0-indexed)
setRenderPosition(row, col)voidOverride visual cursor position
clearRenderPosition()voidRemove render override

Notes

  • Logical vs Visual: getPosition() always returns the actual cursor position, regardless of render override
  • Performance: Render overrides trigger immediate re-rendering
  • Cleanup: Always call clearRenderPosition() when animation completes
  • Coordinates: All positions are 0-indexed

See Also