Skip to content

Plugin API introduction

IdeaVim Bot edited this page Aug 29, 2025 · 3 revisions

Introduction to IdeaVim Plugin Development

⚠️ EXPERIMENTAL API WARNING

The Plugin API is currently in an experimental stage and is not yet recommended for production use.

  • The API is subject to breaking changes without notice
  • Features may be added, modified, or removed in future releases
  • Documentation may not fully reflect the current implementation
  • Use at your own risk for experimental purposes only

We welcome feedback and bug reports to help improve the API, but please be aware that stability is not guaranteed at this time.

This guide explains and gives examples on how to create plugins for IdeaVim, the Vim emulation plugin for IntelliJ-based IDEs. Existing plugins can be found here.

Table of Contents

Introduction

IdeaVim plugins aim to extend the functionality of the IdeaVim plugin, allowing you to add custom Vim-like features to your IntelliJ-based IDE. These plugins can define new commands, mappings, operators, and more, just like Vim plugins do.

The IdeaVim API provides a Kotlin DSL that makes it easy to create new plugins.

Plugin Architecture

IdeaVim plugins are built using a scope-based architecture. Starting scope is the VimApi, which provides access to various aspects of the editor and Vim functionality.

An IdeaVim plugin written with this API consists of:

  1. An entry point function with no parameters and return value annotated with @VimPlugin
  2. One or more scope blocks that define the plugin's functionality
  3. Mappings, commands, or other extensions that users can interact with

Here's a minimal plugin structure:

@VimPlugin(name = "MyPlugin")
fun VimApi.init() {
    // Plugin initialization code
    mappings {
        nmap(keys = "<leader>x", label = "MyPluginAction") {
            // Action implementation
        }
    }
}

Scopes

IdeaVim plugins are written in scopes. They provide a structured way to write code, improve readability and ensure that functions can be called only within a specific scope.

The base scope is VimApi, which provides access to general Vim functionality. From there, plugin writers can access more specialized scopes. The list of all scopes and their functions is available in the API reference (link).

Scopes example

editor {
  // Now in EditorScope
  change {
    // Make changes to the document
    withPrimaryCaret {
      insertText(offset, "New text")
    }
  }
}

mappings {
  // Now in MappingScope
  nmap(keys = "gx", label = "OpenURL") {
    // Action implementation
  }
}

Read and Transaction Operations

In the IdeaVim API there is a distinction between read and write operations:

  • Read operations access the state of the editor without modifying it
  • Transaction operations modify the state of the editor

These operations must be executed under the appropriate locks to ensure thread safety:

// Read operation
val deferred: Deferred<CharSequence> = editor {
  read {
    text  // Get the text of the document
  }
}
runBlocking { println(deferred.await()) }

// Transaction operation
val job: Job = editor {
  change {
    forEachCaret {
      insertText(offset, "Hello, world!")
    }
  }
}
runBlocking { job.join() }
Clone this wiki locally