From dce992cc4859f8c5969045647bdb0c1e6a882445 Mon Sep 17 00:00:00 2001 From: Ashlen Date: Mon, 18 May 2026 18:44:08 -0600 Subject: [PATCH 1/2] feat(nvim): add terraform LSP and tooling support Bring .tf and .tfvars files to feature parity with other supported languages: terraform-ls LSP attaches automatically, conform runs `terraform fmt` on save, nvim-lint runs tflint, and treesitter highlights both filetypes (the .tfvars buffer reuses the terraform parser via vim.treesitter.language.register). Two subtleties caught during verification: * Neovim's builtin .tf detection is content-based and falls back to the legacy `tf` filetype on empty buffers, which blocks the LSP from attaching to fresh files. lua/my/settings/filetypes.lua forces `.tf` -> `terraform` unconditionally. * mason-lspconfig auto-enables tflint as an LSP whenever its Mason package is installed, which produces duplicate diagnostics alongside the nvim-lint runner. The bare `mason-lspconfig.setup()` call now passes `automatic_enable = { exclude = { "tflint" } }` to keep linting in nvim-lint where the rest of the project's linters live. terraform-ls and tflint are installed via mason-tool-installer; the `terraform` CLI itself is not in the Mason registry and must come from a system package manager / tfenv / asdf (documented in the mason-tool-installer header comment alongside perltidy and clang-format). --- .config/nvim/README.md | 5 +++++ .config/nvim/lua/my/plugins/conform.lua | 4 ++++ .config/nvim/lua/my/plugins/lspconfig.lua | 13 ++++++++++--- .../nvim/lua/my/plugins/mason-tool-installer.lua | 5 ++++- .config/nvim/lua/my/plugins/nvim-lint.lua | 1 + .config/nvim/lua/my/plugins/treesitter.lua | 7 +++++++ .config/nvim/lua/my/settings/filetypes.lua | 14 ++++++++++++++ .config/nvim/lua/my/settings/init.lua | 1 + 8 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 .config/nvim/lua/my/settings/filetypes.lua diff --git a/.config/nvim/README.md b/.config/nvim/README.md index 3a5a1cc7..f3f04326 100644 --- a/.config/nvim/README.md +++ b/.config/nvim/README.md @@ -680,6 +680,7 @@ For non-Qubes systems, either: | Perl | perltidy | | Python | black | | Shell | shfmt | +| Terraform | terraform_fmt (requires `terraform` CLI in `PATH`; not in Mason registry) | | TeX | tex-fmt | | XML | xmlformatter | @@ -695,9 +696,13 @@ For non-Qubes systems, either: | Markdown | markdownlint | | Python | mypy, pylint, ruff | | Ruby/ERB | erb_lint | +| Terraform | tflint | > All linters above are auto-installed by `mason-tool-installer.nvim`, the > single source of truth for Mason tool provisioning. +> +> tflint runs with built-in rules by default; create a project-local +> `.tflint.hcl` (via `tflint --init`) to enable plugin rules. diff --git a/.config/nvim/lua/my/plugins/conform.lua b/.config/nvim/lua/my/plugins/conform.lua index 3d63a2dc..194abcdd 100644 --- a/.config/nvim/lua/my/plugins/conform.lua +++ b/.config/nvim/lua/my/plugins/conform.lua @@ -15,6 +15,8 @@ return { "perl", "python", "sh", + "terraform", + "terraform-vars", "tex", "xml", "yaml", @@ -33,6 +35,8 @@ return { perl = { "perltidy" }, python = { "black" }, sh = { "shfmt" }, + terraform = { "terraform_fmt" }, + ["terraform-vars"] = { "terraform_fmt" }, tex = { "tex-fmt" }, xml = { "xmlformatter" }, yaml = { "prettier" }, diff --git a/.config/nvim/lua/my/plugins/lspconfig.lua b/.config/nvim/lua/my/plugins/lspconfig.lua index 71ea96e8..ce9a6afc 100644 --- a/.config/nvim/lua/my/plugins/lspconfig.lua +++ b/.config/nvim/lua/my/plugins/lspconfig.lua @@ -160,11 +160,18 @@ local function setup_lsp_servers() }, }) -- bashls, rust_analyzer, cssls, html, jsonls, yamlls, stylelint_lsp, - -- marksman: use defaults; capabilities are injected via the '*' config - -- above, and mason-lspconfig.automatic_enable picks them up after setup. + -- marksman, terraformls: use defaults; capabilities are injected via the + -- '*' config above, and mason-lspconfig.automatic_enable picks them up + -- after setup. -- stylelint_lsp default filetypes include JS/TS which can duplicate ts_ls -- diagnostics on mixed projects; restrict reactively if friction surfaces. - require("mason-lspconfig").setup() + -- tflint is excluded: nvim-lint already runs tflint via linters_by_ft, and + -- both paths produce identical diagnostics — double-reporting otherwise. + require("mason-lspconfig").setup({ + automatic_enable = { + exclude = { "tflint" }, + }, + }) end return { diff --git a/.config/nvim/lua/my/plugins/mason-tool-installer.lua b/.config/nvim/lua/my/plugins/mason-tool-installer.lua index db86ccbd..3608177b 100644 --- a/.config/nvim/lua/my/plugins/mason-tool-installer.lua +++ b/.config/nvim/lua/my/plugins/mason-tool-installer.lua @@ -3,7 +3,8 @@ -- Sole toolchain provisioner. `lazy = false` is mandatory: `event = "VimEnter"` -- or `VeryLazy` silently skip run_on_start (plugin not loaded yet when VimEnter -- fires). Manual-only tools (not in the Mason registry): perltidy (`cpan install --- Perl::Tidy`), clang-format (system package). +-- Perl::Tidy`), clang-format (system package), terraform (system package / +-- `tfenv` / `asdf`; needed by conform's `terraform_fmt`). return { "WhoIsSethDaniel/mason-tool-installer.nvim", lazy = false, @@ -23,6 +24,7 @@ return { "yamlls", "stylelint_lsp", "marksman", + "terraformls", -- Formatters (from conform.lua formatters_by_ft — Mason registry names) "stylua", @@ -42,6 +44,7 @@ return { "pylint", "ruff", "erb-lint", + "tflint", }, auto_update = false, -- install-missing only, no silent drift run_on_start = true, -- async install on VimEnter diff --git a/.config/nvim/lua/my/plugins/nvim-lint.lua b/.config/nvim/lua/my/plugins/nvim-lint.lua index 301575eb..3e7d1df9 100644 --- a/.config/nvim/lua/my/plugins/nvim-lint.lua +++ b/.config/nvim/lua/my/plugins/nvim-lint.lua @@ -11,6 +11,7 @@ return { html = { "erb_lint" }, markdown = { "markdownlint" }, python = { "mypy", "pylint", "ruff" }, + terraform = { "tflint" }, } -- NOTE: by default, erb_lint cmd and args are expecting a diff --git a/.config/nvim/lua/my/plugins/treesitter.lua b/.config/nvim/lua/my/plugins/treesitter.lua index 5dc3faaa..ac083fa0 100644 --- a/.config/nvim/lua/my/plugins/treesitter.lua +++ b/.config/nvim/lua/my/plugins/treesitter.lua @@ -23,6 +23,7 @@ return { "diff", "gitcommit", "go", + "hcl", "html", "latex", "lua", @@ -30,6 +31,7 @@ return { "markdown", "python", "query", + "terraform", "vim", "vimdoc", "yaml", @@ -108,5 +110,10 @@ return { }, config = function(_, opts) require("nvim-treesitter.configs").setup(opts) + + -- `.tfvars` files get filetype `terraform-vars` (Neovim 0.11+ builtin), + -- but no dedicated treesitter parser exists. Reuse the `terraform` + -- parser for highlighting/text-objects on those buffers. + vim.treesitter.language.register("terraform", "terraform-vars") end, } diff --git a/.config/nvim/lua/my/settings/filetypes.lua b/.config/nvim/lua/my/settings/filetypes.lua new file mode 100644 index 00000000..c85718df --- /dev/null +++ b/.config/nvim/lua/my/settings/filetypes.lua @@ -0,0 +1,14 @@ +-- luacheck: globals vim + +-- Filetype overrides. Loaded eagerly via `lua/my/settings/init.lua` so +-- detection is in place before the first buffer is read. + +-- `.tf` files: Neovim's builtin detect.tf inspects content and falls back to +-- the legacy `tf` filetype on empty / comment-only buffers, which prevents +-- terraform-ls from attaching to fresh files. Force `terraform` unconditionally +-- (the `.tfvars` → `terraform-vars` mapping is unaffected and stays builtin). +vim.filetype.add({ + extension = { + tf = "terraform", + }, +}) diff --git a/.config/nvim/lua/my/settings/init.lua b/.config/nvim/lua/my/settings/init.lua index b196bca8..b7377798 100644 --- a/.config/nvim/lua/my/settings/init.lua +++ b/.config/nvim/lua/my/settings/init.lua @@ -1,3 +1,4 @@ require("my.settings.vim_env") require("my.settings.vim_opt") require("my.settings.vim_g") +require("my.settings.filetypes") From 12afa55489e74473a733b68b88fdb0c6e5d4666f Mon Sep 17 00:00:00 2001 From: Ashlen Date: Mon, 18 May 2026 20:04:10 -0600 Subject: [PATCH 2/2] feat(nvim): install terraform CLI via Mason MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `terraform` package is in fact in the Mason registry (categories: Formatter, Linter, Runtime) — the previous commit's claim that it was not was wrong. Add it to mason-tool-installer's formatters block so conform's `terraform_fmt` works out of the box without requiring the user to install the CLI separately via tfenv / asdf / system package manager. Drop the matching "manual-only" entry from the header comment, and update the README Formatters row to reflect that the binary is Mason-managed. --- .config/nvim/README.md | 2 +- .config/nvim/lua/my/plugins/mason-tool-installer.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/nvim/README.md b/.config/nvim/README.md index f3f04326..00ca5401 100644 --- a/.config/nvim/README.md +++ b/.config/nvim/README.md @@ -680,7 +680,7 @@ For non-Qubes systems, either: | Perl | perltidy | | Python | black | | Shell | shfmt | -| Terraform | terraform_fmt (requires `terraform` CLI in `PATH`; not in Mason registry) | +| Terraform | terraform_fmt (shells out to the `terraform` CLI, installed via Mason) | | TeX | tex-fmt | | XML | xmlformatter | diff --git a/.config/nvim/lua/my/plugins/mason-tool-installer.lua b/.config/nvim/lua/my/plugins/mason-tool-installer.lua index 3608177b..fd9a58a9 100644 --- a/.config/nvim/lua/my/plugins/mason-tool-installer.lua +++ b/.config/nvim/lua/my/plugins/mason-tool-installer.lua @@ -3,8 +3,7 @@ -- Sole toolchain provisioner. `lazy = false` is mandatory: `event = "VimEnter"` -- or `VeryLazy` silently skip run_on_start (plugin not loaded yet when VimEnter -- fires). Manual-only tools (not in the Mason registry): perltidy (`cpan install --- Perl::Tidy`), clang-format (system package), terraform (system package / --- `tfenv` / `asdf`; needed by conform's `terraform_fmt`). +-- Perl::Tidy`), clang-format (system package). return { "WhoIsSethDaniel/mason-tool-installer.nvim", lazy = false, @@ -34,6 +33,7 @@ return { "shfmt", "tex-fmt", "xmlformatter", + "terraform", -- ships the `terraform` CLI; conform's terraform_fmt shells to `terraform fmt -` -- Linters (from nvim-lint.lua linters_by_ft — Mason registry names) "revive",