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

@@ -25,3 +25,9 @@ When adding new options, you should declare the defaults in the main `nvim-tree.
Once you did, you should run the `scripts/update-help.sh`.
Documentation for options should also be added, see how this is done after `nvim-tree.disable_netrw` in the `nvim-tree-lua.txt` file.
## Pull Request
Please reference any issues in the description e.g. "resolves #1234".
Please check "allow edits by maintainers" to allow nvim-tree developers to make small changes such as documentation tweaks.

View File

@@ -174,6 +174,8 @@ Subsequent calls to setup will replace the previous configuration.
sync_root_with_cwd = false,
reload_on_bufenter = false,
respect_buf_cwd = false,
on_attach = "disable", -- function(bufnr). If nil, will use the deprecated mapping strategy
remove_keymaps = false, -- boolean (disable totally or not) or list of key (lhs)
view = {
adaptive_size = false,
centralize_selection = false,
@@ -185,12 +187,24 @@ Subsequent calls to setup will replace the previous configuration.
number = false,
relativenumber = false,
signcolumn = "yes",
-- @deprecated
mappings = {
custom_only = false,
list = {
-- user mappings go here
},
},
float = {
enable = false,
open_win_config = {
relative = "editor",
border = "rounded",
width = 30,
height = 30,
row = 1,
col = 1,
},
},
},
renderer = {
add_trailing = false,
@@ -199,6 +213,7 @@ Subsequent calls to setup will replace the previous configuration.
full_name = false,
highlight_opened_files = "none",
root_folder_modifier = ":~",
indent_width = 2,
indent_markers = {
enable = false,
inline_arrows = true,
@@ -206,6 +221,7 @@ Subsequent calls to setup will replace the previous configuration.
corner = "└",
edge = "│",
item = "│",
bottom = "─",
none = " ",
},
},
@@ -299,6 +315,15 @@ Subsequent calls to setup will replace the previous configuration.
max_folder_discovery = 300,
exclude = {},
},
file_popup = {
open_win_config = {
col = 1,
row = 1,
relative = "cursor",
border = "shadow",
style = "minimal",
},
},
open_file = {
quit_on_open = false,
resize_window = true,
@@ -628,11 +653,33 @@ Window / buffer setup.
Configuration options for |nvim-tree-mappings|
*nvim-tree.view.mappings.custom_only*
DEPRECATED: see |nvim-tree.remove_keymaps|
Will use only the provided user mappings and not the default otherwise,
extends the default mappings with the provided user mappings.
Type: `boolean`, Default: `false`
*nvim-tree.view.mappings.list*
DEPRECATED: see |nvim-tree.on_attach|
A list of keymaps that will extend or override the default keymaps.
Type: `table`
Default: see |nvim-tree-default-mappings|
*nvim-tree.view.float*
Configuration options for floating window
*nvim-tree.view.float.enable*
Display nvim-tree window as float (enforces |nvim-tree.actions.open_file.quit_on_open| if set).
Type: `boolean`, Default: `false`
*nvim-tree.view.float.open_win_config*
Floating window config. See |nvim_open_win| for more details.
Type: `table` or `function` that returns a table, Default:
`{`
`relative = "editor",`
`border = "rounded",`
`width = 30,`
`height = 30,`
`row = 1,`
`col = 1,`
`}`
*nvim-tree.renderer*
UI rendering setup
@@ -664,6 +711,10 @@ UI rendering setup
available options.
Type: `string`, Default: `":~"`
*nvim-tree.renderer.indent_width*
Number of spaces for an each tree nesting level. Minimum 1.
Type: `number`, Default: `2`
*nvim-tree.renderer.indent_markers*
Configuration options for tree indent markers.
@@ -677,8 +728,8 @@ UI rendering setup
Type: `boolean`, Default: `true`
*nvim-tree.renderer.indent_markers.icons*
Icons shown before the file/directory.
Type: `table`, Default: `{ corner = "└", edge = "│", item = "│", none = " ", }`
Icons shown before the file/directory. Length 1.
Type: `table`, Default: `{ corner = "└", edge = "│", item = "│", bottom = "─", none = " ", }`
*nvim-tree.renderer.icons*
Configuration options for icons.
@@ -833,6 +884,22 @@ Configuration for various actions.
E.g `{ ".git", "target", "build" }` etc.
Type: `table`, Default: `{}`
*nvim-tree.actions.file_popup*
Configuration for file_popup behaviour.
*nvim-tree.actions.file_popup.open_win_config*
Floating window config for file_popup. See |nvim_open_win| for more details.
You shouldn't define `"width"` and `"height"` values here. They will be
overridden to fit the file_popup content.
Type: `table`, Default:
`{`
`col = 1,`
`row = 1,`
`relative = "cursor",`
`border = "shadow",`
`style = "minimal",`
`}`
*nvim-tree.actions.open_file*
Configuration options for opening a file from nvim-tree.
@@ -1076,55 +1143,132 @@ exists.
==============================================================================
6. MAPPINGS *nvim-tree-mappings*
Setting your own mapping in the configuration is deprecated, see |nvim-tree.on_attach| now.
Setting your own mapping in the configuration will soon be deprecated, see |nvim-tree.on_attach| for experimental replacement.
You can remove default mappings with |nvim-tree.remove_keymaps|.
The `list` option in `view.mappings.list` is a table of
`<CR>`, `o`, `<2-LeftMouse>` open a file or folder; root will cd to the above directory
`<C-e>` edit the file in place, effectively replacing the tree explorer
`O` same as (edit) with no window picker
`<C-]>`, `<2-RightMouse>` cd in the directory under the cursor
`<C-v>` open the file in a vertical split
`<C-x>` open the file in a horizontal split
`<C-t>` open the file in a new tab
`<` navigate to the previous sibling of current file/directory
`>` navigate to the next sibling of current file/directory
`P` move cursor to the parent directory
`<BS>` close current opened directory or parent
`<Tab>` open the file as a preview (keeps the cursor in the tree)
`K` navigate to the first sibling of current file/directory
`J` navigate to the last sibling of current file/directory
`I` toggle visibility of files/folders hidden via |git.ignore| option
`H` toggle visibility of dotfiles via |filters.dotfiles| option
`U` toggle visibility of files/folders hidden via |filters.custom| option
`R` refresh the tree
`a` add a file; leaving a trailing `/` will add a directory
`d` delete a file (will prompt for confirmation)
`D` trash a file via |trash| option
`r` rename a file
`<C-r>` rename a file and omit the filename on input
`x` add/remove file/directory to cut clipboard
`c` add/remove file/directory to copy clipboard
`p` paste from clipboard; cut clipboard has precedence over copy; will prompt for confirmation
`y` copy name to system clipboard
`Y` copy relative path to system clipboard
`gy` copy absolute path to system clipboard
`[e` go to next diagnostic item
`[c` go to next git item
`]e` go to prev diagnostic item
`]c` go to prev git item
`-` navigate up to the parent directory of the current file/directory
`s` open a file with default system application or a folder with default file manager, using |system_open| option
`f` live filter nodes dynamically based on regex matching.
`F` clear live filter
`q` close tree window
`W` collapse the whole tree
`E` expand the whole tree, stopping after expanding |actions.expand_all.max_folder_discovery| folders; this might hang neovim for a while if running on a big folder
`S` prompt the user to enter a path and then expands the tree to match the path
`.` enter vim command mode with the file the cursor is on
`<C-k>` toggle a popup with file infos about the file under the cursor
`g?` toggle help
`m` Toggle node in bookmarks
- `key` can be either a string or a table of string (lhs)
- `action` is the name of the action, set to `""` to remove default action
- `action_cb` is the function that will be called, it receives the node as a parameter. Optional for default actions
- `mode` is normal by default
>
local tree_cb = require'nvim-tree.config'.nvim_tree_callback
local function print_node_path(node) {
print(node.absolute_path)
}
local list = {
{ key = {"<CR>", "o" }, action = "edit", mode = "n"},
{ key = "p", action = "print_path", action_cb = print_node_path },
{ key = "s", cb = tree_cb("vsplit") }, --tree_cb and the cb property are deprecated
{ key = "<2-RightMouse>", action = "" }, -- will remove default cd action
}
<
Mouse support defined in |KeyBindings|
DEFAULT MAPPINGS *nvim-tree-default-mappings*
`<CR>` edit open a file or folder; root will cd to the above directory
`o`
`<2-LeftMouse>`
`<C-e>` edit_in_place edit the file in place, effectively replacing the tree explorer
`O` edit_no_picker same as (edit) with no window picker
`<C-]>` cd cd in the directory under the cursor
`<2-RightMouse>`
`<C-v>` vsplit open the file in a vertical split
`<C-x>` split open the file in a horizontal split
`<C-t>` tabnew open the file in a new tab
`<` prev_sibling navigate to the previous sibling of current file/directory
`>` next_sibling navigate to the next sibling of current file/directory
`P` parent_node move cursor to the parent directory
`<BS>` close_node close current opened directory or parent
`<Tab>` preview open the file as a preview (keeps the cursor in the tree)
`K` first_sibling navigate to the first sibling of current file/directory
`J` last_sibling navigate to the last sibling of current file/directory
`I` toggle_git_ignored toggle visibility of files/folders hidden via |git.ignore| option
`H` toggle_dotfiles toggle visibility of dotfiles via |filters.dotfiles| option
`U` toggle_custom toggle visibility of files/folders hidden via |filters.custom| option
`R` refresh refresh the tree
`a` create add a file; leaving a trailing `/` will add a directory
`d` remove delete a file (will prompt for confirmation)
`D` trash trash a file via |trash| option
`r` rename rename a file
`<C-r>` full_rename rename a file and omit the filename on input
`x` cut add/remove file/directory to cut clipboard
`c` copy add/remove file/directory to copy clipboard
`p` paste paste from clipboard; cut clipboard has precedence over copy; will prompt for confirmation
`y` copy_name copy name to system clipboard
`Y` copy_path copy relative path to system clipboard
`gy` copy_absolute_path copy absolute path to system clipboard
`[e` prev_diag_item go to next diagnostic item
`[c` prev_git_item go to next git item
`]e` next_diag_item go to prev diagnostic item
`]c` next_git_item go to prev git item
`-` dir_up navigate up to the parent directory of the current file/directory
`s` system_open open a file with default system application or a folder with default file manager, using |system_open| option
`f` live_filter live filter nodes dynamically based on regex matching.
`F` clear_live_filter clear live filter
`q` close close tree window
`W` collapse_all collapse the whole tree
`E` expand_all expand the whole tree, stopping after expanding |actions.expand_all.max_folder_discovery| folders; this might hang neovim for a while if running on a big folder
`S` search_node prompt the user to enter a path and then expands the tree to match the path
`.` run_file_command enter vim command mode with the file the cursor is on
`<C-k>` toggle_file_info toggle a popup with file infos about the file under the cursor
`g?` toggle_help toggle help
`m` toggle_mark Toggle node in bookmarks
`bmv` bulk_move Move all bookmarked nodes into specified location
>
view.mappings.list = { -- BEGIN_DEFAULT_MAPPINGS
{ key = { "<CR>", "o", "<2-LeftMouse>" }, action = "edit" },
{ key = "<C-e>", action = "edit_in_place" },
{ key = "O", action = "edit_no_picker" },
{ key = { "<C-]>", "<2-RightMouse>" }, action = "cd" },
{ key = "<C-v>", action = "vsplit" },
{ key = "<C-x>", action = "split" },
{ key = "<C-t>", action = "tabnew" },
{ key = "<", action = "prev_sibling" },
{ key = ">", action = "next_sibling" },
{ key = "P", action = "parent_node" },
{ key = "<BS>", action = "close_node" },
{ key = "<Tab>", action = "preview" },
{ key = "K", action = "first_sibling" },
{ key = "J", action = "last_sibling" },
{ key = "I", action = "toggle_git_ignored" },
{ key = "H", action = "toggle_dotfiles" },
{ key = "U", action = "toggle_custom" },
{ key = "R", action = "refresh" },
{ key = "a", action = "create" },
{ key = "d", action = "remove" },
{ key = "D", action = "trash" },
{ key = "r", action = "rename" },
{ key = "<C-r>", action = "full_rename" },
{ key = "x", action = "cut" },
{ key = "c", action = "copy" },
{ key = "p", action = "paste" },
{ key = "y", action = "copy_name" },
{ key = "Y", action = "copy_path" },
{ key = "gy", action = "copy_absolute_path" },
{ key = "[e", action = "prev_diag_item" },
{ key = "[c", action = "prev_git_item" },
{ key = "]e", action = "next_diag_item" },
{ key = "]c", action = "next_git_item" },
{ key = "-", action = "dir_up" },
{ key = "s", action = "system_open" },
{ key = "f", action = "live_filter" },
{ key = "F", action = "clear_live_filter" },
{ key = "q", action = "close" },
{ key = "W", action = "collapse_all" },
{ key = "E", action = "expand_all" },
{ key = "S", action = "search_node" },
{ key = ".", action = "run_file_command" },
{ key = "<C-k>", action = "toggle_file_info" },
{ key = "g?", action = "toggle_help" },
{ key = "m", action = "toggle_mark" },
{ key = "bmv", action = "bulk_move" },
} -- END_DEFAULT_MAPPINGS
<
==============================================================================
7. HIGHLIGHT GROUPS *nvim-tree-highlight*

View File

@@ -414,6 +414,10 @@ local function setup_autocommands(opts)
end,
})
end
if opts.view.float.enable then
create_nvim_tree_autocmd("WinLeave", { pattern = "NvimTree_*", callback = view.close })
end
end
local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
@@ -447,13 +451,23 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
number = false,
relativenumber = false,
signcolumn = "yes",
-- @deprecated
mappings = {
custom_only = false,
list = {
-- user mappings go here
},
},
float = {
enable = false,
open_win_config = {
relative = "editor",
border = "rounded",
width = 30,
height = 30,
row = 1,
col = 1,
},
},
},
renderer = {
add_trailing = false,
@@ -462,6 +476,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
full_name = false,
highlight_opened_files = "none",
root_folder_modifier = ":~",
indent_width = 2,
indent_markers = {
enable = false,
inline_arrows = true,
@@ -469,6 +484,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
corner = "",
edge = "",
item = "",
bottom = "",
none = " ",
},
},
@@ -562,6 +578,15 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
max_folder_discovery = 300,
exclude = {},
},
file_popup = {
open_win_config = {
col = 1,
row = 1,
relative = "cursor",
border = "shadow",
style = "minimal",
},
},
open_file = {
quit_on_open = false,
resize_window = true,
@@ -606,6 +631,10 @@ local function merge_options(conf)
return vim.tbl_deep_extend("force", DEFAULT_OPTS, conf or {})
end
local FIELD_SKIP_VALIDATE = {
open_win_config = true,
}
local FIELD_OVERRIDE_TYPECHECK = {
width = { string = true, ["function"] = true, number = true },
height = { string = true, ["function"] = true, number = true },
@@ -623,25 +652,27 @@ local function validate_options(conf)
end
for k, v in pairs(user) do
local invalid
local override_typecheck = FIELD_OVERRIDE_TYPECHECK[k] or {}
if def[k] == nil then
-- option does not exist
invalid = string.format("unknown option: %s%s", prefix, k)
elseif type(v) ~= type(def[k]) and not override_typecheck[type(v)] then
-- option is of the wrong type and is not a function
invalid = string.format("invalid option: %s%s expected: %s actual: %s", prefix, k, type(def[k]), type(v))
end
if invalid then
if msg then
msg = string.format("%s | %s", msg, invalid)
else
msg = string.format("%s", invalid)
if not FIELD_SKIP_VALIDATE[k] then
local invalid
local override_typecheck = FIELD_OVERRIDE_TYPECHECK[k] or {}
if def[k] == nil then
-- option does not exist
invalid = string.format("unknown option: %s%s", prefix, k)
elseif type(v) ~= type(def[k]) and not override_typecheck[type(v)] then
-- option is of the wrong type and is not a function
invalid = string.format("invalid option: %s%s expected: %s actual: %s", prefix, k, type(def[k]), type(v))
end
if invalid then
if msg then
msg = string.format("%s | %s", msg, invalid)
else
msg = string.format("%s", invalid)
end
user[k] = nil
else
validate(v, def[k], prefix .. k .. ".")
end
user[k] = nil
else
validate(v, def[k], prefix .. k .. ".")
end
end
end

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