mirror of
https://github.com/LazyVim/LazyVim.git
synced 2025-06-25 10:18:47 +02:00
feat(root): customizable root detection and :LazyRoot
command
This commit is contained in:
parent
5538ab2d64
commit
171a843edf
6 changed files with 126 additions and 38 deletions
|
@ -104,6 +104,7 @@ function M.setup(opts)
|
||||||
end
|
end
|
||||||
M.load("keymaps")
|
M.load("keymaps")
|
||||||
Util.format.setup()
|
Util.format.setup()
|
||||||
|
Util.root.setup()
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ if vim.lsp.inlay_hint then
|
||||||
end
|
end
|
||||||
|
|
||||||
-- lazygit
|
-- lazygit
|
||||||
map("n", "<leader>gg", function() Util.terminal({ "lazygit" }, { cwd = Util.root.get(), esc_esc = false, ctrl_hjkl = false }) end, { desc = "Lazygit (root dir)" })
|
map("n", "<leader>gg", function() Util.terminal({ "lazygit" }, { cwd = Util.root(), esc_esc = false, ctrl_hjkl = false }) end, { desc = "Lazygit (root dir)" })
|
||||||
map("n", "<leader>gG", function() Util.terminal({ "lazygit" }, {esc_esc = false, ctrl_hjkl = false}) end, { desc = "Lazygit (cwd)" })
|
map("n", "<leader>gG", function() Util.terminal({ "lazygit" }, {esc_esc = false, ctrl_hjkl = false}) end, { desc = "Lazygit (cwd)" })
|
||||||
|
|
||||||
-- quit
|
-- quit
|
||||||
|
@ -128,7 +128,7 @@ map("n", "<leader>ui", vim.show_pos, { desc = "Inspect Pos" })
|
||||||
map("n", "<leader>L", Util.changelog, {desc = "LazyVim Changelog"})
|
map("n", "<leader>L", Util.changelog, {desc = "LazyVim Changelog"})
|
||||||
|
|
||||||
-- floating terminal
|
-- floating terminal
|
||||||
local lazyterm = function() Util.terminal(nil, { cwd = Util.root.get() }) end
|
local lazyterm = function() Util.terminal(nil, { cwd = Util.root() }) end
|
||||||
map("n", "<leader>ft", lazyterm, { desc = "Terminal (root dir)" })
|
map("n", "<leader>ft", lazyterm, { desc = "Terminal (root dir)" })
|
||||||
map("n", "<leader>fT", function() Util.terminal() end, { desc = "Terminal (cwd)" })
|
map("n", "<leader>fT", function() Util.terminal() end, { desc = "Terminal (cwd)" })
|
||||||
map("n", "<c-/>", lazyterm, { desc = "Terminal (root dir)" })
|
map("n", "<c-/>", lazyterm, { desc = "Terminal (root dir)" })
|
||||||
|
|
|
@ -5,6 +5,10 @@ vim.g.maplocalleader = "\\"
|
||||||
-- Enable LazyVim auto format
|
-- Enable LazyVim auto format
|
||||||
vim.g.autoformat = true
|
vim.g.autoformat = true
|
||||||
|
|
||||||
|
-- LazyVim root dir detection
|
||||||
|
-- Each entry can be a detector function like `lsp` or `cwd`, or a pattern like `.git` or `lua`.
|
||||||
|
vim.g.root_spec = { "lsp", { ".git", "lua" }, "cwd" }
|
||||||
|
|
||||||
local opt = vim.opt
|
local opt = vim.opt
|
||||||
|
|
||||||
opt.autowrite = true -- Enable auto write
|
opt.autowrite = true -- Enable auto write
|
||||||
|
|
|
@ -11,7 +11,7 @@ return {
|
||||||
{
|
{
|
||||||
"<leader>fe",
|
"<leader>fe",
|
||||||
function()
|
function()
|
||||||
require("neo-tree.command").execute({ toggle = true, dir = Util.root.get() })
|
require("neo-tree.command").execute({ toggle = true, dir = Util.root() })
|
||||||
end,
|
end,
|
||||||
desc = "Explorer NeoTree (root dir)",
|
desc = "Explorer NeoTree (root dir)",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,122 @@
|
||||||
local Util = require("lazyvim.util")
|
local Util = require("lazyvim.util")
|
||||||
|
|
||||||
---@class lazyvim.util.root
|
---@class lazyvim.util.root
|
||||||
local M = {}
|
---@overload fun(): string
|
||||||
|
local M = setmetatable({}, {
|
||||||
|
__call = function(m)
|
||||||
|
return m.get()
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
M.patterns = { ".git", "lua" }
|
---@class LazyRoot
|
||||||
|
---@field paths string[]
|
||||||
|
---@field spec LazyRootSpec
|
||||||
|
|
||||||
|
---@alias LazyRootSpec string|string[]
|
||||||
|
|
||||||
|
---@type LazyRootSpec[]
|
||||||
|
M.spec = { "lsp", { ".git", "lua" }, "cwd" }
|
||||||
|
|
||||||
|
M.detectors = {}
|
||||||
|
|
||||||
|
function M.detectors.cwd()
|
||||||
|
return { vim.loop.cwd() }
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.detectors.lsp(buf)
|
||||||
|
if not M.bufpath(buf) then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
local roots = {} ---@type string[]
|
||||||
|
for _, client in pairs(Util.lsp.get_clients({ bufnr = buf })) do
|
||||||
|
local workspace = client.config.workspace_folders
|
||||||
|
for _, ws in pairs(workspace or {}) do
|
||||||
|
roots[#roots + 1] = vim.uri_to_fname(ws.uri)
|
||||||
|
end
|
||||||
|
if client.config.root_dir then
|
||||||
|
roots[#roots + 1] = client.config.root_dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return roots
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param patterns string[]|string
|
||||||
|
function M.detectors.pattern(buf, patterns)
|
||||||
|
patterns = type(patterns) == "string" and { patterns } or patterns
|
||||||
|
local path = M.bufpath(buf) or vim.loop.cwd()
|
||||||
|
local pattern = vim.fs.find(patterns, { path = path, upward = true })[1]
|
||||||
|
return pattern and { vim.fs.dirname(pattern) } or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.bufpath(buf)
|
||||||
|
return M.realpath(vim.api.nvim_buf_get_name(assert(buf)))
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.realpath(path)
|
||||||
|
if path == "" or path == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
path = vim.loop.fs_realpath(path) or path
|
||||||
|
return Util.norm(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts? { buf?: number, spec?: LazyRootSpec[], all?: boolean }
|
||||||
|
function M.detect(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
opts.spec = opts.spec or type(vim.g.root_spec) == "table" and vim.g.root_spec or M.spec
|
||||||
|
opts.buf = (opts.buf == nil or opts.buf == 0) and vim.api.nvim_get_current_buf() or opts.buf
|
||||||
|
|
||||||
|
local ret = {} ---@type LazyRoot[]
|
||||||
|
for _, spec in ipairs(opts.spec) do
|
||||||
|
local paths = M.detectors[spec] and M.detectors[spec](opts.buf) or M.detectors.pattern(opts.buf, spec)
|
||||||
|
local roots = {} ---@type string[]
|
||||||
|
for _, p in ipairs(paths) do
|
||||||
|
local pp = M.realpath(p)
|
||||||
|
if pp and not vim.tbl_contains(roots, pp) then
|
||||||
|
roots[#roots + 1] = pp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(roots, function(a, b)
|
||||||
|
return #a > #b
|
||||||
|
end)
|
||||||
|
if #roots > 0 then
|
||||||
|
ret[#ret + 1] = { spec = spec, paths = roots }
|
||||||
|
if opts.all == false then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup()
|
||||||
|
vim.api.nvim_create_user_command("LazyRoot", function()
|
||||||
|
M.info()
|
||||||
|
end, { desc = "LazyVim roots for the current buffer" })
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.info()
|
||||||
|
local spec = type(vim.g.root_spec) == "table" and vim.g.root_spec or M.spec
|
||||||
|
|
||||||
|
local roots = M.detect({ all = true })
|
||||||
|
local lines = {} ---@type string[]
|
||||||
|
local first = true
|
||||||
|
for _, root in ipairs(roots) do
|
||||||
|
for _, path in ipairs(root.paths) do
|
||||||
|
lines[#lines + 1] = ("- [%s] `%s` **(%s)**"):format(
|
||||||
|
first and "x" or " ",
|
||||||
|
path,
|
||||||
|
type(root.spec) == "table" and table.concat(root.spec, ", ") or root.spec
|
||||||
|
)
|
||||||
|
first = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
lines[#lines + 1] = "```lua"
|
||||||
|
lines[#lines + 1] = "vim.g.root_spec = " .. vim.inspect(spec)
|
||||||
|
lines[#lines + 1] = "```"
|
||||||
|
require("lazyvim.util").info(lines, { title = "LazyVim Roots" })
|
||||||
|
return roots[1] and roots[1].paths[1] or vim.loop.cwd()
|
||||||
|
end
|
||||||
|
|
||||||
-- returns the root directory based on:
|
-- returns the root directory based on:
|
||||||
-- * lsp workspace folders
|
-- * lsp workspace folders
|
||||||
|
@ -12,38 +125,8 @@ M.patterns = { ".git", "lua" }
|
||||||
-- * root pattern of cwd
|
-- * root pattern of cwd
|
||||||
---@return string
|
---@return string
|
||||||
function M.get()
|
function M.get()
|
||||||
---@type string?
|
local roots = M.detect({ all = false })
|
||||||
local path = vim.api.nvim_buf_get_name(0)
|
return roots[1] and roots[1].paths[1] or vim.loop.cwd()
|
||||||
path = path ~= "" and vim.loop.fs_realpath(path) or nil
|
|
||||||
---@type string[]
|
|
||||||
local roots = {}
|
|
||||||
if path then
|
|
||||||
for _, client in pairs(Util.lsp.get_clients({ bufnr = 0 })) do
|
|
||||||
local workspace = client.config.workspace_folders
|
|
||||||
local paths = workspace and vim.tbl_map(function(ws)
|
|
||||||
return vim.uri_to_fname(ws.uri)
|
|
||||||
end, workspace) or client.config.root_dir and { client.config.root_dir } or {}
|
|
||||||
for _, p in ipairs(paths) do
|
|
||||||
local r = vim.loop.fs_realpath(p)
|
|
||||||
if path:find(r, 1, true) then
|
|
||||||
roots[#roots + 1] = r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(roots, function(a, b)
|
|
||||||
return #a > #b
|
|
||||||
end)
|
|
||||||
---@type string?
|
|
||||||
local root = roots[1]
|
|
||||||
if not root then
|
|
||||||
path = path and vim.fs.dirname(path) or vim.loop.cwd()
|
|
||||||
---@type string?
|
|
||||||
root = vim.fs.find(M.patterns, { path = path, upward = true })[1]
|
|
||||||
root = root and vim.fs.dirname(root) or vim.loop.cwd()
|
|
||||||
end
|
|
||||||
---@cast root string
|
|
||||||
return root
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
@ -22,7 +22,7 @@ function M.telescope(builtin, opts)
|
||||||
return function()
|
return function()
|
||||||
builtin = params.builtin
|
builtin = params.builtin
|
||||||
opts = params.opts
|
opts = params.opts
|
||||||
opts = vim.tbl_deep_extend("force", { cwd = Util.root.get() }, opts or {}) --[[@as lazyvim.util.telescope.opts]]
|
opts = vim.tbl_deep_extend("force", { cwd = Util.root() }, opts or {}) --[[@as lazyvim.util.telescope.opts]]
|
||||||
if builtin == "files" then
|
if builtin == "files" then
|
||||||
if vim.loop.fs_stat((opts.cwd or vim.loop.cwd()) .. "/.git") then
|
if vim.loop.fs_stat((opts.cwd or vim.loop.cwd()) .. "/.git") then
|
||||||
opts.show_untracked = true
|
opts.show_untracked = true
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue