Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 64 additions & 211 deletions lua/nvim-tree.lua
Original file line number Diff line number Diff line change
@@ -1,244 +1,103 @@
local api = require("nvim-tree.api")
local log = require("nvim-tree.log")
local view = require("nvim-tree.view")
local utils = require("nvim-tree.utils")
local find_file = require("nvim-tree.actions.tree.find-file")
local change_dir = require("nvim-tree.actions.tree.change-dir")
local full_name = require("nvim-tree.renderer.components.full-name")
local core = require("nvim-tree.core")
local notify = require("nvim-tree.notify")
local config = require("nvim-tree.config")

local M = {
init_root = "",
}

--- Helper function to execute some explorer method safely
---@param fn string # key of explorer
---@param ... any|nil
---@return function|nil
local function explorer_fn(fn, ...)
local explorer = core.get_explorer()
if explorer then
return explorer[fn](explorer, ...)
end
end

--- Update the tree root to a directory or the directory containing
---@param path string relative or absolute
---@param bufnr number|nil
function M.change_root(path, bufnr)
-- skip if current file is in ignore_list
if type(bufnr) == "number" then
local ft

if vim.fn.has("nvim-0.10") == 1 then
ft = vim.api.nvim_get_option_value("filetype", { buf = bufnr }) or ""
else
ft = vim.api.nvim_buf_get_option(bufnr, "filetype") or "" ---@diagnostic disable-line: deprecated
end

for _, value in pairs(config.g.update_focused_file.update_root.ignore_list) do
if utils.str_find(path, value) or utils.str_find(ft, value) then
return
end
end
end

-- don't find inexistent
if vim.fn.filereadable(path) == 0 then
return
end

local cwd = core.get_cwd()
if cwd == nil then
return
end

local vim_cwd = vim.fn.getcwd()

-- test if in vim_cwd
if utils.path_relative(path, vim_cwd) ~= path then
if vim_cwd ~= cwd then
explorer_fn("change_dir", vim_cwd)
end
return
end
-- test if in cwd
if utils.path_relative(path, cwd) ~= path then
return
end

-- otherwise test M.init_root
if config.g.prefer_startup_root and utils.path_relative(path, M.init_root) ~= path then
explorer_fn("change_dir", M.init_root)
return
end
-- otherwise root_dirs
for _, dir in pairs(config.g.root_dirs) do
dir = vim.fn.fnamemodify(dir, ":p")
if utils.path_relative(path, dir) ~= path then
explorer_fn("change_dir", dir)
return
end
end
-- finally fall back to the folder containing the file
explorer_fn("change_dir", vim.fn.fnamemodify(path, ":p:h"))
end

function M.tab_enter()
if view.is_visible({ any_tabpage = true }) then
local bufname = vim.api.nvim_buf_get_name(0)

local ft
if vim.fn.has("nvim-0.10") == 1 then
ft = vim.api.nvim_get_option_value("filetype", { buf = 0 }) or ""
else
ft = vim.api.nvim_buf_get_option(0, "ft") ---@diagnostic disable-line: deprecated
end

for _, filter in ipairs(config.g.tab.sync.ignore) do
if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then
return
end
end
view.open({ focus_tree = false })

local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end
end
end

function M.open_on_directory()
local should_proceed = config.g.hijack_directories.auto_open or view.is_visible()
if not should_proceed then
return
end

local buf = vim.api.nvim_get_current_buf()
local bufname = vim.api.nvim_buf_get_name(buf)
if vim.fn.isdirectory(bufname) ~= 1 then
return
end


local explorer = core.get_explorer()
if not explorer then
core.init(bufname)
end

explorer_fn("force_dirchange", bufname, true, false)
end

local function manage_netrw()
if config.g.hijack_netrw then
vim.cmd("silent! autocmd! FileExplorer *")
vim.cmd("autocmd VimEnter * ++once silent! autocmd! FileExplorer *")
end
if config.g.disable_netrw then
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
end
end
local M = {}

local function setup_autocommands()
local augroup_id = vim.api.nvim_create_augroup("NvimTree", { clear = true })
local function create_nvim_tree_autocmd(name, custom_opts)
local default_opts = { group = augroup_id }
vim.api.nvim_create_autocmd(name, vim.tbl_extend("force", default_opts, custom_opts))
end

-- prevent new opened file from opening in the same window as nvim-tree
create_nvim_tree_autocmd("BufWipeout", {
vim.api.nvim_create_autocmd("BufWipeout", {
group = augroup_id,
pattern = "NvimTree_*",
callback = function()
if not utils.is_nvim_tree_buf(0) then
return
end
if config.g.actions.open_file.eject then
view._prevent_buffer_override()
else
view.abandon_current_window()
end
require("nvim-tree.view").wipeout()
end,
})

if config.g.tab.sync.open then
create_nvim_tree_autocmd("TabEnter", { callback = vim.schedule_wrap(M.tab_enter) })
vim.api.nvim_create_autocmd("TabEnter", {
group = augroup_id,
callback = vim.schedule_wrap(function()
require("nvim-tree.actions.tree.open").tab_enter()
end)
})
end

if config.g.sync_root_with_cwd then
create_nvim_tree_autocmd("DirChanged", {
vim.api.nvim_create_autocmd("DirChanged", {
group = augroup_id,
callback = function()
change_dir.fn(vim.loop.cwd())
require("nvim-tree.actions.tree.change-dir").fn(vim.loop.cwd())
end,
})
end

if config.g.update_focused_file.enable then
create_nvim_tree_autocmd("BufEnter", {
vim.api.nvim_create_autocmd("BufEnter", {
group = augroup_id,
callback = function(event)
local exclude = config.g.update_focused_file.exclude
if type(exclude) == "function" and exclude(event) then
return
end
utils.debounce("BufEnter:find_file", config.g.view.debounce_delay, function()
find_file.fn()
end)
require("nvim-tree.actions.tree.find-file").buf_enter(event)
end,
})
end

if config.g.hijack_directories.enable and (config.g.disable_netrw or config.g.hijack_netrw) then
create_nvim_tree_autocmd({ "BufEnter", "BufNewFile" }, { callback = M.open_on_directory, nested = true })
vim.api.nvim_create_autocmd({ "BufEnter", "BufNewFile" }, {
group = augroup_id,
callback = function()
require("nvim-tree.actions.tree.open").open_on_directory()
end,
nested = true
})
end

if config.g.view.centralize_selection then
create_nvim_tree_autocmd("BufEnter", {
vim.api.nvim_create_autocmd("BufEnter", {
group = augroup_id,
pattern = "NvimTree_*",
callback = function()
vim.schedule(function()
vim.api.nvim_buf_call(0, function()
local is_term_mode = vim.api.nvim_get_mode().mode == "t"
if is_term_mode then
return
end
vim.cmd([[norm! zz]])
end)
callback = vim.schedule_wrap(function()
vim.api.nvim_buf_call(0, function()
local is_term_mode = vim.api.nvim_get_mode().mode == "t"
if is_term_mode then
return
end
vim.cmd([[norm! zz]])
end)
end,
end)
})
end

if config.g.diagnostics.enable then
create_nvim_tree_autocmd("DiagnosticChanged", {
vim.api.nvim_create_autocmd("DiagnosticChanged", {
group = augroup_id,
callback = function(ev)
log.line("diagnostics", "DiagnosticChanged")
require("nvim-tree.diagnostics").update_lsp(ev)
end,
})
create_nvim_tree_autocmd("User", {

vim.api.nvim_create_autocmd("User", {
group = augroup_id,
pattern = "CocDiagnosticChange",
callback = function()
log.line("diagnostics", "CocDiagnosticChange")
require("nvim-tree.diagnostics").update_coc()
end,
})
end

if config.g.view.float.enable and config.g.view.float.quit_on_focus_loss then
create_nvim_tree_autocmd("WinLeave", {
vim.api.nvim_create_autocmd("WinLeave", {
group = augroup_id,
pattern = "NvimTree_*",
callback = function()
if utils.is_nvim_tree_buf(0) then
view.close()
if require("nvim-tree.utils").is_nvim_tree_buf(0) then
require("nvim-tree.view").close()
end
end,
})
end

-- Handles event dispatch when tree is closed by `:q`
create_nvim_tree_autocmd("WinClosed", {
vim.api.nvim_create_autocmd("WinClosed", {
group = augroup_id,
pattern = "*",
---@param ev vim.api.keyset.create_autocmd.callback_args
callback = function(ev)
Expand All @@ -252,55 +111,49 @@ local function setup_autocommands()
})

-- renderer.full name
full_name.setup_autocommands()
require("nvim-tree.renderer.components.full-name").setup_autocommands()
end

function M.purge_all_state()
view.close_all_tabs()
view.abandon_all_windows()
local explorer = core.get_explorer()
if explorer then
require("nvim-tree.git").purge_state()
explorer:destroy()
core.reset_explorer()
end
-- purge orphaned that were not destroyed by their nodes
require("nvim-tree.watcher").purge_watchers()
end

---@param config_user? nvim_tree.config user supplied subset of config
---`require("nvim-tree").setup` must be called once to initialise nvim-tree.
---Call again to apply a change in configuration without restarting Nvim.
---@param config_user? nvim_tree.config subset, uses defaults when nil
function M.setup(config_user)
local log = require("nvim-tree.log")

-- Nvim version check
if vim.fn.has("nvim-0.9") == 0 then
notify.warn("nvim-tree.lua requires Neovim 0.9 or higher")
require("nvim-tree.notify").warn("nvim-tree.lua requires Neovim 0.9 or higher")
return
end

M.init_root = vim.fn.getcwd()

-- validate and merge with defaults as config.g
config.setup(config_user)

manage_netrw()

-- optionally create the log file
require("nvim-tree.log").start()

-- optionally log the configuration
if log.enabled("config") then
log.line("config", "default config + user")
log.raw("config", "%s\n", vim.inspect(config.g))
end

-- idempotent highlight definition
require("nvim-tree.appearance").set_hl()

-- idempotent au (re)definition
setup_autocommands()

-- subsequent calls to setup clear all state
if vim.g.NvimTreeSetup == 1 then
-- subsequent calls to setup
M.purge_all_state()
require("nvim-tree.core").purge_all_state()
end

-- hydrate post setup API
require("nvim-tree.api.impl").hydrate_post_setup(require("nvim-tree.api"))

vim.g.NvimTreeSetup = 1
vim.api.nvim_exec_autocmds("User", { pattern = "NvimTreeSetup" })

require("nvim-tree.api.impl").hydrate_post_setup(api)
end

vim.g.NvimTreeRequired = 1
Expand Down
Loading
Loading