nvim/lua/user/lsp_servers/kotlin_ls.lua
2026-07-02 13:49:44 +05:30

86 lines
3.5 KiB
Lua

-- Kotlin LSP = fwcd kotlin-language-server (installed via Mason).
--
-- NOTE: the JetBrains `kotlin-lsp` (intellij-server) was tried but it imports
-- Android Gradle modules with an EMPTY set of source sets, so it resolves
-- nothing for Android (hover/definition/references all return empty). fwcd
-- resolves the Gradle compile classpath itself and DOES navigate Android
-- projects, so we use it here. (kotlin-lsp remains fine for plain Kotlin/JVM.)
--
-- CPU/Memory notes for Android projects:
-- - Formatting disabled in LSP capabilities — ktfmt in-process spawns a SECOND
-- Kotlin compiler, doubling memory (OOMs even at 4GB). Formatting is handled
-- externally by null-ls running ktfmt as a separate process.
-- - Linting enabled — runs on the same compiler already loaded for completions,
-- so no extra memory cost. Provides error diagnostics (red squiggles).
-- - Semantic tokens disabled — extra compilation passes, not worth the CPU.
-- - 500ms debounce reduces compilation frequency during typing.
-- - Persistent tmpdir so the SQLite compilation cache survives across sessions.
local bin = vim.fn.expand("~/.local/share/nvim/mason/bin/kotlin-language-server")
if vim.fn.executable(bin) == 0 then
vim.notify("kotlin-language-server not found — run :MasonInstall kotlin-language-server",
vim.log.levels.WARN)
return
end
-- Build capabilities, then strip formatting to prevent ktfmt OOMs.
local capabilities = require("cmp_nvim_lsp").default_capabilities()
capabilities.textDocument.formatting = nil
capabilities.textDocument.rangeFormatting = nil
capabilities.textDocument.onTypeFormatting = nil
-- 1. Configuration
vim.lsp.config('kotlin_language_server', {
cmd = { bin },
filetypes = { "kotlin" },
-- Kotlin/Android projects are rooted at a Gradle build file or git repo.
-- vim.lsp.config expects callback form: function(bufnr, on_dir).
root_dir = function(bufnr, on_dir)
local fname = vim.api.nvim_buf_get_name(bufnr)
local root = vim.fs.root(fname, {
"settings.gradle.kts",
"settings.gradle",
"build.gradle.kts",
"build.gradle",
"pom.xml",
".git",
})
on_dir(root or vim.fs.dirname(fname))
end,
capabilities = capabilities,
-- Strip formatting from the server's advertised capabilities.
-- client-side capabilities only prevent the server from expecting format
-- requests; server-side capabilities are what vim.lsp.buf.format() actually
-- checks. Without this, format-on-save routes through the LSP's ktfmt →
-- second Kotlin compiler → OOM.
on_attach = function(client)
client.server_capabilities.documentFormattingProvider = false
client.server_capabilities.documentRangeFormattingProvider = false
end,
-- 6GB heap — Android classpath is large (163 deps + 320 source files).
-- Persistent tmpdir so the compilation cache survives across sessions.
-- Use Android Studio's JBR 21 — optimised GC/JIT for Kotlin workloads.
cmd_env = {
KOTLIN_LANGUAGE_SERVER_OPTS = "-Xmx6G -Xms512m -Djava.io.tmpdir=/home/prabhat/.cache/kotlin-ls-fwcd",
JAVA_HOME = "/opt/android-studio/jbr",
},
init_options = {
debounceTextChanges = 500,
},
settings = {
kotlin = {
linting = { enabled = true },
completion = { enabled = true },
semanticTokens = { enabled = false },
},
},
})
-- 2. Enable the server
vim.lsp.enable('kotlin_language_server')