nvim-mybatis is a focused Neovim plugin for jumping between Java MyBatis mapper interfaces and XML statements, with automatic indexing, persisted project snapshots, save-time refreshes, and background manual reindexing. v0.3.2 keeps the release line intentionally narrow, focused on the standard mapper contract rather than broader framework heuristics.
- Install the plugin.
- Open a Java mapper interface or MyBatis XML mapper.
- Run
:MyBatisJumpto jump to the paired location. - The first full project index is written to
<project-root>/.nvim_mybatis/index.mpack. - After the project root has been indexed once, save edited
*.javaor*.xmlfiles to keep that index fresh. - Use
:MyBatisReindexif you want to rebuild the project index manually.
Basic usage does not require a manual setup() call. The plugin auto-initializes from plugin/mybatis.lua when it is loaded by your plugin manager.
- Neovim 0.10 or newer
- A MyBatis project that follows the standard mapper contract
- Optional:
jdtlsfor better Java method resolution - Optional: Neovim tree-sitter parsers for
javaandxmlfor more precise parser backends; the legacy parser remains the fallback
{
"inkpark/nvim-mybatis",
}{
"inkpark/nvim-mybatis",
tag = "v0.3.2",
}No extra setup() call is needed for the default workflow.
The plugin auto-initializes from plugin/mybatis.lua when it is loaded by your plugin manager.
The plugin exposes commands, but it does not install default keybindings. If you want faster access, bind the commands in your plugin manager config or your normal Neovim config.
{
"inkpark/nvim-mybatis",
keys = {
{ "<leader>mj", "<cmd>MyBatisJump<cr>", desc = "MyBatis Jump" },
{ "<leader>mr", "<cmd>MyBatisReindex<cr>", desc = "MyBatis Reindex" },
},
}vim.keymap.set("n", "<leader>mj", "<cmd>MyBatisJump<cr>", { desc = "MyBatis Jump" })
vim.keymap.set("n", "<leader>mr", "<cmd>MyBatisReindex<cr>", { desc = "MyBatis Reindex" })These bindings are only examples. Adjust them to match your own <leader> conventions.
MyBatisJump is the main entry point:
- In a Java mapper buffer, it jumps from the current method to the matching XML statement.
- On a Java mapper parameter type, it jumps to the matching XML
parameterTypewhen present. - On a Java mapper parameter name, it jumps to matching XML placeholders in the paired statement.
- On a Java field such as
Address.city, it jumps to XML placeholders andresultMapproperties that resolve to that field. - In an XML mapper buffer, it jumps from the current statement to the matching Java method.
- On XML type references such as
parameterType,resultType,resultMap type,javaType, andofType, it jumps to the referenced Java type definition. - On an XML placeholder such as
#{query.user.address.city}, it jumps to the matching Java parameter or nested field. - On
resultMapproperty paths such asaddress.city, it jumps to the matching nested Java field. - Inside
<foreach>scopes, it resolves item aliases and nested aliases such as#{item.address.city}and#{tag.name}.
When only one target exists, the plugin jumps directly. When multiple targets exist, it opens a built-in floating selector with a live code preview so you can choose the destination explicitly.
When a project has no saved snapshot yet, the first MyBatisJump builds the index in a background headless Neovim worker, keeps the UI responsive, and automatically resumes the pending jump if you stay on the same cursor while indexing finishes.
MyBatisReindex rebuilds the cached index for the current project root in the background, keeps the editor responsive while it runs, and shows a command-line progress bar until completion.
Save behavior is incremental:
BufWritePostrefreshes*.javabuffers.BufWritePostrefreshes*.xmlbuffers.- This refresh path only applies after the project root has already been indexed once, for example by an earlier
:MyBatisJumpor:MyBatisReindex. - It keeps the cached index aligned with the current file without requiring a full reindex.
Disk cache behavior is project-local:
- The first full index is persisted under
<project-root>/.nvim_mybatis/index.mpack. - Later sessions load that snapshot first, then scan for only new, changed, or deleted
*.javaand*.xmlfiles. BufWritePostand:MyBatisReindexboth update the on-disk snapshot, so future first jumps can reuse it.
v0.3.2 ships these release-level capabilities:
- Mapper method <-> XML statement jumps for the standard MyBatis contract.
- Type-aware XML reference jumps into Java classes and records.
- Nested placeholder and
resultMapproperty resolution into Java fields, plus reverse jumps from Java fields back to matching XML references. - Incremental save-time refreshes backed by a persisted project snapshot.
- Background manual reindex with progress UI and last-known-good state protection on failure.
- Optional
jdtlsassistance for better Java method detection in the current buffer. - Optional tree-sitter parser backends for Java and XML, with legacy parser fallback.
nvim-mybatis assumes the standard Java-to-XML mapper relationship:
- The XML
namespacematches the Java interface fully qualified name. - The XML statement
idmatches the Java method name. - Supported XML statement types are
select,insert,update, anddelete.
Anything outside that contract is intentionally out of scope for the 0.3.x release line.
The plugin discovers a project root by walking upward from the current buffer and looking for common markers:
.gitpom.xmlsettings.gradlebuild.gradlegradlewmvnw
This marker-based approach is layout-tolerant, so it works across common monorepo and nested-module layouts as long as the mapper files live under a recognized root. The runtime flow is project-index driven: on the first full index, the plugin scans Java and XML files under that root, parses mapper declarations, builds a lightweight index, and persists it under .nvim_mybatis. Later sessions load that snapshot, incrementally reconcile changed files, and then resolve jump targets from the refreshed index. It does not continuously watch the full project for changes.
nvim-mybatis can use Neovim tree-sitter parsers for Java and XML when they are installed. Tree-sitter is optional: if Neovim cannot load a required parser, or if a tree-sitter parse path fails, the plugin silently falls back to the built-in legacy parser.
The default behavior enables tree-sitter opportunistically. You can disable it globally or per parser domain:
require("mybatis").setup({
parser = {
treesitter = {
enabled = true,
java = true,
java_type = true,
xml = true,
},
},
})Set enabled = false to force the legacy parser for all Java/XML parsing, or set an individual domain such as xml = false to keep only that parser on the legacy path.
jdtls is optional. When it is available, nvim-mybatis uses it to improve Java method resolution in the current buffer. When it is not available, the plugin falls back to its own lightweight Java parsing and cursor-based heuristics.
jdtls does not change the core contract or the supported file types. It only improves how the current Java symbol is identified before the jump happens.
- No support for arbitrary annotation-based mapper styles outside the XML contract.
- No support for custom statement name mapping or namespace aliasing.
- No support for unsupported XML statement types such as
selectKeyor other non-standard tags. - No automatic project-wide refactoring or code generation.
- No attempt to model every MyBatis or Java parsing edge case in the
0.3.xrelease line.
The goal is dependable mapper navigation, not full MyBatis framework coverage.
The 0.3.x roadmap stays deliberately small:
- Keep direct mapper navigation stable and predictable.
- Improve diagnostics and ergonomics where they reduce user friction.
- Expand parser coverage only when it clearly supports the core mapper workflow.
- Keep
jdtlsoptional rather than making it a hard dependency.
Future work will remain centered on the mapper jump and reindex workflow, not on turning the plugin into a general MyBatis tooling suite.
Run the headless test suite with Neovim:
nvim --headless -u tests/minimal_init.lua -l tests/smoke/commands_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/smoke/minimal_init_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/helpers_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/parser_java_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/parser_java_type_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/parser_xml_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/parser_backend_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/scanner_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/cache_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/index_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/reindex_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/unit/resolver_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/integration/jump_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/integration/java_field_jump_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/integration/refresh_spec.lua
nvim --headless -u tests/minimal_init.lua -l tests/integration/result_map_property_jump_spec.lua