Vimcraft Docs/Plugin Development

Plugin Development

Build powerful plugins for Vimcraft using JavaScript with hot reload and access to the complete Neovim-compatible API.

Why Build Plugins for Vimcraft?

  • JavaScript-Native - Write plugins in JavaScript with modern ES6+ features.
  • Instant Hot Reload - Save your plugin file and see changes instantly. No restart, no rebuild required.
  • Neovim Compatible - Use the familiar Neovim API. Your knowledge transfers directly.

Getting Started

Vimcraft plugins are JavaScript modules that export a function. The vim global object is automatically available. The simplest plugin looks like this:

// ~/.config/vimcraft/plugins/hello.js

export default function(vim) {
  // Plugin initialization
  vim.notify('Hello from my plugin!');

  // Register a command
  vim.command('SayHello', () => {
    vim.notify('Hello, Vimcraft!');
  });

  // Add a keybinding
  vim.keymap.set('n', '<leader>h', () => {
    vim.cmd('SayHello');
  });
}
Plugin Location

Plugins are loaded from ~/.config/vimcraft/plugins/. Create a .js file in this directory and it will be automatically loaded on startup.

Plugin Structure

A plugin is a module that exports a default function. This function receives the vim API object and can use it to interact with the editor:

// Complete plugin example

export default function(vim) {
  // State management
  let myPluginState = {
    enabled: true,
    count: 0,
  };

  // Listen to events
  vim.on('bufEnter', (buffer) => {
    console.log('Entered buffer:', buffer.name);
  });

  vim.on('bufWrite', async (buffer) => {
    console.log('Buffer saved:', buffer.name);
  });

  // Register commands
  vim.command('ToggleMyPlugin', () => {
    myPluginState.enabled = !myPluginState.enabled;
    vim.notify(`Plugin ${myPluginState.enabled ? 'enabled' : 'disabled'}`);
  });

  // Create custom keybindings
  vim.keymap.set('n', '<leader>mp', () => {
    vim.cmd('ToggleMyPlugin');
  });

  // Access current buffer and window
  const currentBuffer = vim.api.getCurrentBuffer();
  const currentWindow = vim.api.getCurrentWindow();
}

Plugin Configuration

You can create configurable plugins by managing state within your plugin:

// my-plugin.js

export default function(vim) {
  const config = {
    enabled: true,
    autoSave: true,
    delay: 1000,
  };

  // Event handlers
  vim.on('bufWrite', async (buffer) => {
    if (config.autoSave) {
      vim.notify('File saved!');
    }
  });

  // Keybindings
  vim.keymap.set('n', '<leader>t', () => {
    config.enabled = !config.enabled;
    vim.notify(`Plugin ${config.enabled ? 'enabled' : 'disabled'}`);
  });
}

Hot Reload Development

During development, Vimcraft automatically reloads your plugins when you save them. No need to restart the editor:

  1. Create your plugin file in ~/.config/vimcraft/plugins/
  2. Edit and save - Vimcraft automatically reloads the plugin
  3. Check the console (:messages) for any errors
  4. Use console.log() for debugging - output appears in :messages

Debugging with Chrome DevTools

Vimcraft's --debug mode opens Chrome DevTools for enhanced debugging:

# Start Vimcraft in debug mode
vimcraft --debug myfile.txt

Chrome DevTools will automatically open with console access for logging and debugging.

Currently available:

  • Console access - Full JavaScript console with console.log(), console.error(), etc.
  • Live inspection - View logged objects and values in real-time

Coming soon:

  • Breakpoints and step-through debugging
  • Variable inspection and watch expressions
  • Performance profiling and memory analysis
  • Network monitoring
Development Status

Full debugging features (breakpoints, step-through, profiling) are planned and coming soon. For now, use console.log() for debugging - all output appears in the DevTools console.

Example Plugins

Auto-save Plugin


export default function(vim) {
  let timer = null;
  const SAVE_DELAY = 1000; // 1 second

  vim.on('textChanged', () => {
    if (timer) clearTimeout(timer);

    timer = setTimeout(() => {
      vim.cmd('write');
      vim.notify('Auto-saved', { timeout: 1000 });
    }, SAVE_DELAY);
  });
}

Project-wide Find & Replace


export default function(vim) {
  vim.command('FindReplace', async (args) => {
    const [find, replace] = args.split(' ');
    const files = await vim.fs.glob('**/*.{ts,js,tsx,jsx}');

    let count = 0;
    for (const file of files) {
      const content = await vim.fs.readFile(file);
      const newContent = content.replaceAll(find, replace);

      if (content !== newContent) {
        await vim.fs.writeFile(file, newContent);
        count++;
      }
    }

    vim.notify(`Replaced in ${count} files`);
  });
}

Next Steps