return { "mason-org/mason-lspconfig.nvim", dependencies = { { "mason-org/mason.nvim", opts = {} }, "neovim/nvim-lspconfig", "saghen/blink.cmp", }, opts = { ensure_installed = { "pyright", "lua_ls", "bashls", "terraformls", "yamlls", "ts_ls", "cssls", "html", "helm_ls", }, -- default true: auto-calls vim.lsp.enable() for every Mason-installed -- server as it finishes installing. }, config = function(_, opts) -- helm_ls's default config only attaches to filetype "helm" / -- "yaml.helm-values" (not plain "yaml"), so without this, -- templates/*.yaml only gets yamlls — which flags Helm's Go template -- syntax ({{ .Values.x }}) as invalid YAML. values.yaml gets the -- composite filetype instead, which both yamlls and helm_ls match, so -- it keeps YAML validation *and* gets helm_ls's values-schema support. vim.filetype.add({ pattern = { [".*/templates/.*%.ya?ml"] = "helm", [".*/templates/.*%.tpl"] = "helm", [".*/values.*%.ya?ml"] = "yaml.helm-values", }, }) -- Merge blink.cmp's LSP capabilities into every server's default config -- (Mason-managed or not) before any server gets enabled. vim.lsp.config("*", { capabilities = require("blink.cmp").get_lsp_capabilities(), }) -- yamlls only completes/validates when a schema is associated with the -- file, and plain k8s manifests don't match anything by filename. So we -- detect manifests by content (top-level apiVersion + kind) and add each -- one to yamlls's built-in "kubernetes" schema. CRDs (ArgoCD, Traefik, -- cert-manager, ...) are then resolved from apiVersion/kind by yamlls's -- CRD store, which is enabled by default. vim.lsp.config("yamlls", { settings = { yaml = { schemas = { kubernetes = {} } } }, }) local function is_k8s_manifest(bufnr) local has_api, has_kind = false, false for _, line in ipairs(vim.api.nvim_buf_get_lines(bufnr, 0, 50, false)) do has_api = has_api or line:match("^apiVersion:") ~= nil has_kind = has_kind or line:match("^kind:") ~= nil end return has_api and has_kind end local function attach_k8s_schema(client, bufnr) local path = vim.api.nvim_buf_get_name(bufnr) if path == "" or not is_k8s_manifest(bufnr) then return end local k8s = client.settings.yaml.schemas.kubernetes if vim.tbl_contains(k8s, path) then return end table.insert(k8s, path) client:notify("workspace/didChangeConfiguration", { settings = client.settings }) end local group = vim.api.nvim_create_augroup("yamlls_kubernetes", { clear = true }) vim.api.nvim_create_autocmd("LspAttach", { group = group, callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) if client and client.name == "yamlls" then attach_k8s_schema(client, args.buf) end end, }) -- Also re-check on save, so a new file picks up the schema once -- apiVersion/kind have been written. vim.api.nvim_create_autocmd("BufWritePost", { group = group, callback = function(args) for _, client in ipairs(vim.lsp.get_clients({ bufnr = args.buf, name = "yamlls" })) do attach_k8s_schema(client, args.buf) end end, }) require("mason-lspconfig").setup(opts) -- clangd ships with Xcode Command Line Tools already, so it's not -- Mason-managed — nvim-lspconfig provides its default config, we just -- need to turn it on. vim.lsp.enable("clangd") end, }