From 2a1f3c370145c3465c0142deade3e3b2660ce829 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 15 Oct 2025 08:36:32 +0200 Subject: [PATCH 1/5] feat(treesitter): on windows, use `gcc` if available and `cl.exe` is not available for building parsers --- lua/lazyvim/plugins/treesitter.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lua/lazyvim/plugins/treesitter.lua b/lua/lazyvim/plugins/treesitter.lua index 5106da4e..6256904d 100644 --- a/lua/lazyvim/plugins/treesitter.lua +++ b/lua/lazyvim/plugins/treesitter.lua @@ -59,6 +59,16 @@ return { config = function(_, opts) local TS = require("nvim-treesitter") + -- On Windows, use `gcc` if `cl` is not available, and `gcc` is. + if + not vim.env.CC + and vim.fn.has("win32") == 1 + and vim.fn.executable("cl") == 0 + and vim.fn.executable("gcc") == 1 + then + vim.env.CC = "gcc" + end + setmetatable(require("nvim-treesitter.install"), { __newindex = function(_, k) if k == "compilers" then From 413b9d5fa91c5bd5088b28c6d1764caa88542cb1 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 15 Oct 2025 08:37:11 +0200 Subject: [PATCH 2/5] feat(treesitter): better health checks for treesitter requirements --- lua/lazyvim/health.lua | 15 +++++++ lua/lazyvim/plugins/treesitter.lua | 4 +- lua/lazyvim/util/treesitter.lua | 71 +++++++++++++++++++++++------- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/lua/lazyvim/health.lua b/lua/lazyvim/health.lua index d1632e75..7af6ad94 100644 --- a/lua/lazyvim/health.lua +++ b/lua/lazyvim/health.lua @@ -4,6 +4,7 @@ local start = vim.health.start or vim.health.report_start local ok = vim.health.ok or vim.health.report_ok local warn = vim.health.warn or vim.health.report_warn local error = vim.health.error or vim.health.report_error +local info = vim.health.info or vim.health.report_info function M.check() start("LazyVim") @@ -33,6 +34,20 @@ function M.check() warn(("`%s` is not installed"):format(name)) end end + + start("LazyVim nvim-treesitter") + local tsok, health = LazyVim.treesitter.check() + local keys = vim.tbl_keys(health) ---@type string[] + table.sort(keys) + for _, k in pairs(keys) do + (health[k] and ok or error)(("`%s` is %s"):format(k, health[k] and "installed" or "not installed")) + end + if not tsok then + info( + "See the requirements at [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/tree/main?tab=readme-ov-file#requirements)" + ) + info("Run `:checkhealth nvim-treesitter` for more information.") + end end return M diff --git a/lua/lazyvim/plugins/treesitter.lua b/lua/lazyvim/plugins/treesitter.lua index 6256904d..5ab47721 100644 --- a/lua/lazyvim/plugins/treesitter.lua +++ b/lua/lazyvim/plugins/treesitter.lua @@ -13,7 +13,7 @@ return { LazyVim.error("Please restart Neovim and run `:TSUpdate` to use the `nvim-treesitter` **main** branch.") return end - LazyVim.treesitter.ensure_treesitter_cli(function() + LazyVim.treesitter.build(function() TS.update(nil, { summary = true }) end) end, @@ -100,7 +100,7 @@ return { return not LazyVim.treesitter.have(lang) end, opts.ensure_installed or {}) if #install > 0 then - LazyVim.treesitter.ensure_treesitter_cli(function() + LazyVim.treesitter.build(function() TS.install(install, { summary = true }):await(function() LazyVim.treesitter.get_installed(true) -- refresh the installed langs end) diff --git a/lua/lazyvim/util/treesitter.lua b/lua/lazyvim/util/treesitter.lua index b1d647e8..d5c845fd 100644 --- a/lua/lazyvim/util/treesitter.lua +++ b/lua/lazyvim/util/treesitter.lua @@ -51,31 +51,68 @@ function M.indentexpr() return M.have(nil, "indents") and require("nvim-treesitter").indentexpr() or -1 end ----@param cb fun() -function M.ensure_treesitter_cli(cb) - if vim.fn.executable("tree-sitter") == 1 then - return cb() +---@return boolean ok, lazyvim.util.treesitter.Health health +function M.check() + local is_win = vim.fn.has("win32") == 1 + ---@param tool string + ---@param win boolean? + local function have(tool, win) + return (win == nil or is_win == win) and vim.fn.executable(tool) == 1 end - ---@param msg? string - local function fail(msg) - return LazyVim.error({ - "**treesitter-nvim** `main` requires the `tree-sitter` CLI executable to be installed.", - "Please install it manually from https://github.com/tree-sitter/tree-sitter/tree/master/crates/cli or", - "use your system package manager.", - "Run `:checkhealth nvim-treesitter` for more information.", - msg, - }) + ---@class lazyvim.util.treesitter.Health: table + local ret = { + ["tree-sitter (CLI)"] = have("tree-sitter"), + ["C compiler"] = vim.env.CC or have("cc", false) or have("cl", true), + tar = have("tar"), + curl = have("curl"), + node = have("node"), + } + local ok = true + for _, v in pairs(ret) do + ok = ok and v + end + return ok, ret +end + +---@param cb fun() +function M.build(cb) + M.ensure_treesitter_cli(function(_, err) + local ok, health = M.check() + if ok then + return cb() + else + local lines = { "Unmet requirements for **nvim-treesitter** `main`:" } + local keys = vim.tbl_keys(health) ---@type string[] + table.sort(keys) + for _, k in pairs(keys) do + lines[#lines + 1] = ("- %s `%s`"):format(health[k] and "✅" or "❌", k) + end + vim.list_extend(lines, { + "", + "See the requirements at [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/tree/main?tab=readme-ov-file#requirements)", + "Run `:checkhealth nvim-treesitter` for more information.", + }) + vim.list_extend(lines, err and { "", err } or {}) + LazyVim.error(lines, { title = "LazyVim Treesitter" }) + end + end) +end + +---@param cb fun(ok:boolean, err?:string) +function M.ensure_treesitter_cli(cb) + if vim.fn.executable("tree-sitter") == 1 then + return cb(true) end -- try installing with mason if not pcall(require, "mason") then - return fail("`mason.nvim` is disabled in your config, so we cannot install it automatically.") + return cb(false, "`mason.nvim` is disabled in your config, so we cannot install it automatically.") end -- check again since we might have installed it already if vim.fn.executable("tree-sitter") == 1 then - return cb() + return cb(true) end local mr = require("mason-registry") @@ -88,9 +125,9 @@ function M.ensure_treesitter_cli(cb) vim.schedule_wrap(function(success) if success then LazyVim.info("Installed `tree-sitter-cli` with `mason.nvim`.") - cb() + cb(true) else - fail("Failed to install `tree-sitter-cli` with `mason.nvim`.") + cb(false, "Failed to install `tree-sitter-cli` with `mason.nvim`.") end end) ) From 37032dabd657ae567011e7ab4f881ecbf476eb76 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 15 Oct 2025 08:41:55 +0200 Subject: [PATCH 3/5] feat(treesitter): add installation instructions to get a C compiler on windows --- lua/lazyvim/health.lua | 3 +++ lua/lazyvim/util/treesitter.lua | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lua/lazyvim/health.lua b/lua/lazyvim/health.lua index 7af6ad94..2542c150 100644 --- a/lua/lazyvim/health.lua +++ b/lua/lazyvim/health.lua @@ -47,6 +47,9 @@ function M.check() "See the requirements at [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/tree/main?tab=readme-ov-file#requirements)" ) info("Run `:checkhealth nvim-treesitter` for more information.") + if vim.fn.has("win32") == 1 and not health["C compiler"] then + info("Install a C compiler with `winget install --id=BrechtSanders.WinLibs.POSIX.UCRT -e`") + end end end diff --git a/lua/lazyvim/util/treesitter.lua b/lua/lazyvim/util/treesitter.lua index d5c845fd..23795005 100644 --- a/lua/lazyvim/util/treesitter.lua +++ b/lua/lazyvim/util/treesitter.lua @@ -93,6 +93,9 @@ function M.build(cb) "See the requirements at [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/tree/main?tab=readme-ov-file#requirements)", "Run `:checkhealth nvim-treesitter` for more information.", }) + if vim.fn.has("win32") == 1 and not health["C compiler"] then + lines[#lines + 1] = "Install a C compiler with `winget install --id=BrechtSanders.WinLibs.POSIX.UCRT -e`" + end vim.list_extend(lines, err and { "", err } or {}) LazyVim.error(lines, { title = "LazyVim Treesitter" }) end From 144fe24be15007de0db036ba145514011d4d55d6 Mon Sep 17 00:00:00 2001 From: folke <292349+folke@users.noreply.github.com> Date: Wed, 15 Oct 2025 06:43:19 +0000 Subject: [PATCH 4/5] chore(build): auto-generate docs --- doc/LazyVim.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/LazyVim.txt b/doc/LazyVim.txt index 0b80e343..3da695fb 100644 --- a/doc/LazyVim.txt +++ b/doc/LazyVim.txt @@ -1,4 +1,4 @@ -*LazyVim.txt* For Neovim Last change: 2025 October 14 +*LazyVim.txt* For Neovim Last change: 2025 October 15 ============================================================================== Table of Contents *LazyVim-table-of-contents* From 5effc77185f94f557809375b5650d427e7171c32 Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 15 Oct 2025 19:43:42 +0200 Subject: [PATCH 5/5] fix(treesitter): better check on windows if microsoft build tools is installed --- lua/lazyvim/plugins/treesitter.lua | 10 ---------- lua/lazyvim/util/treesitter.lua | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lua/lazyvim/plugins/treesitter.lua b/lua/lazyvim/plugins/treesitter.lua index 5ab47721..25223725 100644 --- a/lua/lazyvim/plugins/treesitter.lua +++ b/lua/lazyvim/plugins/treesitter.lua @@ -59,16 +59,6 @@ return { config = function(_, opts) local TS = require("nvim-treesitter") - -- On Windows, use `gcc` if `cl` is not available, and `gcc` is. - if - not vim.env.CC - and vim.fn.has("win32") == 1 - and vim.fn.executable("cl") == 0 - and vim.fn.executable("gcc") == 1 - then - vim.env.CC = "gcc" - end - setmetatable(require("nvim-treesitter.install"), { __newindex = function(_, k) if k == "compilers" then diff --git a/lua/lazyvim/util/treesitter.lua b/lua/lazyvim/util/treesitter.lua index 23795005..e8688a6f 100644 --- a/lua/lazyvim/util/treesitter.lua +++ b/lua/lazyvim/util/treesitter.lua @@ -51,6 +51,13 @@ function M.indentexpr() return M.have(nil, "indents") and require("nvim-treesitter").indentexpr() or -1 end +---@return string? +local function win_find_cl() + local path = "C:/Program Files (x86)/Microsoft Visual Studio" + local pattern = "*/*/VC/Tools/MSVC/*/bin/Hostx64/x64/cl.exe" + return vim.fn.globpath(path, pattern, true, true)[1] +end + ---@return boolean ok, lazyvim.util.treesitter.Health health function M.check() local is_win = vim.fn.has("win32") == 1 @@ -60,10 +67,17 @@ function M.check() return (win == nil or is_win == win) and vim.fn.executable(tool) == 1 end + local have_cc = vim.env.CC ~= nil or have("cc", false) or have("cl", true) or (is_win and win_find_cl() ~= nil) + + if not have_cc and is_win and vim.fn.executable("gcc") == 1 then + vim.env.CC = "gcc" + have_cc = true + end + ---@class lazyvim.util.treesitter.Health: table local ret = { ["tree-sitter (CLI)"] = have("tree-sitter"), - ["C compiler"] = vim.env.CC or have("cc", false) or have("cl", true), + ["C compiler"] = have_cc, tar = have("tar"), curl = have("curl"), node = have("node"),