-- 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')