Add .config/nvim/lua/project_nvim/project.lua
This commit is contained in:
parent
82805695c5
commit
7e8738af90
1 changed files with 286 additions and 0 deletions
286
.config/nvim/lua/project_nvim/project.lua
Normal file
286
.config/nvim/lua/project_nvim/project.lua
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
local config = require("project_nvim.config")
|
||||||
|
local history = require("project_nvim.utils.history")
|
||||||
|
local glob = require("project_nvim.utils.globtopattern")
|
||||||
|
local path = require("project_nvim.utils.path")
|
||||||
|
local uv = vim.loop
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
-- Internal states
|
||||||
|
M.attached_lsp = false
|
||||||
|
M.last_project = nil
|
||||||
|
|
||||||
|
function M.find_lsp_root()
|
||||||
|
-- Get lsp client for current buffer
|
||||||
|
-- Returns nil or string
|
||||||
|
local buf_ft = vim.api.nvim_buf_get_option(0, "filetype")
|
||||||
|
local clients = vim.lsp.get_clients({bufnr = 0})
|
||||||
|
if next(clients) == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, client in pairs(clients) do
|
||||||
|
local filetypes = client.config.filetypes
|
||||||
|
if filetypes and vim.tbl_contains(filetypes, buf_ft) then
|
||||||
|
if not vim.tbl_contains(config.options.ignore_lsp, client.name) then
|
||||||
|
return client.config.root_dir, client.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.find_pattern_root()
|
||||||
|
local search_dir = vim.fn.expand("%:p:h", true)
|
||||||
|
if vim.fn.has("win32") > 0 then
|
||||||
|
search_dir = search_dir:gsub("\\", "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
local last_dir_cache = ""
|
||||||
|
local curr_dir_cache = {}
|
||||||
|
|
||||||
|
local function get_parent(path)
|
||||||
|
path = path:match("^(.*)/")
|
||||||
|
if path == "" then
|
||||||
|
path = "/"
|
||||||
|
end
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_files(file_dir)
|
||||||
|
last_dir_cache = file_dir
|
||||||
|
curr_dir_cache = {}
|
||||||
|
|
||||||
|
local dir = uv.fs_scandir(file_dir)
|
||||||
|
if dir == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local file = uv.fs_scandir_next(dir)
|
||||||
|
if file == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(curr_dir_cache, file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is(dir, identifier)
|
||||||
|
dir = dir:match(".*/(.*)")
|
||||||
|
return dir == identifier
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sub(dir, identifier)
|
||||||
|
local path = get_parent(dir)
|
||||||
|
while true do
|
||||||
|
if is(path, identifier) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
local current = path
|
||||||
|
path = get_parent(path)
|
||||||
|
if current == path then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function child(dir, identifier)
|
||||||
|
local path = get_parent(dir)
|
||||||
|
return is(path, identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function has(dir, identifier)
|
||||||
|
if last_dir_cache ~= dir then
|
||||||
|
get_files(dir)
|
||||||
|
end
|
||||||
|
local pattern = glob.globtopattern(identifier)
|
||||||
|
for _, file in ipairs(curr_dir_cache) do
|
||||||
|
if file:match(pattern) ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function match(dir, pattern)
|
||||||
|
local first_char = pattern:sub(1, 1)
|
||||||
|
if first_char == "=" then
|
||||||
|
return is(dir, pattern:sub(2))
|
||||||
|
elseif first_char == "^" then
|
||||||
|
return sub(dir, pattern:sub(2))
|
||||||
|
elseif first_char == ">" then
|
||||||
|
return child(dir, pattern:sub(2))
|
||||||
|
else
|
||||||
|
return has(dir, pattern)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- breadth-first search
|
||||||
|
while true do
|
||||||
|
for _, pattern in ipairs(config.options.patterns) do
|
||||||
|
local exclude = false
|
||||||
|
if pattern:sub(1, 1) == "!" then
|
||||||
|
exclude = true
|
||||||
|
pattern = pattern:sub(2)
|
||||||
|
end
|
||||||
|
if match(search_dir, pattern) then
|
||||||
|
if exclude then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
return search_dir, "pattern " .. pattern
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local parent = get_parent(search_dir)
|
||||||
|
if parent == search_dir or parent == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
search_dir = parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: unused-local
|
||||||
|
local on_attach_lsp = function(client, bufnr)
|
||||||
|
M.on_buf_enter() -- Recalculate root dir after lsp attaches
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.attach_to_lsp()
|
||||||
|
if M.attached_lsp then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local _start_client = vim.lsp.start_client
|
||||||
|
vim.lsp.start_client = function(lsp_config)
|
||||||
|
if lsp_config.on_attach == nil then
|
||||||
|
lsp_config.on_attach = on_attach_lsp
|
||||||
|
else
|
||||||
|
local _on_attach = lsp_config.on_attach
|
||||||
|
lsp_config.on_attach = function(client, bufnr)
|
||||||
|
on_attach_lsp(client, bufnr)
|
||||||
|
_on_attach(client, bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return _start_client(lsp_config)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.attached_lsp = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.set_pwd(dir, method)
|
||||||
|
if dir ~= nil then
|
||||||
|
M.last_project = dir
|
||||||
|
table.insert(history.session_projects, dir)
|
||||||
|
|
||||||
|
if vim.fn.getcwd() ~= dir then
|
||||||
|
local scope_chdir = config.options.scope_chdir
|
||||||
|
if scope_chdir == 'global' then
|
||||||
|
vim.api.nvim_set_current_dir(dir)
|
||||||
|
elseif scope_chdir == 'tab' then
|
||||||
|
vim.cmd('tcd ' .. dir)
|
||||||
|
elseif scope_chdir == 'win' then
|
||||||
|
vim.cmd('lcd ' .. dir)
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if config.options.silent_chdir == false then
|
||||||
|
vim.notify("Set CWD to " .. dir .. " using " .. method)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get_project_root()
|
||||||
|
-- returns project root, as well as method
|
||||||
|
for _, detection_method in ipairs(config.options.detection_methods) do
|
||||||
|
if detection_method == "lsp" then
|
||||||
|
local root, lsp_name = M.find_lsp_root()
|
||||||
|
if root ~= nil then
|
||||||
|
return root, '"' .. lsp_name .. '"' .. " lsp"
|
||||||
|
end
|
||||||
|
elseif detection_method == "pattern" then
|
||||||
|
local root, method = M.find_pattern_root()
|
||||||
|
if root ~= nil then
|
||||||
|
return root, method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.is_file()
|
||||||
|
local buf_type = vim.api.nvim_buf_get_option(0, "buftype")
|
||||||
|
|
||||||
|
local whitelisted_buf_type = { "", "acwrite" }
|
||||||
|
local is_in_whitelist = false
|
||||||
|
for _, wtype in ipairs(whitelisted_buf_type) do
|
||||||
|
if buf_type == wtype then
|
||||||
|
is_in_whitelist = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not is_in_whitelist then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.on_buf_enter()
|
||||||
|
if vim.v.vim_did_enter == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not M.is_file() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_dir = vim.fn.expand("%:p:h", true)
|
||||||
|
if not path.exists(current_dir) or path.is_excluded(current_dir) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local root, method = M.get_project_root()
|
||||||
|
M.set_pwd(root, method)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.add_project_manually()
|
||||||
|
local current_dir = vim.fn.expand("%:p:h", true)
|
||||||
|
M.set_pwd(current_dir, 'manual')
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.init()
|
||||||
|
local autocmds = {}
|
||||||
|
if not config.options.manual_mode then
|
||||||
|
autocmds[#autocmds + 1] = 'autocmd VimEnter,BufEnter * ++nested lua require("project_nvim.project").on_buf_enter()'
|
||||||
|
|
||||||
|
if vim.tbl_contains(config.options.detection_methods, "lsp") then
|
||||||
|
M.attach_to_lsp()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.cmd([[
|
||||||
|
command! ProjectRoot lua require("project_nvim.project").on_buf_enter()
|
||||||
|
command! AddProject lua require("project_nvim.project").add_project_manually()
|
||||||
|
]])
|
||||||
|
|
||||||
|
autocmds[#autocmds + 1] =
|
||||||
|
'autocmd VimLeavePre * lua require("project_nvim.utils.history").write_projects_to_history()'
|
||||||
|
|
||||||
|
vim.cmd([[augroup project_nvim
|
||||||
|
au!
|
||||||
|
]])
|
||||||
|
for _, value in ipairs(autocmds) do
|
||||||
|
vim.cmd(value)
|
||||||
|
end
|
||||||
|
vim.cmd("augroup END")
|
||||||
|
|
||||||
|
history.read_projects_from_history()
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
Loading…
Add table
Reference in a new issue