Update generated nvim config
This commit is contained in:
@ -0,0 +1,66 @@
|
||||
local M = {}
|
||||
|
||||
---@class ProjectOptions
|
||||
M.defaults = {
|
||||
-- Manual mode doesn't automatically change your root directory, so you have
|
||||
-- the option to manually do so using `:ProjectRoot` command.
|
||||
manual_mode = false,
|
||||
|
||||
-- Methods of detecting the root directory. **"lsp"** uses the native neovim
|
||||
-- lsp, while **"pattern"** uses vim-rooter like glob pattern matching. Here
|
||||
-- order matters: if one is not detected, the other is used as fallback. You
|
||||
-- can also delete or rearangne the detection methods.
|
||||
detection_methods = { "lsp", "pattern" },
|
||||
|
||||
-- All the patterns used to detect root dir, when **"pattern"** is in
|
||||
-- detection_methods
|
||||
patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json" },
|
||||
|
||||
-- Table of lsp clients to ignore by name
|
||||
-- eg: { "efm", ... }
|
||||
ignore_lsp = {},
|
||||
|
||||
-- Don't calculate root dir on specific directories
|
||||
-- Ex: { "~/.cargo/*", ... }
|
||||
exclude_dirs = {},
|
||||
|
||||
-- Show hidden files in telescope
|
||||
show_hidden = false,
|
||||
|
||||
-- When set to false, you will get a message when project.nvim changes your
|
||||
-- directory.
|
||||
silent_chdir = true,
|
||||
|
||||
-- What scope to change the directory, valid options are
|
||||
-- * global (default)
|
||||
-- * tab
|
||||
-- * win
|
||||
scope_chdir = 'global',
|
||||
|
||||
-- Path where project.nvim will store the project history for use in
|
||||
-- telescope
|
||||
datapath = vim.fn.stdpath("data"),
|
||||
}
|
||||
|
||||
---@type ProjectOptions
|
||||
M.options = {}
|
||||
|
||||
M.setup = function(options)
|
||||
M.options = vim.tbl_deep_extend("force", M.defaults, options or {})
|
||||
|
||||
local glob = require("project_nvim.utils.globtopattern")
|
||||
local home = vim.fn.expand("~")
|
||||
M.options.exclude_dirs = vim.tbl_map(function(pattern)
|
||||
if vim.startswith(pattern, "~/") then
|
||||
pattern = home .. "/" .. pattern:sub(3, #pattern)
|
||||
end
|
||||
return glob.globtopattern(pattern)
|
||||
end, M.options.exclude_dirs)
|
||||
|
||||
vim.opt.autochdir = false -- implicitly unset autochdir
|
||||
|
||||
require("project_nvim.utils.path").init()
|
||||
require("project_nvim.project").init()
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,8 @@
|
||||
local config = require("project_nvim.config")
|
||||
local history = require("project_nvim.utils.history")
|
||||
local M = {}
|
||||
|
||||
M.setup = config.setup
|
||||
M.get_recent_projects = history.get_recent_projects
|
||||
|
||||
return M
|
||||
@ -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.buf_get_clients()
|
||||
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
|
||||
@ -0,0 +1,139 @@
|
||||
-- Credits for this module goes to: David Manura
|
||||
-- https://github.com/davidm/lua-glob-pattern
|
||||
|
||||
local M = { _TYPE = "module", _NAME = "globtopattern", _VERSION = "0.2.1.20120406" }
|
||||
|
||||
function M.globtopattern(g)
|
||||
-- Some useful references:
|
||||
-- - apr_fnmatch in Apache APR. For example,
|
||||
-- http://apr.apache.org/docs/apr/1.3/group__apr__fnmatch.html
|
||||
-- which cites POSIX 1003.2-1992, section B.6.
|
||||
|
||||
local p = "^" -- pattern being built
|
||||
local i = 0 -- index in g
|
||||
local c -- char at index i in g.
|
||||
|
||||
-- unescape glob char
|
||||
local function unescape()
|
||||
if c == "\\" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- escape pattern char
|
||||
local function escape(c)
|
||||
return c:match("^%w$") and c or "%" .. c
|
||||
end
|
||||
|
||||
-- Convert tokens at end of charset.
|
||||
local function charset_end()
|
||||
while 1 do
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "]" then
|
||||
p = p .. "]"
|
||||
break
|
||||
else
|
||||
if not unescape() then
|
||||
break
|
||||
end
|
||||
local c1 = c
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "-" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "]" then
|
||||
p = p .. escape(c1) .. "%-]"
|
||||
break
|
||||
else
|
||||
if not unescape() then
|
||||
break
|
||||
end
|
||||
p = p .. escape(c1) .. "-" .. escape(c)
|
||||
end
|
||||
elseif c == "]" then
|
||||
p = p .. escape(c1) .. "]"
|
||||
break
|
||||
else
|
||||
p = p .. escape(c1)
|
||||
i = i - 1 -- put back
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Convert tokens in charset.
|
||||
local function charset()
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" or c == "]" then
|
||||
p = "[^]"
|
||||
return false
|
||||
elseif c == "^" or c == "!" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "]" then
|
||||
-- ignored
|
||||
else
|
||||
p = p .. "[^"
|
||||
if not charset_end() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
p = p .. "["
|
||||
if not charset_end() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Convert tokens.
|
||||
while 1 do
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = p .. "$"
|
||||
break
|
||||
elseif c == "?" then
|
||||
p = p .. "."
|
||||
elseif c == "*" then
|
||||
p = p .. ".*"
|
||||
elseif c == "[" then
|
||||
if not charset() then
|
||||
break
|
||||
end
|
||||
elseif c == "\\" then
|
||||
i = i + 1
|
||||
c = g:sub(i, i)
|
||||
if c == "" then
|
||||
p = p .. "\\$"
|
||||
break
|
||||
end
|
||||
p = p .. escape(c)
|
||||
else
|
||||
p = p .. escape(c)
|
||||
end
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,178 @@
|
||||
local path = require("project_nvim.utils.path")
|
||||
local uv = vim.loop
|
||||
local M = {}
|
||||
local is_windows = vim.fn.has('win32') or vim.fn.has('wsl')
|
||||
|
||||
M.recent_projects = nil -- projects from previous neovim sessions
|
||||
M.session_projects = {} -- projects from current neovim session
|
||||
M.has_watch_setup = false
|
||||
|
||||
local function open_history(mode, callback)
|
||||
if callback ~= nil then -- async
|
||||
path.create_scaffolding(function(_, _)
|
||||
uv.fs_open(path.historyfile, mode, 438, callback)
|
||||
end)
|
||||
else -- sync
|
||||
path.create_scaffolding()
|
||||
return uv.fs_open(path.historyfile, mode, 438)
|
||||
end
|
||||
end
|
||||
|
||||
local function dir_exists(dir)
|
||||
local stat = uv.fs_stat(dir)
|
||||
if stat ~= nil and stat.type == "directory" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function normalise_path(path_to_normalise)
|
||||
local normalised_path = path_to_normalise:gsub("\\", "/"):gsub("//", "/")
|
||||
|
||||
if is_windows then
|
||||
normalised_path = normalised_path:sub(1,1):lower()..normalised_path:sub(2)
|
||||
end
|
||||
|
||||
return normalised_path
|
||||
end
|
||||
|
||||
local function delete_duplicates(tbl)
|
||||
local cache_dict = {}
|
||||
for _, v in ipairs(tbl) do
|
||||
local normalised_path = normalise_path(v)
|
||||
if cache_dict[normalised_path] == nil then
|
||||
cache_dict[normalised_path] = 1
|
||||
else
|
||||
cache_dict[normalised_path] = cache_dict[normalised_path] + 1
|
||||
end
|
||||
end
|
||||
|
||||
local res = {}
|
||||
for _, v in ipairs(tbl) do
|
||||
local normalised_path = normalise_path(v)
|
||||
if cache_dict[normalised_path] == 1 then
|
||||
table.insert(res, normalised_path)
|
||||
else
|
||||
cache_dict[normalised_path] = cache_dict[normalised_path] - 1
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function M.delete_project(project)
|
||||
for k, v in ipairs(M.recent_projects) do
|
||||
if v == project.value then
|
||||
M.recent_projects[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function deserialize_history(history_data)
|
||||
-- split data to table
|
||||
local projects = {}
|
||||
for s in history_data:gmatch("[^\r\n]+") do
|
||||
if not path.is_excluded(s) and dir_exists(s) then
|
||||
table.insert(projects, s)
|
||||
end
|
||||
end
|
||||
|
||||
projects = delete_duplicates(projects)
|
||||
|
||||
M.recent_projects = projects
|
||||
end
|
||||
|
||||
local function setup_watch()
|
||||
-- Only runs once
|
||||
if M.has_watch_setup == false then
|
||||
M.has_watch_setup = true
|
||||
local event = uv.new_fs_event()
|
||||
if event == nil then
|
||||
return
|
||||
end
|
||||
event:start(path.projectpath, {}, function(err, _, events)
|
||||
if err ~= nil then
|
||||
return
|
||||
end
|
||||
if events["change"] then
|
||||
M.recent_projects = nil
|
||||
M.read_projects_from_history()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function M.read_projects_from_history()
|
||||
open_history("r", function(_, fd)
|
||||
setup_watch()
|
||||
if fd ~= nil then
|
||||
uv.fs_fstat(fd, function(_, stat)
|
||||
if stat ~= nil then
|
||||
uv.fs_read(fd, stat.size, -1, function(_, data)
|
||||
uv.fs_close(fd, function(_, _) end)
|
||||
deserialize_history(data)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function sanitize_projects()
|
||||
local tbl = {}
|
||||
if M.recent_projects ~= nil then
|
||||
vim.list_extend(tbl, M.recent_projects)
|
||||
vim.list_extend(tbl, M.session_projects)
|
||||
else
|
||||
tbl = M.session_projects
|
||||
end
|
||||
|
||||
tbl = delete_duplicates(tbl)
|
||||
|
||||
local real_tbl = {}
|
||||
for _, dir in ipairs(tbl) do
|
||||
if dir_exists(dir) then
|
||||
table.insert(real_tbl, dir)
|
||||
end
|
||||
end
|
||||
|
||||
return real_tbl
|
||||
end
|
||||
|
||||
function M.get_recent_projects()
|
||||
return sanitize_projects()
|
||||
end
|
||||
|
||||
function M.write_projects_to_history()
|
||||
-- Unlike read projects, write projects is synchronous
|
||||
-- because it runs when vim ends
|
||||
local mode = "w"
|
||||
if M.recent_projects == nil then
|
||||
mode = "a"
|
||||
end
|
||||
local file = open_history(mode)
|
||||
|
||||
if file ~= nil then
|
||||
local res = sanitize_projects()
|
||||
|
||||
-- Trim table to last 100 entries
|
||||
local len_res = #res
|
||||
local tbl_out
|
||||
if #res > 100 then
|
||||
tbl_out = vim.list_slice(res, len_res - 100, len_res)
|
||||
else
|
||||
tbl_out = res
|
||||
end
|
||||
|
||||
-- Transform table to string
|
||||
local out = ""
|
||||
for _, v in ipairs(tbl_out) do
|
||||
out = out .. v .. "\n"
|
||||
end
|
||||
|
||||
-- Write string out to file and close
|
||||
uv.fs_write(file, out, -1)
|
||||
uv.fs_close(file)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,37 @@
|
||||
local config = require("project_nvim.config")
|
||||
local uv = vim.loop
|
||||
local M = {}
|
||||
|
||||
M.datapath = vim.fn.stdpath("data") -- directory
|
||||
M.projectpath = M.datapath .. "/project_nvim" -- directory
|
||||
M.historyfile = M.projectpath .. "/project_history" -- file
|
||||
|
||||
function M.init()
|
||||
M.datapath = require("project_nvim.config").options.datapath
|
||||
M.projectpath = M.datapath .. "/project_nvim" -- directory
|
||||
M.historyfile = M.projectpath .. "/project_history" -- file
|
||||
end
|
||||
|
||||
function M.create_scaffolding(callback)
|
||||
if callback ~= nil then -- async
|
||||
uv.fs_mkdir(M.projectpath, 448, callback)
|
||||
else -- sync
|
||||
uv.fs_mkdir(M.projectpath, 448)
|
||||
end
|
||||
end
|
||||
|
||||
function M.is_excluded(dir)
|
||||
for _, dir_pattern in ipairs(config.options.exclude_dirs) do
|
||||
if dir:match(dir_pattern) ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function M.exists(path)
|
||||
return vim.fn.empty(vim.fn.glob(path)) == 0
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,180 @@
|
||||
-- Inspiration from:
|
||||
-- https://github.com/nvim-telescope/telescope-project.nvim
|
||||
local has_telescope, telescope = pcall(require, "telescope")
|
||||
|
||||
if not has_telescope then
|
||||
return
|
||||
end
|
||||
|
||||
local finders = require("telescope.finders")
|
||||
local pickers = require("telescope.pickers")
|
||||
local telescope_config = require("telescope.config").values
|
||||
local actions = require("telescope.actions")
|
||||
local state = require("telescope.actions.state")
|
||||
local builtin = require("telescope.builtin")
|
||||
local entry_display = require("telescope.pickers.entry_display")
|
||||
|
||||
local history = require("project_nvim.utils.history")
|
||||
local project = require("project_nvim.project")
|
||||
local config = require("project_nvim.config")
|
||||
|
||||
----------
|
||||
-- Actions
|
||||
----------
|
||||
|
||||
local function create_finder()
|
||||
local results = history.get_recent_projects()
|
||||
|
||||
-- Reverse results
|
||||
for i = 1, math.floor(#results / 2) do
|
||||
results[i], results[#results - i + 1] = results[#results - i + 1], results[i]
|
||||
end
|
||||
local displayer = entry_display.create({
|
||||
separator = " ",
|
||||
items = {
|
||||
{
|
||||
width = 30,
|
||||
},
|
||||
{
|
||||
remaining = true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
local function make_display(entry)
|
||||
return displayer({ entry.name, { entry.value, "Comment" } })
|
||||
end
|
||||
|
||||
return finders.new_table({
|
||||
results = results,
|
||||
entry_maker = function(entry)
|
||||
local name = vim.fn.fnamemodify(entry, ":t")
|
||||
return {
|
||||
display = make_display,
|
||||
name = name,
|
||||
value = entry,
|
||||
ordinal = name .. " " .. entry,
|
||||
}
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local function change_working_directory(prompt_bufnr, prompt)
|
||||
local selected_entry = state.get_selected_entry(prompt_bufnr)
|
||||
if selected_entry == nil then
|
||||
actions.close(prompt_bufnr)
|
||||
return
|
||||
end
|
||||
local project_path = selected_entry.value
|
||||
if prompt == true then
|
||||
actions._close(prompt_bufnr, true)
|
||||
else
|
||||
actions.close(prompt_bufnr)
|
||||
end
|
||||
local cd_successful = project.set_pwd(project_path, "telescope")
|
||||
return project_path, cd_successful
|
||||
end
|
||||
|
||||
local function find_project_files(prompt_bufnr)
|
||||
local project_path, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd = project_path,
|
||||
hidden = config.options.show_hidden,
|
||||
mode = "insert",
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.find_files(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function browse_project_files(prompt_bufnr)
|
||||
local project_path, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd = project_path,
|
||||
hidden = config.options.show_hidden,
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.file_browser(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function search_in_project_files(prompt_bufnr)
|
||||
local project_path, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd = project_path,
|
||||
hidden = config.options.show_hidden,
|
||||
mode = "insert",
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.live_grep(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function recent_project_files(prompt_bufnr)
|
||||
local _, cd_successful = change_working_directory(prompt_bufnr, true)
|
||||
local opt = {
|
||||
cwd_only = true,
|
||||
hidden = config.options.show_hidden,
|
||||
}
|
||||
if cd_successful then
|
||||
builtin.oldfiles(opt)
|
||||
end
|
||||
end
|
||||
|
||||
local function delete_project(prompt_bufnr)
|
||||
local selectedEntry = state.get_selected_entry(prompt_bufnr)
|
||||
if selectedEntry == nil then
|
||||
actions.close(prompt_bufnr)
|
||||
return
|
||||
end
|
||||
local choice = vim.fn.confirm("Delete '" .. selectedEntry.value .. "' from project list?", "&Yes\n&No", 2)
|
||||
|
||||
if choice == 1 then
|
||||
history.delete_project(selectedEntry)
|
||||
|
||||
local finder = create_finder()
|
||||
state.get_current_picker(prompt_bufnr):refresh(finder, {
|
||||
reset_prompt = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---Main entrypoint for Telescope.
|
||||
---@param opts table
|
||||
local function projects(opts)
|
||||
opts = opts or {}
|
||||
|
||||
pickers.new(opts, {
|
||||
prompt_title = "Recent Projects",
|
||||
finder = create_finder(),
|
||||
previewer = false,
|
||||
sorter = telescope_config.generic_sorter(opts),
|
||||
attach_mappings = function(prompt_bufnr, map)
|
||||
map("n", "f", find_project_files)
|
||||
map("n", "b", browse_project_files)
|
||||
map("n", "d", delete_project)
|
||||
map("n", "s", search_in_project_files)
|
||||
map("n", "r", recent_project_files)
|
||||
map("n", "w", change_working_directory)
|
||||
|
||||
map("i", "<c-f>", find_project_files)
|
||||
map("i", "<c-b>", browse_project_files)
|
||||
map("i", "<c-d>", delete_project)
|
||||
map("i", "<c-s>", search_in_project_files)
|
||||
map("i", "<c-r>", recent_project_files)
|
||||
map("i", "<c-w>", change_working_directory)
|
||||
|
||||
local on_project_selected = function()
|
||||
find_project_files(prompt_bufnr)
|
||||
end
|
||||
actions.select_default:replace(on_project_selected)
|
||||
return true
|
||||
end,
|
||||
}):find()
|
||||
end
|
||||
|
||||
return telescope.register_extension({
|
||||
exports = {
|
||||
projects = projects,
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user