Vimcraft Docs/Plugin Development

Plugin Development

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

Why Build Plugins for Vimcraft?

  • TypeScript-First - Write plugins in TypeScript with full IntelliSense and type checking.
  • 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.
  • Cross-Plugin Communication - Import and share functionality between plugins with require().

Getting Started

// ~/.config/vimcraft/plugins/hello.ts

// Plugin initialization
console.log('Hello from my plugin!');

// Register a command
vim.api.createUserCmd('SayHello', () => {
  console.log('Hello, Vimcraft!');
}, { desc: 'Say hello' });

// Add a keybinding
vim.keymap.set('n', '<leader>h', () => {
  vim.cmd('SayHello');
}, { desc: 'Say hello' });

JavaScript Plugin

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

console.log('Hello from my plugin!');

vim.api.createUserCmd('SayHello', () => {
  console.log('Hello, Vimcraft!');
}, { desc: 'Say hello' });

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

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

Plugin Structure

Plugins run in a global scope with the vim object automatically available:

// ~/.config/vimcraft/plugins/my-plugin.ts

// State management
let myPluginState = {
  enabled: true,
  count: 0,
};

// Listen to events via autocommands
vim.api.createAutocmd('BufEnter', {
  callback: (args) => {
    console.log('Entered buffer:', args.file);
  }
});

vim.api.createAutocmd('BufWritePost', {
  callback: (args) => {
    console.log('Buffer saved:', args.file);
  }
});

// Register commands
vim.api.createUserCmd('ToggleMyPlugin', () => {
  myPluginState.enabled = !myPluginState.enabled;
  console.log(`Plugin ${myPluginState.enabled ? 'enabled' : 'disabled'}`);
}, { desc: 'Toggle my plugin' });

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

// Access current buffer and window
const currentBuffer = vim.api.getCurrentBuf();
const currentWindow = vim.api.getCurrentWin();

Cross-Plugin Communication

Use require() to share functionality between plugins:

Exporting from a Plugin

// ~/.config/vimcraft/plugins/utils.ts

// Export utilities via module.exports
module.exports = {
  formatDate: (date: Date) => date.toISOString().split('T')[0],

  showNotification: (msg: string) => {
    console.log(`[Notification] ${msg}`);
  },

  debounce: (fn: Function, ms: number) => {
    let timeout: ReturnType<typeof setTimeout>;
    return (...args: any[]) => {
      clearTimeout(timeout);
      timeout = setTimeout(() => fn(...args), ms);
    };
  }
};

Importing in Another Plugin

// ~/.config/vimcraft/plugins/my-plugin.ts

const utils = require('./utils');

vim.api.createUserCmd('ShowDate', () => {
  const today = utils.formatDate(new Date());
  utils.showNotification(`Today is ${today}`);
}, { desc: 'Show current date' });

Plugin Configuration

Create configurable plugins by managing state:

// ~/.config/vimcraft/plugins/my-plugin.ts

const config = {
  enabled: true,
  autoSave: true,
  delay: 1000,
};

// Event handlers
vim.api.createAutocmd('BufWritePost', {
  callback: (args) => {
    if (config.autoSave) {
      console.log('File saved:', args.file);
    }
  }
});

// Toggle keybinding
vim.keymap.set('n', '<leader>t', () => {
  config.enabled = !config.enabled;
  console.log(`Plugin ${config.enabled ? 'enabled' : 'disabled'}`);
}, { desc: 'Toggle plugin' });

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

// ~/.config/vimcraft/plugins/auto-save.ts

let timer: ReturnType<typeof setTimeout> | null = null;
const SAVE_DELAY = 1000; // 1 second

vim.api.createAutocmd('TextChanged', {
  callback: () => {
    if (timer) clearTimeout(timer);

    timer = setTimeout(() => {
      vim.cmd('write');
      console.log('Auto-saved');
    }, SAVE_DELAY);
  }
});

Word Counter Plugin

// ~/.config/vimcraft/plugins/word-count.ts

function countWords(): number {
  const content = vim.buffer.getContent();
  const text = new TextDecoder().decode(content);
  return text.split(/\s+/).filter(w => w.length > 0).length;
}

vim.api.createUserCmd('WordCount', () => {
  const count = countWords();
  console.log(`Word count: ${count}`);
}, { desc: 'Count words in buffer' });

vim.keymap.set('n', '<leader>wc', () => {
  vim.cmd('WordCount');
}, { desc: 'Count words' });

Filetype-Specific Settings

// ~/.config/vimcraft/plugins/ft-settings.ts

vim.api.createAutocmd('FileType', {
  pattern: 'typescript',
  callback: () => {
    vim.opt.tabStop = 2;
    vim.opt.shiftWidth = 2;
    vim.opt.expandTab = true;

    vim.keymap.set('n', '<leader>r', () => {
      vim.cmd('!npx ts-node %');
    }, { buffer: true, desc: 'Run TypeScript file' });
  }
});

vim.api.createAutocmd('FileType', {
  pattern: 'python',
  callback: () => {
    vim.opt.tabStop = 4;
    vim.opt.shiftWidth = 4;

    vim.keymap.set('n', '<leader>r', () => {
      vim.cmd('!python3 %');
    }, { buffer: true, desc: 'Run Python file' });
  }
});

Testing Plugins

Use the E2E testing framework to test your plugins:

// tests/my-plugin.ts

vim.e2e.describe('My Plugin', function() {
  vim.e2e.test('WordCount command works', function() {
    vim.e2e.keys('iHello World<Esc>');
    vim.cmd('WordCount');
    // Check console output or state
  });
});

vim.e2e.runAll();

Run tests with:

vimc test tests/

Next Steps