mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-04 05:50:06 +08:00
740 lines
26 KiB
Lua
740 lines
26 KiB
Lua
local utils = require("neo-tree.utils")
|
|
local defaults = require("neo-tree.defaults")
|
|
local mapping_helper = require("neo-tree.setup.mapping-helper")
|
|
local events = require("neo-tree.events")
|
|
local log = require("neo-tree.log")
|
|
local file_nesting = require("neo-tree.sources.common.file-nesting")
|
|
local highlights = require("neo-tree.ui.highlights")
|
|
local manager = require("neo-tree.sources.manager")
|
|
local netrw = require("neo-tree.setup.netrw")
|
|
|
|
local M = {}
|
|
|
|
local normalize_mappings = function(config)
|
|
if config == nil then
|
|
return false
|
|
end
|
|
local mappings = utils.get_value(config, "window.mappings", nil)
|
|
if mappings then
|
|
local fixed = mapping_helper.normalize_map(mappings)
|
|
config.window.mappings = fixed
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local events_setup = false
|
|
local define_events = function()
|
|
if events_setup then
|
|
return
|
|
end
|
|
|
|
events.define_event(events.FS_EVENT, {
|
|
debounce_frequency = 100,
|
|
debounce_strategy = utils.debounce_strategy.CALL_LAST_ONLY,
|
|
})
|
|
|
|
local v = vim.version()
|
|
local diag_autocmd = "DiagnosticChanged"
|
|
if v.major < 1 and v.minor < 6 then
|
|
diag_autocmd = "User LspDiagnosticsChanged"
|
|
end
|
|
events.define_autocmd_event(events.VIM_DIAGNOSTIC_CHANGED, { diag_autocmd }, 500, function(args)
|
|
args.diagnostics_lookup = utils.get_diagnostic_counts()
|
|
return args
|
|
end)
|
|
|
|
|
|
|
|
local update_opened_buffers = function(args)
|
|
args.opened_buffers = utils.get_opened_buffers()
|
|
return args
|
|
end
|
|
|
|
events.define_autocmd_event(events.VIM_AFTER_SESSION_LOAD, { "SessionLoadPost" }, 200)
|
|
events.define_autocmd_event(events.VIM_BUFFER_ADDED, { "BufAdd" }, 200, update_opened_buffers)
|
|
events.define_autocmd_event(
|
|
events.VIM_BUFFER_DELETED,
|
|
{ "BufDelete" },
|
|
200,
|
|
update_opened_buffers
|
|
)
|
|
events.define_autocmd_event(events.VIM_BUFFER_ENTER, { "BufEnter", "BufWinEnter" }, 0)
|
|
events.define_autocmd_event(
|
|
events.VIM_BUFFER_MODIFIED_SET,
|
|
{ "BufModifiedSet" },
|
|
0,
|
|
update_opened_buffers
|
|
)
|
|
events.define_autocmd_event(events.VIM_COLORSCHEME, { "ColorScheme" }, 0)
|
|
events.define_autocmd_event(events.VIM_CURSOR_MOVED, { "CursorMoved" }, 100)
|
|
events.define_autocmd_event(events.VIM_DIR_CHANGED, { "DirChanged" }, 200, nil, true)
|
|
events.define_autocmd_event(events.VIM_INSERT_LEAVE, { "InsertLeave" }, 200)
|
|
events.define_autocmd_event(events.VIM_LEAVE, { "VimLeavePre" })
|
|
events.define_autocmd_event(events.VIM_RESIZED, { "VimResized" }, 100)
|
|
events.define_autocmd_event(events.VIM_TAB_CLOSED, { "TabClosed" })
|
|
events.define_autocmd_event(events.VIM_TERMINAL_ENTER, { "TermEnter" }, 0)
|
|
events.define_autocmd_event(events.VIM_TEXT_CHANGED_NORMAL, { "TextChanged" }, 200)
|
|
events.define_autocmd_event(events.VIM_WIN_CLOSED, { "WinClosed" })
|
|
events.define_autocmd_event(events.VIM_WIN_ENTER, { "WinEnter" }, 0, nil, true)
|
|
|
|
events.define_autocmd_event(events.GIT_EVENT, { "User FugitiveChanged" }, 100)
|
|
events.define_event(events.GIT_STATUS_CHANGED, { debounce_frequency = 0 })
|
|
events_setup = true
|
|
|
|
events.subscribe({
|
|
event = events.VIM_LEAVE,
|
|
handler = function()
|
|
events.clear_all_events()
|
|
end,
|
|
})
|
|
|
|
events.subscribe({
|
|
event = events.VIM_RESIZED,
|
|
handler = function()
|
|
require("neo-tree.ui.renderer").update_floating_window_layouts()
|
|
end,
|
|
})
|
|
end
|
|
|
|
local prior_window_options = {}
|
|
|
|
--- Store the current window options so we can restore them when we close the tree.
|
|
--- @param winid number | nil The window id to store the options for, defaults to current window
|
|
local store_local_window_settings = function(winid)
|
|
winid = winid or vim.api.nvim_get_current_win()
|
|
local neo_tree_settings_applied, _ =
|
|
pcall(vim.api.nvim_win_get_var, winid, "neo_tree_settings_applied")
|
|
if neo_tree_settings_applied then
|
|
-- don't store our own window settings
|
|
return
|
|
end
|
|
prior_window_options[tostring(winid)] = {
|
|
cursorline = vim.wo.cursorline,
|
|
cursorlineopt = vim.wo.cursorlineopt,
|
|
foldcolumn = vim.wo.foldcolumn,
|
|
wrap = vim.wo.wrap,
|
|
list = vim.wo.list,
|
|
spell = vim.wo.spell,
|
|
number = vim.wo.number,
|
|
relativenumber = vim.wo.relativenumber,
|
|
winhighlight = vim.wo.winhighlight,
|
|
}
|
|
end
|
|
|
|
--- Restore the window options for the current window
|
|
--- @param winid number | nil The window id to restore the options for, defaults to current window
|
|
local restore_local_window_settings = function(winid)
|
|
winid = winid or vim.api.nvim_get_current_win()
|
|
-- return local window settings to their prior values
|
|
local wo = prior_window_options[tostring(winid)]
|
|
if wo then
|
|
vim.wo.cursorline = wo.cursorline
|
|
vim.wo.cursorlineopt = wo.cursorlineopt
|
|
vim.wo.foldcolumn = wo.foldcolumn
|
|
vim.wo.wrap = wo.wrap
|
|
vim.wo.list = wo.list
|
|
vim.wo.spell = wo.spell
|
|
vim.wo.number = wo.number
|
|
vim.wo.relativenumber = wo.relativenumber
|
|
vim.wo.winhighlight = wo.winhighlight
|
|
log.debug("Window settings restored")
|
|
vim.api.nvim_win_set_var(0, "neo_tree_settings_applied", false)
|
|
else
|
|
log.debug("No window settings to restore")
|
|
end
|
|
end
|
|
|
|
local last_buffer_enter_filetype = nil
|
|
M.buffer_enter_event = function()
|
|
-- if it is a neo-tree window, just set local options
|
|
if vim.bo.filetype == "neo-tree" then
|
|
if last_buffer_enter_filetype == "neo-tree" then
|
|
-- we've switched to another neo-tree window
|
|
events.fire_event(events.NEO_TREE_BUFFER_LEAVE)
|
|
else
|
|
store_local_window_settings()
|
|
end
|
|
vim.cmd([[
|
|
setlocal cursorline
|
|
setlocal cursorlineopt=line
|
|
setlocal nowrap
|
|
setlocal nolist nospell nonumber norelativenumber
|
|
]])
|
|
|
|
local winhighlight =
|
|
"Normal:NeoTreeNormal,NormalNC:NeoTreeNormalNC,SignColumn:NeoTreeSignColumn,CursorLine:NeoTreeCursorLine,FloatBorder:NeoTreeFloatBorder,StatusLine:NeoTreeStatusLine,StatusLineNC:NeoTreeStatusLineNC,VertSplit:NeoTreeVertSplit,EndOfBuffer:NeoTreeEndOfBuffer"
|
|
if vim.version().minor >= 7 then
|
|
vim.cmd("setlocal winhighlight=" .. winhighlight .. ",WinSeparator:NeoTreeWinSeparator")
|
|
else
|
|
vim.cmd("setlocal winhighlight=" .. winhighlight)
|
|
end
|
|
|
|
events.fire_event(events.NEO_TREE_BUFFER_ENTER)
|
|
last_buffer_enter_filetype = vim.bo.filetype
|
|
vim.api.nvim_win_set_var(0, "neo_tree_settings_applied", true)
|
|
return
|
|
end
|
|
|
|
if vim.bo.filetype == "neo-tree-popup" then
|
|
vim.cmd([[
|
|
setlocal winhighlight=Normal:NeoTreeFloatNormal,FloatBorder:NeoTreeFloatBorder
|
|
setlocal nolist nospell nonumber norelativenumber
|
|
]])
|
|
events.fire_event(events.NEO_TREE_POPUP_BUFFER_ENTER)
|
|
last_buffer_enter_filetype = vim.bo.filetype
|
|
return
|
|
end
|
|
|
|
if last_buffer_enter_filetype == "neo-tree" then
|
|
events.fire_event(events.NEO_TREE_BUFFER_LEAVE)
|
|
end
|
|
if last_buffer_enter_filetype == "neo-tree-popup" then
|
|
events.fire_event(events.NEO_TREE_POPUP_BUFFER_LEAVE)
|
|
end
|
|
last_buffer_enter_filetype = vim.bo.filetype
|
|
|
|
-- there is nothing more we want to do with floating windows
|
|
if utils.is_floating() then
|
|
return
|
|
end
|
|
|
|
-- if vim is trying to open a dir, then we hijack it
|
|
if netrw.hijack() then
|
|
return
|
|
end
|
|
|
|
-- For all others, make sure another buffer is not hijacking our window
|
|
-- ..but not if the position is "current"
|
|
local prior_buf = vim.fn.bufnr("#")
|
|
if prior_buf < 1 then
|
|
return
|
|
end
|
|
local winid = vim.api.nvim_get_current_win()
|
|
local prior_type = vim.api.nvim_buf_get_option(prior_buf, "filetype")
|
|
if prior_type == "neo-tree" then
|
|
local success, position = pcall(vim.api.nvim_buf_get_var, prior_buf, "neo_tree_position")
|
|
if not success then
|
|
-- just bail out now, the rest of these lookups will probably fail too.
|
|
return
|
|
end
|
|
|
|
if position == "current" then
|
|
-- nothing to do here, files are supposed to open in same window
|
|
return
|
|
end
|
|
|
|
local current_tabid = vim.api.nvim_get_current_tabpage()
|
|
local neo_tree_tabid = vim.api.nvim_buf_get_var(prior_buf, "neo_tree_tabid")
|
|
if neo_tree_tabid ~= current_tabid then
|
|
-- This a new tab, so the alternate being neo-tree doesn't matter.
|
|
return
|
|
end
|
|
local neo_tree_winid = vim.api.nvim_buf_get_var(prior_buf, "neo_tree_winid")
|
|
local current_winid = vim.api.nvim_get_current_win()
|
|
if neo_tree_winid ~= current_winid then
|
|
-- This is not the neo-tree window, so the alternate being neo-tree doesn't matter.
|
|
return
|
|
end
|
|
|
|
local bufname = vim.api.nvim_buf_get_name(0)
|
|
log.debug("redirecting buffer " .. bufname .. " to new split")
|
|
vim.cmd("b#")
|
|
-- Using schedule at this point fixes problem with syntax
|
|
-- highlighting in the buffer. I also prevents errors with diagnostics
|
|
-- trying to work with the buffer as it's being closed.
|
|
vim.schedule(function()
|
|
-- try to delete the buffer, only because if it was new it would take
|
|
-- on options from the neo-tree window that are undesirable.
|
|
pcall(vim.cmd, "bdelete " .. bufname)
|
|
local fake_state = {
|
|
window = {
|
|
position = position,
|
|
},
|
|
}
|
|
utils.open_file(fake_state, bufname)
|
|
end)
|
|
end
|
|
end
|
|
|
|
M.win_enter_event = function()
|
|
local win_id = vim.api.nvim_get_current_win()
|
|
if utils.is_floating(win_id) then
|
|
return
|
|
end
|
|
|
|
-- if the new win is not a floating window, make sure all neo-tree floats are closed
|
|
manager.close_all("float")
|
|
|
|
if M.config.close_if_last_window then
|
|
local tabid = vim.api.nvim_get_current_tabpage()
|
|
local wins = utils.get_value(M, "config.prior_windows", {})[tabid]
|
|
local prior_exists = utils.truthy(wins)
|
|
local non_floating_wins = vim.tbl_filter(function(win)
|
|
return not utils.is_floating(win)
|
|
end, vim.api.nvim_tabpage_list_wins(tabid))
|
|
local win_count = #non_floating_wins
|
|
log.trace("checking if last window")
|
|
log.trace("prior window exists = ", prior_exists)
|
|
log.trace("win_count: ", win_count)
|
|
if prior_exists and win_count == 1 and vim.o.filetype == "neo-tree" then
|
|
local position = vim.api.nvim_buf_get_var(0, "neo_tree_position")
|
|
local source = vim.api.nvim_buf_get_var(0, "neo_tree_source")
|
|
if position ~= "current" then
|
|
-- close_if_last_window just doesn't make sense for a split style
|
|
log.trace("last window, closing")
|
|
local state = require("neo-tree.sources.manager").get_state(source)
|
|
if state == nil then
|
|
return
|
|
end
|
|
local mod = utils.get_opened_buffers()
|
|
log.debug("close_if_last_window, modified files found: ", vim.inspect(mod))
|
|
for filename, buf_info in pairs(mod) do
|
|
if buf_info.modified then
|
|
local buf_name, message
|
|
if vim.startswith(filename, "[No Name]#") then
|
|
buf_name = string.sub(filename, 11)
|
|
message = "Cannot close because an unnamed buffer is modified. Please save or discard this file."
|
|
else
|
|
buf_name = filename
|
|
message = "Cannot close because one of the files is modified. Please save or discard changes."
|
|
end
|
|
log.trace("close_if_last_window, showing unnamed modified buffer: ", filename)
|
|
vim.schedule(function()
|
|
log.warn(message)
|
|
vim.cmd("rightbelow vertical split")
|
|
vim.api.nvim_win_set_width(win_id, state.window.width or 40)
|
|
vim.cmd("b" .. buf_name)
|
|
end)
|
|
return
|
|
end
|
|
end
|
|
vim.cmd("q!")
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
if vim.o.filetype == "neo-tree" then
|
|
local _, position = pcall(vim.api.nvim_buf_get_var, 0, "neo_tree_position")
|
|
if position == "current" then
|
|
-- make sure the buffer wasn't moved to a new window
|
|
local neo_tree_winid = vim.api.nvim_buf_get_var(0, "neo_tree_winid")
|
|
local current_winid = vim.api.nvim_get_current_win()
|
|
local current_bufnr = vim.api.nvim_get_current_buf()
|
|
if neo_tree_winid ~= current_winid then
|
|
-- At this point we know that either the neo-tree window was split,
|
|
-- or the neo-tree buffer is being shown in another window for some other reason.
|
|
-- Sometime the split is just the first step in the process of opening somethig else,
|
|
-- so instead of fixing this right away, we add a short delay and check back again to see
|
|
-- if the buffer is still in this window.
|
|
local old_state = manager.get_state("filesystem", nil, neo_tree_winid)
|
|
vim.schedule(function()
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
if bufnr ~= current_bufnr then
|
|
-- The neo-tree buffer was replaced with something else, so we don't need to do anything.
|
|
log.trace("neo-tree buffer replaced with something else - no further action required")
|
|
return
|
|
end
|
|
-- create a new tree for this window
|
|
local state = manager.get_state("filesystem", nil, current_winid)
|
|
state.path = old_state.path
|
|
state.current_position = "current"
|
|
local renderer = require("neo-tree.ui.renderer")
|
|
state.force_open_folders = renderer.get_expanded_nodes(old_state.tree)
|
|
require("neo-tree.sources.filesystem")._navigate_internal(state, nil, nil, nil, false)
|
|
end)
|
|
return
|
|
end
|
|
end
|
|
-- it's a neo-tree window, ignore
|
|
return
|
|
end
|
|
|
|
M.config.prior_windows = M.config.prior_windows or {}
|
|
|
|
local tabid = vim.api.nvim_get_current_tabpage()
|
|
local tab_windows = M.config.prior_windows[tabid]
|
|
if tab_windows == nil then
|
|
tab_windows = {}
|
|
M.config.prior_windows[tabid] = tab_windows
|
|
end
|
|
table.insert(tab_windows, win_id)
|
|
|
|
-- prune the history when it gets too big
|
|
if #tab_windows > 100 then
|
|
local new_array = {}
|
|
local win_count = #tab_windows
|
|
for i = 80, win_count do
|
|
table.insert(new_array, tab_windows[i])
|
|
end
|
|
M.config.prior_windows[tabid] = new_array
|
|
end
|
|
end
|
|
|
|
M.set_log_level = function(level)
|
|
log.set_level(level)
|
|
end
|
|
|
|
local function merge_global_components_config(components, config)
|
|
local indent_exists = false
|
|
local merged_components = {}
|
|
local do_merge
|
|
|
|
do_merge = function(component)
|
|
local name = component[1]
|
|
if type(name) == "string" then
|
|
if name == "indent" then
|
|
indent_exists = true
|
|
end
|
|
local merged = { name }
|
|
local global_config = config.default_component_configs[name]
|
|
if global_config then
|
|
for k, v in pairs(global_config) do
|
|
merged[k] = v
|
|
end
|
|
end
|
|
for k, v in pairs(component) do
|
|
merged[k] = v
|
|
end
|
|
if name == "container" then
|
|
for i, child in ipairs(component.content) do
|
|
merged.content[i] = do_merge(child)
|
|
end
|
|
end
|
|
return merged
|
|
else
|
|
log.error("component name is the wrong type", component)
|
|
end
|
|
end
|
|
|
|
for _, component in ipairs(components) do
|
|
local merged = do_merge(component)
|
|
table.insert(merged_components, merged)
|
|
end
|
|
|
|
-- If the indent component is not specified, then add it.
|
|
-- We do this because it used to be implicitly added, so we don't want to
|
|
-- break any existing configs.
|
|
if not indent_exists then
|
|
local indent = { "indent" }
|
|
for k, v in pairs(config.default_component_configs.indent or {}) do
|
|
indent[k] = v
|
|
end
|
|
table.insert(merged_components, 1, indent)
|
|
end
|
|
return merged_components
|
|
end
|
|
|
|
local merge_renderers = function(default_config, source_default_config, user_config)
|
|
-- This can't be a deep copy/merge. If a renderer is specified in the target it completely
|
|
-- replaces the base renderer.
|
|
|
|
if source_default_config == nil then
|
|
-- first override the default config global renderer with the user's global renderers
|
|
for name, renderer in pairs(user_config.renderers or {}) do
|
|
log.debug("overriding global renderer for " .. name)
|
|
default_config.renderers[name] = renderer
|
|
end
|
|
else
|
|
-- then override the global renderers with the source specific renderers
|
|
source_default_config.renderers = source_default_config.renderers or {}
|
|
for name, renderer in pairs(default_config.renderers or {}) do
|
|
if source_default_config.renderers[name] == nil then
|
|
log.debug("overriding source renderer for " .. name)
|
|
local r = {}
|
|
-- Only copy components that exist in the target source.
|
|
-- This alllows us to specify global renderers that include components from all sources,
|
|
-- even if some of those components are not universal
|
|
for _, value in ipairs(renderer) do
|
|
if value[1] and source_default_config.components[value[1]] ~= nil then
|
|
table.insert(r, value)
|
|
end
|
|
end
|
|
source_default_config.renderers[name] = r
|
|
end
|
|
end
|
|
|
|
-- if user sets renderers, completely wipe the default ones
|
|
local source_name = source_default_config.name
|
|
for name, _ in pairs(source_default_config.renderers) do
|
|
local user = utils.get_value(user_config, source_name .. ".renderers." .. name)
|
|
if user then
|
|
source_default_config.renderers[name] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
M.merge_config = function(user_config, is_auto_config)
|
|
local default_config = vim.deepcopy(defaults)
|
|
user_config = vim.deepcopy(user_config or {})
|
|
|
|
local migrations = require("neo-tree.setup.deprecations").migrate(user_config)
|
|
if #migrations > 0 then
|
|
-- defer to make sure it is the last message printed
|
|
vim.defer_fn(function()
|
|
vim.cmd(
|
|
"echohl WarningMsg | echo 'Some options have changed, please run `:Neotree migrations` to see the changes' | echohl NONE"
|
|
)
|
|
end, 50)
|
|
end
|
|
|
|
if user_config.log_level ~= nil then
|
|
M.set_log_level(user_config.log_level)
|
|
end
|
|
log.use_file(user_config.log_to_file, true)
|
|
log.debug("setup")
|
|
|
|
events.clear_all_events()
|
|
define_events()
|
|
|
|
-- Prevent accidentally opening another file in the neo-tree window.
|
|
events.subscribe({
|
|
event = events.VIM_BUFFER_ENTER,
|
|
handler = M.buffer_enter_event,
|
|
})
|
|
|
|
-- Setup autocmd for neo-tree BufLeave, to restore window settings.
|
|
-- This is set to happen just before leaving the window.
|
|
-- The patterns used should ensure it only runs in neo-tree windows where position = "current"
|
|
local augroup = vim.api.nvim_create_augroup("NeoTree_BufLeave", { clear = true })
|
|
local bufleave = function(data)
|
|
-- Vim patterns in autocmds are not quite precise enough
|
|
-- so we are doing a second stage filter in lua
|
|
local pattern = "neo%-tree [^ ]+ %[1%d%d%d%]"
|
|
if string.match(data.file, pattern) then
|
|
restore_local_window_settings()
|
|
end
|
|
end
|
|
vim.api.nvim_create_autocmd({ "BufWinLeave" }, {
|
|
group = augroup,
|
|
pattern = "neo-tree *",
|
|
callback = bufleave,
|
|
})
|
|
|
|
if user_config.event_handlers ~= nil then
|
|
for _, handler in ipairs(user_config.event_handlers) do
|
|
events.subscribe(handler)
|
|
end
|
|
end
|
|
|
|
highlights.setup()
|
|
|
|
-- used to either limit the sources that or loaded, or add extra external sources
|
|
local all_sources = {}
|
|
local all_source_names = {}
|
|
for _, source in ipairs(user_config.sources or default_config.sources) do
|
|
local parts = utils.split(source, ".")
|
|
local name = parts[#parts]
|
|
local is_internal_ns, is_external_ns = false, false
|
|
local module
|
|
|
|
if #parts == 1 then
|
|
-- might be a module name in the internal namespace
|
|
is_internal_ns, module = pcall(require, "neo-tree.sources." .. source)
|
|
end
|
|
if is_internal_ns then
|
|
name = module.name or name
|
|
all_sources[name] = "neo-tree.sources." .. name
|
|
else
|
|
-- fully qualified module name
|
|
-- or just a root level module name
|
|
is_external_ns, module = pcall(require, source)
|
|
if is_external_ns then
|
|
name = module.name or name
|
|
all_sources[name] = source
|
|
else
|
|
log.error("Source module not found", source)
|
|
name = nil
|
|
end
|
|
end
|
|
if name then
|
|
default_config[name] = module.default_config or default_config[name]
|
|
table.insert(all_source_names, name)
|
|
end
|
|
end
|
|
log.debug("Sources to load: ", vim.inspect(all_sources))
|
|
require("neo-tree.command.parser").setup(all_source_names)
|
|
|
|
-- setup the default values for all sources
|
|
normalize_mappings(default_config)
|
|
normalize_mappings(user_config)
|
|
merge_renderers(default_config, nil, user_config)
|
|
|
|
for source_name, mod_root in pairs(all_sources) do
|
|
local module = require(mod_root)
|
|
default_config[source_name] = default_config[source_name]
|
|
or {
|
|
renderers = {},
|
|
components = {},
|
|
}
|
|
local source_default_config = default_config[source_name]
|
|
source_default_config.components = module.components or require(mod_root .. ".components")
|
|
source_default_config.commands = module.commands or require(mod_root .. ".commands")
|
|
source_default_config.name = source_name
|
|
source_default_config.display_name = module.display_name or source_default_config.name
|
|
|
|
if user_config.use_default_mappings == false then
|
|
default_config.window.mappings = {}
|
|
source_default_config.window.mappings = {}
|
|
end
|
|
-- Make sure all the mappings are normalized so they will merge properly.
|
|
normalize_mappings(source_default_config)
|
|
normalize_mappings(user_config[source_name])
|
|
-- merge the global config with the source specific config
|
|
source_default_config.window = vim.tbl_deep_extend(
|
|
"force",
|
|
default_config.window or {},
|
|
source_default_config.window or {},
|
|
user_config.window or {}
|
|
)
|
|
|
|
merge_renderers(default_config, source_default_config, user_config)
|
|
|
|
--validate the window.position
|
|
local pos_key = source_name .. ".window.position"
|
|
local position = utils.get_value(user_config, pos_key, "left", true)
|
|
local valid_positions = {
|
|
left = true,
|
|
right = true,
|
|
top = true,
|
|
bottom = true,
|
|
float = true,
|
|
current = true,
|
|
}
|
|
if not valid_positions[position] then
|
|
log.error("Invalid value for ", pos_key, ": ", position)
|
|
user_config[source_name].window.position = "left"
|
|
end
|
|
end
|
|
--print(vim.inspect(default_config.filesystem))
|
|
|
|
-- Moving user_config.sources to user_config.orig_sources
|
|
user_config.orig_sources = user_config.sources and user_config.sources or {}
|
|
|
|
-- apply the users config
|
|
M.config = vim.tbl_deep_extend("force", default_config, user_config)
|
|
|
|
-- RE: 873, fixes issue with invalid source checking by overriding
|
|
-- source table with name table
|
|
-- Setting new "sources" to be the parsed names of the sources
|
|
M.config.sources = all_source_names
|
|
|
|
if ( M.config.source_selector.winbar or M.config.source_selector.statusline )
|
|
and M.config.source_selector.sources
|
|
and not user_config.default_source then
|
|
-- Set the default source to the head of these
|
|
-- This resolves some weirdness with the source selector having
|
|
-- a different "head" item than our current default.
|
|
-- Removing this line makes Neo-tree show the "filesystem"
|
|
-- source instead of whatever the first item in the config is.
|
|
-- Probably don't remove this unless you have a better fix for that
|
|
M.config.default_source = M.config.source_selector.sources[1].source
|
|
end
|
|
-- Check if the default source is not included in config.sources
|
|
-- log a warning and then "pick" the first in the sources list
|
|
local match = false
|
|
for _, source in ipairs(M.config.sources) do
|
|
if source == M.config.default_source then
|
|
match = true
|
|
break
|
|
end
|
|
end
|
|
if not match then
|
|
M.config.default_source = M.config.sources[1]
|
|
log.warn(string.format("Invalid default source found in configuration. Using first available source: %s", M.config.default_source))
|
|
end
|
|
|
|
if not M.config.enable_git_status then
|
|
M.config.git_status_async = false
|
|
end
|
|
|
|
-- Validate that the source_selector.sources are all available and if any
|
|
-- aren't, remove them
|
|
local source_selector_sources = {}
|
|
for _, ss_source in ipairs(M.config.source_selector.sources or {}) do
|
|
local source_match = false
|
|
for _, source in ipairs(M.config.sources) do
|
|
if ss_source.source == source then
|
|
source_match = true
|
|
break
|
|
end
|
|
end
|
|
if source_match then
|
|
table.insert(source_selector_sources, ss_source)
|
|
else
|
|
log.debug(string.format("Unable to locate Neo-tree extension %s", ss_source.source))
|
|
end
|
|
end
|
|
M.config.source_selector.sources = source_selector_sources
|
|
|
|
file_nesting.setup(M.config.nesting_rules)
|
|
|
|
for source_name, mod_root in pairs(all_sources) do
|
|
for name, rndr in pairs(M.config[source_name].renderers) do
|
|
M.config[source_name].renderers[name] = merge_global_components_config(rndr, M.config)
|
|
end
|
|
local module = require(mod_root)
|
|
if M.config.commands then
|
|
M.config[source_name].commands =
|
|
vim.tbl_extend("keep", M.config[source_name].commands or {}, M.config.commands)
|
|
end
|
|
manager.setup(source_name, M.config[source_name], M.config, module)
|
|
manager.redraw(source_name)
|
|
end
|
|
|
|
if M.config.auto_clean_after_session_restore then
|
|
require("neo-tree.ui.renderer").clean_invalid_neotree_buffers(false)
|
|
events.subscribe({
|
|
event = events.VIM_AFTER_SESSION_LOAD,
|
|
handler = function()
|
|
require("neo-tree.ui.renderer").clean_invalid_neotree_buffers(true)
|
|
end,
|
|
})
|
|
end
|
|
|
|
events.subscribe({
|
|
event = events.VIM_COLORSCHEME,
|
|
handler = highlights.setup,
|
|
id = "neo-tree-highlight",
|
|
})
|
|
|
|
events.subscribe({
|
|
event = events.VIM_WIN_ENTER,
|
|
handler = M.win_enter_event,
|
|
id = "neo-tree-win-enter",
|
|
})
|
|
|
|
--Dispose ourselves if the tab closes
|
|
events.subscribe({
|
|
event = events.VIM_TAB_CLOSED,
|
|
handler = function(args)
|
|
local tabnr = tonumber(args.afile)
|
|
log.debug("VIM_TAB_CLOSED: disposing state for tabnr", tabnr)
|
|
-- Internally we use tabids to track state but <afile> is tabnr of a tab that has already been
|
|
-- closed so there is no way to get its tabid. Instead dispose all tabs that are no longer valid.
|
|
-- Must be scheduled because nvim_tabpage_is_valid does not work inside TabClosed event callback.
|
|
vim.schedule_wrap(manager.dispose_invalid_tabs)()
|
|
end,
|
|
})
|
|
|
|
--Dispose ourselves if the tab closes
|
|
events.subscribe({
|
|
event = events.VIM_WIN_CLOSED,
|
|
handler = function(args)
|
|
local winid = tonumber(args.afile)
|
|
log.debug("VIM_WIN_CLOSED: disposing state for window", winid)
|
|
manager.dispose_window(winid)
|
|
end,
|
|
})
|
|
|
|
local rt = utils.get_value(M.config, "resize_timer_interval", 50, true)
|
|
require("neo-tree.ui.renderer").resize_timer_interval = rt
|
|
|
|
return M.config
|
|
end
|
|
|
|
return M
|