Merge branch 'master' into chore/action_cb_help_poc

This commit is contained in:
Alexander Courtis
2022-09-04 13:09:50 +10:00
23 changed files with 517 additions and 148 deletions

View File

@@ -27,12 +27,20 @@ function M.fn(fname)
local line = core.get_nodes_starting_line()
local absolute_paths_searched = {}
local found = Iterator.builder(core.get_explorer().nodes)
:matcher(function(node)
return node.absolute_path == fname_real or node.link_to == fname_real
end)
:applier(function(node)
line = line + 1
if vim.tbl_contains(absolute_paths_searched, node.absolute_path) then
return
end
table.insert(absolute_paths_searched, node.absolute_path)
local abs_match = vim.startswith(fname_real, node.absolute_path .. utils.path_separator)
local link_match = node.link_to and vim.startswith(fname_real, node.link_to .. utils.path_separator)

View File

@@ -7,42 +7,54 @@ local find_file = require("nvim-tree.actions.finders.find-file").fn
local M = {}
local function search(dir, input_path)
local path, name, stat, handle, _
local function search(search_dir, input_path)
local realpaths_searched = {}
if not dir then
if not search_dir then
return
end
handle, _ = uv.fs_scandir(dir)
if not handle then
return
end
local function iter(dir)
local realpath, path, name, stat, handle, _
name, _ = uv.fs_scandir_next(handle)
while name do
path = dir .. "/" .. name
stat, _ = uv.fs_stat(path)
if not stat then
break
handle, _ = uv.fs_scandir(dir)
if not handle then
return
end
if not filters.should_ignore(path) then
if string.find(path, "/" .. input_path .. "$") then
return path
end
if stat.type == "directory" then
path = search(path, input_path)
if path then
return path
end
end
realpath, _ = uv.fs_realpath(dir)
if not realpath or vim.tbl_contains(realpaths_searched, realpath) then
return
end
table.insert(realpaths_searched, realpath)
name, _ = uv.fs_scandir_next(handle)
while name do
path = dir .. "/" .. name
stat, _ = uv.fs_stat(path)
if not stat then
break
end
if not filters.should_ignore(path) then
if string.find(path, "/" .. input_path .. "$") then
return path
end
if stat.type == "directory" then
path = iter(path)
if path then
return path
end
end
end
name, _ = uv.fs_scandir_next(handle)
end
end
return iter(search_dir)
end
function M.fn()

View File

@@ -108,7 +108,7 @@ function M.fn(node)
end
-- INFO: defer needed when reload is automatic (watchers)
vim.defer_fn(function()
utils.focus_file(new_file_path)
utils.focus_file(utils.path_remove_trailing(new_file_path))
end, 150)
end)
end

View File

@@ -3,10 +3,15 @@ local luv = vim.loop
local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events"
local view = require "nvim-tree.view"
local M = {}
local function close_windows(windows)
if view.View.float.enable and #a.nvim_list_wins() == 1 then
return
end
for _, window in ipairs(windows) do
if a.nvim_win_is_valid(window) then
a.nvim_win_close(window, true)
@@ -18,11 +23,13 @@ local function clear_buffer(absolute_path)
local bufs = vim.fn.getbufinfo { bufloaded = 1, buflisted = 1 }
for _, buf in pairs(bufs) do
if buf.name == absolute_path then
if buf.hidden == 0 and #bufs > 1 then
if buf.hidden == 0 and (#bufs > 1 or view.View.float.enable) then
local winnr = a.nvim_get_current_win()
a.nvim_set_current_win(buf.windows[1])
vim.cmd ":bn"
a.nvim_set_current_win(winnr)
if not view.View.float.enable then
a.nvim_set_current_win(winnr)
end
end
a.nvim_buf_delete(buf.bufnr, { force = true })
if M.close_window then

View File

@@ -390,6 +390,7 @@ local DEFAULT_MAPPING_CONFIG = {
function M.setup(opts)
require("nvim-tree.actions.fs.trash").setup(opts)
require("nvim-tree.actions.node.system-open").setup(opts)
require("nvim-tree.actions.node.file-popup").setup(opts)
require("nvim-tree.actions.node.open-file").setup(opts)
require("nvim-tree.actions.root.change-dir").setup(opts)
require("nvim-tree.actions.fs.create-file").setup(opts)

View File

@@ -28,16 +28,13 @@ local function setup_window(node)
local max_width = vim.fn.max(vim.tbl_map(function(n)
return #n
end, lines))
local winnr = a.nvim_open_win(0, false, {
col = 1,
row = 1,
relative = "cursor",
local open_win_config = vim.tbl_extend("force", M.open_win_config, {
width = max_width + 1,
height = #lines,
border = "shadow",
noautocmd = true,
style = "minimal",
zindex = 60,
})
local winnr = a.nvim_open_win(0, false, open_win_config)
current_popup = {
winnr = winnr,
file_path = node.absolute_path,
@@ -78,4 +75,8 @@ function M.toggle_file_info(node)
})
end
function M.setup(opts)
M.open_win_config = opts.actions.file_popup.open_win_config
end
return M

View File

@@ -280,7 +280,7 @@ function M.fn(mode, filename)
end
function M.setup(opts)
M.quit_on_open = opts.actions.open_file.quit_on_open
M.quit_on_open = opts.actions.open_file.quit_on_open or opts.view.float.enable
M.resize_window = opts.actions.open_file.resize_window
if opts.actions.open_file.window_picker.chars then
opts.actions.open_file.window_picker.chars = tostring(opts.actions.open_file.window_picker.chars):upper()

View File

@@ -4,6 +4,7 @@ local watch = require "nvim-tree.explorer.watch"
local M = {
is_windows = vim.fn.has "win32" == 1,
is_wsl = vim.fn.has "wsl" == 1,
}
function M.folder(parent, absolute_path, name)
@@ -11,6 +12,7 @@ function M.folder(parent, absolute_path, name)
local has_children = handle and uv.fs_scandir_next(handle) ~= nil
return {
type = "directory",
absolute_path = absolute_path,
fs_stat = uv.fs_stat(absolute_path),
group_next = nil, -- If node is grouped, this points to the next child dir/link node
@@ -23,9 +25,19 @@ function M.folder(parent, absolute_path, name)
}
end
function M.is_executable(absolute_path, ext)
function M.is_executable(parent, absolute_path, ext)
if M.is_windows then
return utils.is_windows_exe(ext)
elseif M.is_wsl then
if parent.is_wsl_windows_fs_path == nil then
-- Evaluate lazily when needed and do so only once for each parent
-- as 'wslpath' calls can get expensive in highly populated directories.
parent.is_wsl_windows_fs_path = utils.is_wsl_windows_fs_path(absolute_path)
end
if parent.is_wsl_windows_fs_path then
return utils.is_wsl_windows_fs_exe(ext)
end
end
return uv.fs_access(absolute_path, "X")
end
@@ -34,8 +46,9 @@ function M.file(parent, absolute_path, name)
local ext = string.match(name, ".?[^.]+%.(.*)") or ""
return {
type = "file",
absolute_path = absolute_path,
executable = M.is_executable(absolute_path, ext),
executable = M.is_executable(parent, absolute_path, ext),
extension = ext,
fs_stat = uv.fs_stat(absolute_path),
name = name,
@@ -61,6 +74,7 @@ function M.link(parent, absolute_path, name)
end
return {
type = "link",
absolute_path = absolute_path,
fs_stat = uv.fs_stat(absolute_path),
group_next = nil, -- If node is grouped, this points to the next child dir/link node

View File

@@ -41,10 +41,32 @@ function M.reload(node, status)
break
end
local stat
local function fs_stat_cached(path)
if stat ~= nil then
return stat
end
stat = uv.fs_stat(path)
return stat
end
local abs = utils.path_join { cwd, name }
t = t or (uv.fs_stat(abs) or {}).type
t = t or (fs_stat_cached(abs) or {}).type
if not filters.should_ignore(abs) and not filters.should_ignore_git(abs, status.files) then
child_names[abs] = true
-- Recreate node if type changes.
if nodes_by_path[abs] then
local n = nodes_by_path[abs]
if n.type ~= t then
utils.array_remove(node.nodes, n)
common.node_destroy(n)
nodes_by_path[abs] = nil
end
end
if not nodes_by_path[abs] then
if t == "directory" and uv.fs_access(abs, "R") then
local folder = builders.folder(node, abs, name)
@@ -61,10 +83,12 @@ function M.reload(node, status)
table.insert(node.nodes, link)
end
end
end
local n = nodes_by_path[abs]
if n then
n.executable = builders.is_executable(abs, n.extension or "")
else
local n = nodes_by_path[abs]
if n then
n.executable = builders.is_executable(n.parent, abs, n.extension or "")
n.fs_stat = fs_stat_cached(abs)
end
end
end
end

View File

@@ -19,7 +19,7 @@ local function update_parent_statuses(node, project, root)
end
local function is_git(path)
return path:match "%.git$" ~= nil or path:match(utils.path_add_trailing ".git") ~= nil
return vim.fn.fnamemodify(path, ":t") == ".git"
end
local IGNORED_PATHS = {

View File

@@ -84,7 +84,7 @@ function Runner:_run_git_job()
local opts = self:_getopts(stdout, stderr)
log.line("git", "running job with timeout %dms", self.timeout)
log.line("git", "git %s", table.concat(opts.args, " "))
log.line("git", "git %s", table.concat(utils.array_remove_nils(opts.args), " "))
handle, pid = uv.spawn(
"git",

View File

@@ -14,7 +14,7 @@ function M.get_toplevel(cwd)
log.raw("git", toplevel)
log.profile_end(ps, "git toplevel %s", cwd)
if not toplevel or #toplevel == 0 or toplevel:match "fatal" then
if vim.v.shell_error ~= 0 or not toplevel or #toplevel == 0 or toplevel:match "fatal" then
return nil
end
@@ -23,6 +23,9 @@ function M.get_toplevel(cwd)
-- msys2 git support
if has_cygpath then
toplevel = vim.fn.system("cygpath -w " .. vim.fn.shellescape(toplevel))
if vim.v.shell_error ~= 0 then
return nil
end
end
toplevel = toplevel:gsub("/", "\\")
end
@@ -38,7 +41,7 @@ function M.should_show_untracked(cwd)
return untracked[cwd]
end
local cmd = "git -C " .. cwd .. " config --type=bool status.showUntrackedFiles"
local cmd = "git -C " .. cwd .. " config status.showUntrackedFiles"
local ps = log.profile_start("git untracked %s", cwd)
log.line("git", cmd)
@@ -48,7 +51,7 @@ function M.should_show_untracked(cwd)
log.raw("git", has_untracked)
log.profile_end(ps, "git untracked %s", cwd)
untracked[cwd] = vim.trim(has_untracked) ~= "false"
untracked[cwd] = vim.trim(has_untracked) ~= "no"
return untracked[cwd]
end

View File

@@ -238,7 +238,7 @@ local DEFAULT_KEYMAPS = {
},
},
{
key = "[e",
key = "]e",
callback = Api.node.navigate.diagnostics.next,
desc = {
long = "Go to next diagnostic item.",
@@ -246,7 +246,7 @@ local DEFAULT_KEYMAPS = {
},
},
{
key = "[c",
key = "]c",
callback = Api.node.navigate.git.next,
desc = {
long = "Go to next git item.",
@@ -254,7 +254,7 @@ local DEFAULT_KEYMAPS = {
},
},
{
key = "]e",
key = "[e",
callback = Api.node.navigate.diagnostics.prev,
desc = {
long = "Go to prev diagnostic item.",
@@ -262,7 +262,7 @@ local DEFAULT_KEYMAPS = {
},
},
{
key = "]c",
key = "[c",
callback = Api.node.navigate.git.prev,
desc = {
long = "Go to prev git item.",

View File

@@ -25,6 +25,15 @@ local overlay_bufnr = nil
local overlay_winnr = nil
local function remove_overlay()
if view.View.float.enable then
-- return to normal nvim-tree float behaviour when filter window is closed
a.nvim_create_autocmd("WinLeave", {
pattern = "NvimTree_*",
group = a.nvim_create_augroup("NvimTree", { clear = false }),
callback = view.close,
})
end
a.nvim_win_close(overlay_winnr, { force = true })
overlay_bufnr = nil
overlay_winnr = nil
@@ -92,12 +101,24 @@ local function configure_buffer_overlay()
end
local function create_overlay()
local min_width = 20
if view.View.float.enable then
-- don't close nvim-tree float when focus is changed to filter window
a.nvim_clear_autocmds {
event = "WinLeave",
pattern = "NvimTree_*",
group = a.nvim_create_augroup("NvimTree", { clear = false }),
}
min_width = min_width - 2
end
configure_buffer_overlay()
overlay_winnr = a.nvim_open_win(overlay_bufnr, true, {
col = 1,
row = 0,
relative = "cursor",
width = math.max(20, a.nvim_win_get_width(view.get_winnr()) - #M.prefix - 2),
width = math.max(min_width, a.nvim_win_get_width(view.get_winnr()) - #M.prefix - 2),
height = 1,
border = "none",
style = "minimal",

View File

@@ -17,6 +17,10 @@ local function remove_mark(node)
end
function M.toggle_mark(node)
if node.absolute_path == nil then
return
end
if M.get_mark(node) then
remove_mark(node)
else

View File

@@ -1,4 +1,5 @@
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local git = require "nvim-tree.renderer.components.git"
local pad = require "nvim-tree.renderer.components.padding"
@@ -114,7 +115,8 @@ function Builder:_build_folder(node, padding, git_hl, git_icons_tbl)
local foldername = name .. self.trailing_slash
if node.link_to and self.symlink_destination then
local arrow = icons.i.symlink_arrow
foldername = foldername .. arrow .. node.link_to
local link_to = utils.path_relative(node.link_to, core.get_cwd())
foldername = foldername .. arrow .. link_to
end
local git_icons = self:_unwrap_git_data(git_icons_tbl, offset + #icon + (self.is_git_after and #foldername + 1 or 0))
@@ -160,7 +162,8 @@ function Builder:_build_symlink(node, padding, git_highlight, git_icons_tbl)
local arrow = icons.i.symlink_arrow
local symlink_formatted = node.name
if self.symlink_destination then
symlink_formatted = symlink_formatted .. arrow .. node.link_to
local link_to = utils.path_relative(node.link_to, core.get_cwd())
symlink_formatted = symlink_formatted .. arrow .. link_to
end
local link_highlight = git_highlight or "NvimTreeSymlink"
@@ -258,9 +261,9 @@ function Builder:_build_line(node, idx, num_children)
self.index = self.index + 1
if node.open then
self.depth = self.depth + 2
self.depth = self.depth + 1
self:build(node)
self.depth = self.depth - 2
self.depth = self.depth - 1
end
end

View File

@@ -99,6 +99,7 @@ local git_hl = {
["AD"] = "NvimTreeFileStaged",
["MD"] = "NvimTreeFileStaged",
["T "] = "NvimTreeFileStaged",
["TT"] = "NvimTreeFileStaged",
[" M"] = "NvimTreeFileDirty",
["CM"] = "NvimTreeFileDirty",
[" C"] = "NvimTreeFileDirty",

View File

@@ -25,30 +25,33 @@ local function get_padding_indent_markers(depth, idx, nodes_number, markers, wit
if depth > 0 then
local has_folder_sibling = check_siblings_for_folder(node, with_arrows)
local rdepth = depth / 2
markers[rdepth] = idx ~= nodes_number
for i = 1, rdepth do
local indent = string.rep(" ", M.config.indent_width - 1)
markers[depth] = idx ~= nodes_number
for i = 1, depth do
local glyph
if idx == nodes_number and i == rdepth then
if idx == nodes_number and i == depth then
local bottom_width = M.config.indent_width
- 2
+ (with_arrows and not inline_arrows and has_folder_sibling and 2 or 0)
glyph = M.config.indent_markers.icons.corner
elseif markers[i] and i == rdepth then
glyph = M.config.indent_markers.icons.item
.. string.rep(M.config.indent_markers.icons.bottom, bottom_width)
.. (M.config.indent_width > 1 and " " or "")
elseif markers[i] and i == depth then
glyph = M.config.indent_markers.icons.item .. indent
elseif markers[i] then
glyph = M.config.indent_markers.icons.edge
glyph = M.config.indent_markers.icons.edge .. indent
else
glyph = M.config.indent_markers.icons.none
glyph = M.config.indent_markers.icons.none .. indent
end
if not with_arrows or (inline_arrows and (rdepth ~= i or not node.nodes)) then
padding = padding .. glyph .. " "
if not with_arrows or (inline_arrows and (depth ~= i or not node.nodes)) then
padding = padding .. glyph
elseif inline_arrows then
padding = padding
elseif idx == nodes_number and i == rdepth and has_folder_sibling then
padding = padding .. base_padding .. glyph .. "── "
elseif rdepth == i and not node.nodes and has_folder_sibling then
padding = padding .. base_padding .. glyph .. " " .. base_padding
elseif idx ~= nodes_number and depth == i and not node.nodes and has_folder_sibling then
padding = padding .. base_padding .. glyph .. base_padding
else
padding = padding .. base_padding .. glyph .. " "
padding = padding .. base_padding .. glyph
end
end
end
@@ -71,11 +74,12 @@ function M.get_padding(depth, idx, nodes_number, node, markers)
local show_arrows = M.config.icons.show.folder_arrow
local show_markers = M.config.indent_markers.enable
local inline_arrows = M.config.indent_markers.inline_arrows
local indent_width = M.config.indent_width
if show_markers then
padding = padding .. get_padding_indent_markers(depth, idx, nodes_number, markers, show_arrows, inline_arrows, node)
else
padding = padding .. string.rep(" ", depth)
padding = padding .. string.rep(" ", depth * indent_width)
end
if show_arrows then
@@ -87,6 +91,22 @@ end
function M.setup(opts)
M.config = opts.renderer
if M.config.indent_width < 1 then
M.config.indent_width = 1
end
local function check_marker(symbol)
if #symbol == 0 then
return " "
end
-- return the first character from the UTF-8 encoded string; we may use utf8.codes from Lua 5.3 when available
return symbol:match "[%z\1-\127\194-\244][\128-\191]*"
end
for k, v in pairs(M.config.indent_markers.icons) do
M.config.indent_markers.icons[k] = check_marker(v)
end
end
return M

View File

@@ -10,6 +10,7 @@ local M = {
}
M.is_windows = vim.fn.has "win32" == 1 or vim.fn.has "win32unix" == 1
M.is_wsl = vim.fn.has "wsl" == 1
function M.path_to_matching_str(path)
return path:gsub("(%-)", "(%%-)"):gsub("(%.)", "(%%.)"):gsub("(%_)", "(%%_)")
@@ -171,8 +172,11 @@ end
---@return boolean
function M.is_windows_exe(ext)
if not M.pathexts then
local PATHEXT = vim.env.PATHEXT or ""
local wexe = vim.split(PATHEXT:gsub("%.", ""), ";")
if not vim.env.PATHEXT then
return false
end
local wexe = vim.split(vim.env.PATHEXT:gsub("%.", ""), ";")
M.pathexts = {}
for _, v in pairs(wexe) do
M.pathexts[v] = true
@@ -182,6 +186,44 @@ function M.is_windows_exe(ext)
return M.pathexts[ext:upper()]
end
--- Check whether path maps to Windows filesystem mounted by WSL
-- @param path string
-- @return boolean
function M.is_wsl_windows_fs_path(path)
-- Run 'wslpath' command to try translating WSL path to Windows path.
-- Consume stderr output as well because 'wslpath' can produce permission
-- errors on some files (e.g. temporary files in root of system drive).
local handle = io.popen('wslpath -w "' .. path .. '" 2>/dev/null')
if handle then
local output = handle:read "*a"
handle:close()
return string.find(output, "^\\\\wsl$\\") == nil
end
return false
end
--- Check whether extension is Windows executable under WSL
-- @param ext string
-- @return boolean
function M.is_wsl_windows_fs_exe(ext)
if not vim.env.PATHEXT then
-- Extract executable extensions from within WSL.
-- Redirect stderr to null to silence warnings when
-- Windows command is executed from Linux filesystem:
-- > CMD.EXE was started with the above path as the current directory.
-- > UNC paths are not supported. Defaulting to Windows directory.
local handle = io.popen 'cmd.exe /c "echo %PATHEXT%" 2>/dev/null'
if handle then
vim.env.PATHEXT = handle:read "*a"
handle:close()
end
end
return M.is_windows_exe(ext)
end
function M.rename_loaded_buffers(old_path, new_path)
for _, buf in pairs(a.nvim_list_bufs()) do
if a.nvim_buf_is_loaded(buf) then
@@ -408,6 +450,12 @@ function M.array_remove(array, item)
end
end
function M.array_remove_nils(array)
return vim.tbl_filter(function(v)
return v ~= nil
end, array)
end
function M.inject_node(f)
return function()
f(require("nvim-tree.lib").get_node_at_cursor())

View File

@@ -133,9 +133,21 @@ local function set_window_options_and_buffer()
end
end
local function open_win_config()
if type(M.View.float.open_win_config) == "function" then
return M.View.float.open_win_config()
else
return M.View.float.open_win_config
end
end
local function open_window()
a.nvim_command "vsp"
M.reposition_window()
if M.View.float.enable then
a.nvim_open_win(0, true, open_win_config())
else
a.nvim_command "vsp"
M.reposition_window()
end
setup_tabpage(a.nvim_get_current_tabpage())
set_window_options_and_buffer()
end
@@ -184,11 +196,13 @@ function M.close()
local current_win = a.nvim_get_current_win()
for _, win in pairs(a.nvim_list_wins()) do
if tree_win ~= win and a.nvim_win_get_config(win).relative == "" then
a.nvim_win_close(tree_win, true)
local prev_win = vim.fn.winnr "#" -- this tab only
if tree_win == current_win and prev_win > 0 then
a.nvim_set_current_win(vim.fn.win_getid(prev_win))
end
if a.nvim_win_is_valid(tree_win) then
a.nvim_win_close(tree_win, true)
end
events._dispatch_on_tree_close()
return
end
@@ -232,6 +246,12 @@ function M.grow_from_content()
end
function M.resize(size)
if M.View.float.enable and not M.View.adaptive_size then
-- if the floating windows's adaptive size is not desired, then the
-- float size should be defined in view.float.open_win_config
return
end
if type(size) == "string" then
size = vim.trim(size)
local first_char = size:sub(1, 1)
@@ -431,6 +451,7 @@ function M.setup(opts)
M.View.winopts.number = options.number
M.View.winopts.relativenumber = options.relativenumber
M.View.winopts.signcolumn = options.signcolumn
M.View.float = options.float
M.on_attach = opts.on_attach
end