Language Server Protocol (LSP) implementation for Vim script, written in Rust.
- Diagnostics (syntax errors + lint rules)
- Completion (built-in functions + user-defined symbols with scope support)
- Go to definition (same file + cross-file autoload support)
- Hover information (function signatures, autoload file paths)
- Find references (same file + cross-file)
- Document symbols (outline)
- Rename (cross-file support)
- Signature help (parameter info on function calls)
- Workspace symbols (project-wide symbol search)
- Document highlight (highlight symbol under cursor)
- Folding range (function/if/for/while/try/augroup)
- Selection range (smart expand selection via syntax tree)
- Code actions (quick fixes for lint rules)
- Formatting (configuration)
Total: 786 functions
| Category | Count | Description |
|---|---|---|
| Common | ~420 | Vim/Neovim shared functions (strlen, expand, bufnr, etc.) |
| Vim-only | ~130 | popup_*, ch_*, job_*, term_*, test_*, etc. |
| Neovim-only | ~170 | nvim_* API functions |
Total: 149 variables
| Scope | Count | Examples |
|---|---|---|
v: |
126 | v:version, v:errmsg, v:true, v:false |
b: |
2 | b:changedtick, b:current_syntax |
g: |
21 | g:colors_name, g:mapleader, g:clipboard |
Only variables with help documentation tags are supported. Runtime-defined variables (e.g., g:markdown_* syntax options) are excluded due to dynamic naming and noise.
- Rust 1.85+
- Nix (optional, for development environment)
nix run github:kawarimidoll/hjklsAdd to your flake.nix inputs:
{
inputs = {
hjkls.url = "github:kawarimidoll/hjkls";
# ...
};
}Then add to your home configuration:
{ inputs, pkgs, ... }:
{
home.packages = [
inputs.hjkls.packages.${pkgs.system}.default
];
}cargo install --git https://github.com/kawarimidoll/hjklsvim.lsp.config("hjkls", {
cmd = { "/path/to/hjkls" },
filetypes = { "vim" },
root_markers = { ".git" },
})
vim.lsp.enable("hjkls")yegappan/lsp is a Vim9 script-based LSP client.
let lspServers = [#{
\ name: 'hjkls',
\ filetype: ['vim'],
\ path: '/path/to/hjkls',
\ args: [],
\ }]
let lspOptions = #{
\ autoComplete: v:true,
\ autoHighlight: v:true,
\ showDiagWithVirtualText: v:true,
\ showSignature: v:true,
\ }
autocmd VimEnter * ++once call LspOptionsSet(lspOptions) | call LspAddServer(lspServers)vim-lsp is a popular async LSP client.
augroup hjkls_lsp
autocmd!
autocmd User lsp_setup call lsp#register_server({
\ 'name': 'hjkls',
\ 'cmd': ['/path/to/hjkls'],
\ 'allowlist': ['vim'],
\ })
augroup END
let g:lsp_diagnostics_enabled = 1
let g:lsp_diagnostics_virtual_text_enabled = 1Neovim 0.11:
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
vim.opt.completeopt = { "menuone", "noselect", "fuzzy" }
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client and client:supports_method("textDocument/completion") then
vim.lsp.completion.enable(true, client.id, args.buf, { autotrigger = true })
end
end,
})Neovim 0.12+ (nightly):
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
-- 'complete' with 'o' flag: include omnifunc (LSP) in CTRL-N/P completion
-- 'autocomplete': trigger completion automatically as you type
vim.bo[args.buf].complete = ".,o"
vim.bo[args.buf].autocomplete = true
vim.opt.completeopt = { "menuone", "noselect", "fuzzy" }
end,
})-- Highlight references to the symbol under cursor
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
callback = function()
if #vim.lsp.get_clients({ bufnr = 0 }) > 0 then
vim.lsp.buf.document_highlight()
end
end,
})
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
callback = function()
vim.lsp.buf.clear_references()
end,
})-- Enable LSP folding (zc=close, zo=open, zR=open all, zM=close all)
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client and client:supports_method("textDocument/foldingRange") then
vim.wo[args.buf].foldmethod = "expr"
vim.wo[args.buf].foldexpr = "v:lua.vim.lsp.foldexpr()"
vim.wo[args.buf].foldlevel = 99 -- start with all folds open
end
end,
})cp .envrc.sample .envrc
direnv allowjust # Show available commands
just build # Build debug binary
just release # Build release binary
just check # Run clippy and check
just fmt # Format code (Rust, Markdown, YAML, Nix)
just test # Run tests
just dev-nvim # Open sample file in Neovim for manual testing
just dev-vim # Open sample file in Vim for manual testingtree-sitter-vim (v0.4.0) cannot correctly parse <Cmd>...<CR> style mappings (e.g., nmap qu <Cmd>quit<CR>). The grammar's command_argument: /\S+/ greedily consumes <CR>, preventing _map_rhs_statement from recognizing its closing token. This causes the parser to emit ERROR or MISSING nodes.
hjkls includes a workaround to detect and suppress these false positives so that valid <Cmd> mappings do not appear as syntax errors.
- tower-lsp-server - LSP server framework for Rust
- texter - Text management library with tree-sitter integration
- tree-sitter-vim - Vim script grammar for tree-sitter
- salsa - Incremental computation framework
- vim-language-server - Prior art: Vim script LSP in TypeScript
MIT