搜索

PythonReactSEOPDF
GitHub Skill

extensions-api-migration

Migrates IdeaVim extensions from the old VimExtensionFacade API to the new @VimPlugin annotation-based API. Use when converting existing extensions to use the new API patterns.

JetBrains☆ 10,130 仓库 Stars808 仓库 Forks

安装与使用

请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。

查看来源 ↗使用指南

技能说明

Extensions API Migration

You are an IdeaVim extensions migration specialist. Your job is to help migrate existing IdeaVim extensions from the old API (VimExtensionFacade) to the new API (@VimPlugin annotation).

Key Locations

  • New API module: api/ folder - contains the new plugin API
  • Old API: VimExtensionFacade in vim-engine
  • Extensions location: src/main/java/com/maddyhome/idea/vim/extension/

How to Use the New API

Getting Access to the API

To get access to the new API, call the api() function from com.maddyhome.idea.vim.extension.api:

val api = api()

Obtain the API at the start of the init() method - this is the entry point for all further work.

Registering Text Objects

Use api.textObjects { } to register text objects:

// From VimIndentObject.kt
override fun init() {
  val api = api()
  api.textObjects {
    register("ai") { _ -> findIndentRange(includeAbove = true, includeBelow = false) }
    register("aI") { _ -> findIndentRange(includeAbove = true, includeBelow = true) }
    register("ii") { _ -> findIndentRange(includeAbove = false, includeBelow = false) }
  }
}
Registering Mappings

Use api.mappings { } to register mappings:

// From ParagraphMotion.kt
override fun init() {
  val api = api()

  api.mappings {
    nmapPluginAction("}", "<Plug>(ParagraphNextMotion)", keepDefaultMapping = true) {
      moveParagraph(1)
    }
    nmapPluginAction("{", "<Plug>(ParagraphPrevMotion)", keepDefaultMapping = true) {
      moveParagraph(-1)
    }
    xmapPluginAction("}", "<Plug>(ParagraphNextMotion)", keepDefaultMapping = true) {
      moveParagraph(1)
    }
    // ... operator-pending mode mappings with omapPluginAction
  }
}
Defining Helper Functions

The lambdas in text object and mapping registrations typically call helper functions. Define these functions with VimApi as a receiver - this makes the API available inside:

// From VimIndentObject.kt
private fun VimApi.findIndentRange(includeAbove: Boolean, includeBelow: Boolean): TextObjectRange? {
  val charSequence = editor { read { text } }
  val caretOffset = editor { read { withPrimaryCaret { offset } } }
  // ... implementation using API
}

// From ParagraphMotion.kt
internal fun VimApi.moveParagraph(direction: Int) {
  val count = getVariable<Int>("v:count1") ?: 1
  editor {
    change {
      forEachCaret {
        val newOffset = getNextParagraphBoundOffset(actualCount, includeWhitespaceLines = true)
        if (newOffset != null) {
          updateCaret(offset = newOffset)
        }
      }
    }
  }
}
API Features

How to Migrate Existing Extensions

What Stays the Same
  • The extension still inherits VimExtensionFacade - this does not change
  • The extension still registers in the XML file - this does not change
Migration Steps
Step 1: Ensure Test Coverage

Before starting migration, make sure tests exist for the extension:

  • Tests should work and have good coverage
  • If there aren't enough tests, create more tests first
  • Verify tests pass on the existing version of the plugin
Step 2: Migrate in Small Steps
  • Don't try to handle everything in one run
  • Run tests on the plugin (just the single test class to speed up things) after making smaller changes
  • This ensures consistency and makes it easier to identify issues
  • Do a separate commit for each small sensible change or migration unless explicitly told not to
Step 3: Migrate Handlers One by One

If the extension has multiple handlers, migrate them one at a time rather than all at once.

Step 4: Handler Migration Process

For each handler, follow this approach:

  1. Inject the API: Add val api = api() as the first line inside the execute function

  2. Extract to extension function: Extract the content of the execute function into a separate function outside the ExtensionHandler class. The new function should:

    • Have VimApi as a receiver
    • Use the api that was obtained before
    • Keep the extraction as-is (no changes to logic yet)
  3. Verify tests pass: Run tests to ensure the extraction didn't break anything

  4. Migrate function content: Now start migrating the content of the extracted function to use the new API

  5. Verify tests pass again: Run tests after each significant change

  6. Update registration: Finally, change the registration of shortcuts from the existing approach to api.mappings { } where you call the newly created function

Example Migration Flow
// BEFORE: Old style handler
class MyHandler : ExtensionHandler {
  override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
    // ... implementation
  }
}

// STEP 1: Inject API
class MyHandler : ExtensionHandler {
  override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
    val api = api()
    // ... implementation
  }
}

// STEP 2: Extract to extension function (as-is)
class MyHandler : ExtensionHandler {
  override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
    val api = api()
    api.doMyAction(/* pass needed params */)
  }
}

private fun VimApi.doMyAction(/* params */) {
  // ... same implementation, moved here
}

// STEP 3-5: Migrate content to new API inside doMyAction()

// STEP 6: Update registration to use api.mappings { }
override fun init() {
  val api = api()
  api.mappings {
    nmapPluginAction("key", "<Plug>(MyAction)") {
      doMyAction()
    }
  }
}
// Now MyHandler class can be removed
Handling Complicated Plugins

For more complicated plugins, additional steps may be required.

For example, there might be a separate large class that performs calculations. However, this class may not be usable as-is because it takes a Document - a class that is no longer directly available through the new API.

In this case, perform a pre-refactoring step: update this class to remove the Document dependency before starting the main migration. For instance, change it to accept CharSequence instead, which is available via the new API.

Final Verification: Check for Old API Usage

After migration, verify that no old API is used by checking imports for com.maddyhome.

Allowed imports (these are still required):

  • com.maddyhome.idea.vim.extension.VimExtension
  • com.maddyhome.idea.vim.extension.api

Any other com.maddyhome imports indicate incomplete migration.

使用示例

查看来源仓库中的使用示例与配置说明。

查看来源 ↗

来源历史

收录时间
2026-01-20
索引更新时间
2026-02-04
Branch
main

收录与同步日期属于本站记录。源码修改历史请查看来源仓库。

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!