feat/chore: rewrite git with job and some other fixes (#743)
* feat/chore: rewrite git with job and some other fixes * fix: fs clear window, rename echo_warning -> warn also fix renaming and add an event blocker to avoid running many events at the same time
This commit is contained in:
parent
b853e1083c
commit
6662b60a2b
10
README.md
10
README.md
@ -23,7 +23,9 @@ Install with [packer](https://github.com/wbthomason/packer.nvim):
|
|||||||
```lua
|
```lua
|
||||||
use {
|
use {
|
||||||
'kyazdani42/nvim-tree.lua',
|
'kyazdani42/nvim-tree.lua',
|
||||||
requires = 'kyazdani42/nvim-web-devicons',
|
requires = {
|
||||||
|
'kyazdani42/nvim-web-devicons', -- optional, for file icon
|
||||||
|
},
|
||||||
config = function() require'nvim-tree'.setup {} end
|
config = function() require'nvim-tree'.setup {} end
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -72,6 +74,11 @@ require'nvim-tree'.setup {
|
|||||||
dotfiles = false,
|
dotfiles = false,
|
||||||
custom = {}
|
custom = {}
|
||||||
},
|
},
|
||||||
|
git = {
|
||||||
|
enable = true,
|
||||||
|
ignore = true,
|
||||||
|
timeout = 500,
|
||||||
|
},
|
||||||
view = {
|
view = {
|
||||||
width = 30,
|
width = 30,
|
||||||
height = 30,
|
height = 30,
|
||||||
@ -89,7 +96,6 @@ require'nvim-tree'.setup {
|
|||||||
These additional options must be set **BEFORE** calling `require'nvim-tree'` or calling setup.
|
These additional options must be set **BEFORE** calling `require'nvim-tree'` or calling setup.
|
||||||
They are being migrated to the setup function bit by bit, check [this issue](https://github.com/kyazdani42/nvim-tree.lua/issues/674) if you encounter any problems related to configs not working after update.
|
They are being migrated to the setup function bit by bit, check [this issue](https://github.com/kyazdani42/nvim-tree.lua/issues/674) if you encounter any problems related to configs not working after update.
|
||||||
```vim
|
```vim
|
||||||
let g:nvim_tree_gitignore = 1 "0 by default
|
|
||||||
let g:nvim_tree_quit_on_open = 1 "0 by default, closes the tree when you open a file
|
let g:nvim_tree_quit_on_open = 1 "0 by default, closes the tree when you open a file
|
||||||
let g:nvim_tree_indent_markers = 1 "0 by default, this option shows indent markers when folders are open
|
let g:nvim_tree_indent_markers = 1 "0 by default, this option shows indent markers when folders are open
|
||||||
let g:nvim_tree_git_hl = 1 "0 by default, will enable file highlight for git attributes (can be used without the icons).
|
let g:nvim_tree_git_hl = 1 "0 by default, will enable file highlight for git attributes (can be used without the icons).
|
||||||
|
|||||||
@ -101,6 +101,10 @@ function.
|
|||||||
cmd = nil,
|
cmd = nil,
|
||||||
args = {}
|
args = {}
|
||||||
},
|
},
|
||||||
|
git = {
|
||||||
|
enable = true,
|
||||||
|
ignore = true,
|
||||||
|
},
|
||||||
view = {
|
view = {
|
||||||
width = 30,
|
width = 30,
|
||||||
height = 30,
|
height = 30,
|
||||||
@ -231,6 +235,31 @@ Here is a list of the options available in the setup call:
|
|||||||
- `NvimTreeLspDiagnosticsInformation`
|
- `NvimTreeLspDiagnosticsInformation`
|
||||||
- `NvimTreeLspDiagnosticsHint`
|
- `NvimTreeLspDiagnosticsHint`
|
||||||
|
|
||||||
|
*nvim-tree.git*
|
||||||
|
- |git|: git integration with icons and colors
|
||||||
|
|
||||||
|
- |git.enable|: enable / disable the feature
|
||||||
|
type: `boolean`
|
||||||
|
default: `true`
|
||||||
|
|
||||||
|
- |git.ignore|: ignore files based on `.gitignore`.
|
||||||
|
will add `ignored=matching` to the integration when `true`. Otherwise will
|
||||||
|
add `ignored=no` to the integration which can lead to better performance.
|
||||||
|
|
||||||
|
- |git.timeout|: kills the git process after some time if it takes too long
|
||||||
|
type: `number`
|
||||||
|
default: `400` (ms)
|
||||||
|
|
||||||
|
You will still need to configure `g:nvim_tree_show_icons.git` or
|
||||||
|
`g:nvim_tree_git_hl` to be able to see things in the tree. This will be
|
||||||
|
changed in the future versions.
|
||||||
|
|
||||||
|
The configurable timeout will kill the current process and so disable the
|
||||||
|
git integration for the project that takes too long.
|
||||||
|
The git integration is blocking, so if your timeout is too long (like not in
|
||||||
|
milliseconds but a few seconds), it will not render anything until the git
|
||||||
|
process returned the data.
|
||||||
|
|
||||||
*nvim-tree.view*
|
*nvim-tree.view*
|
||||||
- |view|: window / buffer setup
|
- |view|: window / buffer setup
|
||||||
|
|
||||||
@ -296,16 +325,6 @@ width of the window, can be *width_in_columns* or *'width_in_percent%'*
|
|||||||
where the window will open (default to 'left')
|
where the window will open (default to 'left')
|
||||||
- 'left' or 'right'
|
- 'left' or 'right'
|
||||||
|
|
||||||
|g:nvim_tree_gitignore| *g:nvim_tree_gitignore*
|
|
||||||
|
|
||||||
Determines whether to include in g:nvim_tree_ignore
|
|
||||||
files ignored by git.
|
|
||||||
|
|
||||||
Must be:
|
|
||||||
0: not ignored
|
|
||||||
1: ignored files from `git ls-files --others --ignored --exclude-standard --directory`
|
|
||||||
|
|
||||||
>
|
|
||||||
|g:nvim_tree_show_icons| *g:nvim_tree_show_icons*
|
|g:nvim_tree_show_icons| *g:nvim_tree_show_icons*
|
||||||
|
|
||||||
Dictionary, if your terminal or font doesn't support certain unicode
|
Dictionary, if your terminal or font doesn't support certain unicode
|
||||||
|
|||||||
@ -114,7 +114,7 @@ local keypress_funcs = {
|
|||||||
elseif _config.is_unix then
|
elseif _config.is_unix then
|
||||||
_config.system_open.cmd = 'xdg-open'
|
_config.system_open.cmd = 'xdg-open'
|
||||||
else
|
else
|
||||||
require'nvim-tree.utils'.echo_warning("Cannot open file with system application. Unrecognized platform.")
|
require'nvim-tree.utils'.warn("Cannot open file with system application. Unrecognized platform.")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -173,16 +173,12 @@ function M.on_keypress(mode)
|
|||||||
if node.link_to and not node.entries then
|
if node.link_to and not node.entries then
|
||||||
lib.open_file(mode, node.link_to)
|
lib.open_file(mode, node.link_to)
|
||||||
elseif node.entries ~= nil then
|
elseif node.entries ~= nil then
|
||||||
lib.unroll_dir(node)
|
lib.expand_or_collapse(node)
|
||||||
else
|
else
|
||||||
lib.open_file(mode, node.absolute_path)
|
lib.open_file(mode, node.absolute_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.refresh()
|
|
||||||
lib.refresh_tree()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.print_clipboard()
|
function M.print_clipboard()
|
||||||
fs.print_clipboard()
|
fs.print_clipboard()
|
||||||
end
|
end
|
||||||
@ -227,7 +223,7 @@ function M.on_enter(opts)
|
|||||||
M.hijack_current_window()
|
M.hijack_current_window()
|
||||||
end
|
end
|
||||||
|
|
||||||
lib.init(should_open, should_open)
|
lib.init(should_open)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_file_readable(fname)
|
local function is_file_readable(fname)
|
||||||
@ -242,7 +238,7 @@ local function update_base_dir_with_filepath(filepath, bufnr)
|
|||||||
|
|
||||||
local ft = api.nvim_buf_get_option(bufnr, 'filetype') or ""
|
local ft = api.nvim_buf_get_option(bufnr, 'filetype') or ""
|
||||||
for _, value in pairs(_config.update_focused_file.ignore_list) do
|
for _, value in pairs(_config.update_focused_file.ignore_list) do
|
||||||
if vim.fn.stridx(filepath, value) ~= -1 or vim.fn.stridx(ft, value) ~= -1 then
|
if utils.str_find(filepath, value) or utils.str_find(ft, value) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -359,7 +355,7 @@ local function setup_vim_commands()
|
|||||||
command! NvimTreeClose lua require'nvim-tree'.close()
|
command! NvimTreeClose lua require'nvim-tree'.close()
|
||||||
command! NvimTreeToggle lua require'nvim-tree'.toggle(false)
|
command! NvimTreeToggle lua require'nvim-tree'.toggle(false)
|
||||||
command! NvimTreeFocus lua require'nvim-tree'.focus()
|
command! NvimTreeFocus lua require'nvim-tree'.focus()
|
||||||
command! NvimTreeRefresh lua require'nvim-tree'.refresh()
|
command! NvimTreeRefresh lua require'nvim-tree.lib'.refresh_tree()
|
||||||
command! NvimTreeClipboard lua require'nvim-tree'.print_clipboard()
|
command! NvimTreeClipboard lua require'nvim-tree'.print_clipboard()
|
||||||
command! NvimTreeFindFile lua require'nvim-tree'.find_file(true)
|
command! NvimTreeFindFile lua require'nvim-tree'.find_file(true)
|
||||||
command! NvimTreeFindFileToggle lua require'nvim-tree'.toggle(true)
|
command! NvimTreeFindFileToggle lua require'nvim-tree'.toggle(true)
|
||||||
@ -381,8 +377,8 @@ local function setup_autocommands(opts)
|
|||||||
""" reset highlights when colorscheme is changed
|
""" reset highlights when colorscheme is changed
|
||||||
au ColorScheme * lua require'nvim-tree'.reset_highlight()
|
au ColorScheme * lua require'nvim-tree'.reset_highlight()
|
||||||
|
|
||||||
au BufWritePost * lua require'nvim-tree'.refresh()
|
au BufWritePost * lua require'nvim-tree.lib'.refresh_tree()
|
||||||
au User FugitiveChanged,NeogitStatusRefreshed lua require'nvim-tree'.refresh()
|
au User FugitiveChanged,NeogitStatusRefreshed lua require'nvim-tree.lib'.reload_git()
|
||||||
]]
|
]]
|
||||||
|
|
||||||
if opts.auto_close then
|
if opts.auto_close then
|
||||||
@ -400,6 +396,7 @@ local function setup_autocommands(opts)
|
|||||||
if opts.update_focused_file.enable then
|
if opts.update_focused_file.enable then
|
||||||
vim.cmd "au BufEnter * lua require'nvim-tree'.find_file(false)"
|
vim.cmd "au BufEnter * lua require'nvim-tree'.find_file(false)"
|
||||||
end
|
end
|
||||||
|
vim.cmd "au BufUnload NvimTree lua require'nvim-tree.view'.View.tabpages = {}"
|
||||||
|
|
||||||
vim.cmd "augroup end"
|
vim.cmd "augroup end"
|
||||||
end
|
end
|
||||||
@ -439,6 +436,11 @@ local DEFAULT_OPTS = {
|
|||||||
filters = {
|
filters = {
|
||||||
dotfiles = false,
|
dotfiles = false,
|
||||||
custom_filter = {}
|
custom_filter = {}
|
||||||
|
},
|
||||||
|
git = {
|
||||||
|
enable = true,
|
||||||
|
ignore = true,
|
||||||
|
timeout = 400,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,7 +454,7 @@ function M.setup(conf)
|
|||||||
_config.open_on_setup = opts.open_on_setup
|
_config.open_on_setup = opts.open_on_setup
|
||||||
_config.ignore_ft_on_setup = opts.ignore_ft_on_setup
|
_config.ignore_ft_on_setup = opts.ignore_ft_on_setup
|
||||||
if type(opts.update_to_buf_dir) == "boolean" then
|
if type(opts.update_to_buf_dir) == "boolean" then
|
||||||
utils.echo_warning("update_to_buf_dir is now a table, see :help nvim-tree.update_to_buf_dir")
|
utils.warn("update_to_buf_dir is now a table, see :help nvim-tree.update_to_buf_dir")
|
||||||
_config.update_to_buf_dir = {
|
_config.update_to_buf_dir = {
|
||||||
enable = opts.update_to_buf_dir,
|
enable = opts.update_to_buf_dir,
|
||||||
auto_open = opts.update_to_buf_dir,
|
auto_open = opts.update_to_buf_dir,
|
||||||
@ -462,13 +464,14 @@ function M.setup(conf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if opts.lsp_diagnostics ~= nil then
|
if opts.lsp_diagnostics ~= nil then
|
||||||
utils.echo_warning("setup.lsp_diagnostics has been removed, see :help nvim-tree.diagnostics")
|
utils.warn("setup.lsp_diagnostics has been removed, see :help nvim-tree.diagnostics")
|
||||||
end
|
end
|
||||||
|
|
||||||
require'nvim-tree.colors'.setup()
|
require'nvim-tree.colors'.setup()
|
||||||
require'nvim-tree.view'.setup(opts.view or {})
|
require'nvim-tree.view'.setup(opts.view or {})
|
||||||
require'nvim-tree.diagnostics'.setup(opts)
|
require'nvim-tree.diagnostics'.setup(opts)
|
||||||
require'nvim-tree.populate'.setup(opts)
|
require'nvim-tree.populate'.setup(opts)
|
||||||
|
require'nvim-tree.git'.setup(opts)
|
||||||
|
|
||||||
setup_autocommands(opts)
|
setup_autocommands(opts)
|
||||||
setup_vim_commands()
|
setup_vim_commands()
|
||||||
|
|||||||
@ -58,12 +58,6 @@ function M.get_icon_state()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.use_git()
|
|
||||||
return M.get_icon_state().show_git_icon
|
|
||||||
or vim.g.nvim_tree_git_hl == 1
|
|
||||||
or vim.g.nvim_tree_gitignore == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.nvim_tree_callback(callback_name)
|
function M.nvim_tree_callback(callback_name)
|
||||||
return string.format(":lua require'nvim-tree'.on_keypress('%s')<CR>", callback_name)
|
return string.format(":lua require'nvim-tree'.on_keypress('%s')<CR>", callback_name)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -34,7 +34,7 @@ local function create_file(file)
|
|||||||
else
|
else
|
||||||
luv.fs_close(fd)
|
luv.fs_close(fd)
|
||||||
events._dispatch_file_created(file)
|
events._dispatch_file_created(file)
|
||||||
lib.refresh_tree(true)
|
lib.refresh_tree()
|
||||||
focus_file(file)
|
focus_file(file)
|
||||||
end
|
end
|
||||||
end))
|
end))
|
||||||
@ -98,7 +98,7 @@ function M.create(node)
|
|||||||
end
|
end
|
||||||
api.nvim_out_write(ans..' was properly created\n')
|
api.nvim_out_write(ans..' was properly created\n')
|
||||||
events._dispatch_folder_created(ans)
|
events._dispatch_folder_created(ans)
|
||||||
lib.refresh_tree(true)
|
lib.refresh_tree()
|
||||||
focus_file(ans)
|
focus_file(ans)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -113,6 +113,9 @@ local function clear_buffer(absolute_path)
|
|||||||
api.nvim_set_current_win(winnr)
|
api.nvim_set_current_win(winnr)
|
||||||
end
|
end
|
||||||
vim.api.nvim_buf_delete(buf.bufnr, {})
|
vim.api.nvim_buf_delete(buf.bufnr, {})
|
||||||
|
if buf.windows[1] then
|
||||||
|
vim.api.nvim_win_close(buf.windows[1], true)
|
||||||
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -239,7 +242,7 @@ local function do_paste(node, action_type, action_fn)
|
|||||||
end
|
end
|
||||||
|
|
||||||
clipboard[action_type] = {}
|
clipboard[action_type] = {}
|
||||||
return lib.refresh_tree(true)
|
return lib.refresh_tree()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function add_to_clipboard(node, clip)
|
local function add_to_clipboard(node, clip)
|
||||||
@ -276,7 +279,7 @@ function M.remove(node)
|
|||||||
events._dispatch_file_removed(node.absolute_path)
|
events._dispatch_file_removed(node.absolute_path)
|
||||||
clear_buffer(node.absolute_path)
|
clear_buffer(node.absolute_path)
|
||||||
end
|
end
|
||||||
lib.refresh_tree(true)
|
lib.refresh_tree()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -289,7 +292,13 @@ function M.rename(with_sub)
|
|||||||
local abs_path = with_sub and node.absolute_path:sub(0, namelen * (-1) -1) or node.absolute_path
|
local abs_path = with_sub and node.absolute_path:sub(0, namelen * (-1) -1) or node.absolute_path
|
||||||
local new_name = vim.fn.input("Rename " ..node.name.. " to ", abs_path)
|
local new_name = vim.fn.input("Rename " ..node.name.. " to ", abs_path)
|
||||||
utils.clear_prompt()
|
utils.clear_prompt()
|
||||||
if not new_name or #new_name == 0 then return end
|
if not new_name or #new_name == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if luv.fs_access(new_name, 'R') then
|
||||||
|
utils.warn("Cannot rename: file already exists")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local success = luv.fs_rename(node.absolute_path, new_name)
|
local success = luv.fs_rename(node.absolute_path, new_name)
|
||||||
if not success then
|
if not success then
|
||||||
@ -298,7 +307,7 @@ function M.rename(with_sub)
|
|||||||
api.nvim_out_write(node.absolute_path..' ➜ '..new_name..'\n')
|
api.nvim_out_write(node.absolute_path..' ➜ '..new_name..'\n')
|
||||||
rename_loaded_buffers(node.absolute_path, new_name)
|
rename_loaded_buffers(node.absolute_path, new_name)
|
||||||
events._dispatch_node_renamed(abs_path, new_name)
|
events._dispatch_node_renamed(abs_path, new_name)
|
||||||
lib.refresh_tree(true)
|
lib.refresh_tree()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,168 +0,0 @@
|
|||||||
local utils = require'nvim-tree.utils'
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
local roots = {}
|
|
||||||
|
|
||||||
---A map from git roots to a list of ignored paths
|
|
||||||
local gitignore_map = {}
|
|
||||||
|
|
||||||
local not_git = 'not a git repo'
|
|
||||||
local is_win = vim.api.nvim_call_function("has", {"win32"}) == 1
|
|
||||||
|
|
||||||
local function update_root_status(root)
|
|
||||||
local e_root = vim.fn.shellescape(root)
|
|
||||||
local untracked = ' -u'
|
|
||||||
|
|
||||||
local cmd = "git -C " .. e_root .. " config --type=bool status.showUntrackedFiles"
|
|
||||||
if vim.trim(vim.fn.system(cmd)) == 'false' then
|
|
||||||
untracked = ''
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd = "git -C " .. e_root .. " status --porcelain=v1 --ignored=matching" .. untracked
|
|
||||||
local status = vim.fn.systemlist(cmd)
|
|
||||||
|
|
||||||
roots[root] = {}
|
|
||||||
gitignore_map[root] = {}
|
|
||||||
|
|
||||||
for _, v in pairs(status) do
|
|
||||||
local head = v:sub(0, 2)
|
|
||||||
local body = v:sub(4, -1)
|
|
||||||
if body:match('%->') ~= nil then
|
|
||||||
body = body:gsub('^.* %-> ', '')
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Git returns paths with a forward slash wherever you run it, thats why i have to replace it only on windows
|
|
||||||
if is_win then
|
|
||||||
body = body:gsub("/", "\\")
|
|
||||||
end
|
|
||||||
|
|
||||||
roots[root][body] = head
|
|
||||||
|
|
||||||
if head == "!!" then
|
|
||||||
gitignore_map[root][utils.path_remove_trailing(utils.path_join({root, body}))] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.reload_roots()
|
|
||||||
for root, status in pairs(roots) do
|
|
||||||
if status ~= not_git then
|
|
||||||
update_root_status(root)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_git_root(path)
|
|
||||||
if roots[path] then
|
|
||||||
return path, roots[path]
|
|
||||||
end
|
|
||||||
|
|
||||||
for name, status in pairs(roots) do
|
|
||||||
if status ~= not_git then
|
|
||||||
if path:match(utils.path_to_matching_str(name)) then
|
|
||||||
return name, status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_root(cwd)
|
|
||||||
local cmd = "git -C " .. vim.fn.shellescape(cwd) .. " rev-parse --show-toplevel"
|
|
||||||
local git_root = vim.fn.system(cmd)
|
|
||||||
|
|
||||||
if not git_root or #git_root == 0 or git_root:match('fatal') then
|
|
||||||
roots[cwd] = not_git
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_win then
|
|
||||||
git_root = git_root:gsub("/", "\\")
|
|
||||||
end
|
|
||||||
|
|
||||||
update_root_status(git_root:sub(0, -2))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
---Get the root of the git dir containing the given path or `nil` if it's not a
|
|
||||||
---git dir.
|
|
||||||
---@param path string
|
|
||||||
---@return string|nil
|
|
||||||
function M.git_root(path)
|
|
||||||
local git_root, git_status = get_git_root(path)
|
|
||||||
if not git_root then
|
|
||||||
if not create_root(path) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
git_root, git_status = get_git_root(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
if git_status == not_git then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
return git_root
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.update_status(entries, cwd, parent_node, with_redraw)
|
|
||||||
local git_root, git_status = get_git_root(cwd)
|
|
||||||
if not git_root then
|
|
||||||
if not create_root(cwd) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
git_root, git_status = get_git_root(cwd)
|
|
||||||
elseif git_status == not_git then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not git_root then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not parent_node then parent_node = {} end
|
|
||||||
|
|
||||||
local matching_cwd = utils.path_to_matching_str( utils.path_add_trailing(git_root) )
|
|
||||||
|
|
||||||
for _, node in pairs(entries) do
|
|
||||||
if parent_node.git_status == "!!" then
|
|
||||||
node.git_status = "!!"
|
|
||||||
else
|
|
||||||
local relpath = node.absolute_path:gsub(matching_cwd, '')
|
|
||||||
if node.entries ~= nil then
|
|
||||||
relpath = utils.path_add_trailing(relpath)
|
|
||||||
node.git_status = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local status = git_status[relpath]
|
|
||||||
if status then
|
|
||||||
node.git_status = status
|
|
||||||
elseif node.entries ~= nil then
|
|
||||||
local matcher = '^'..utils.path_to_matching_str(relpath)
|
|
||||||
for key, entry_status in pairs(git_status) do
|
|
||||||
if entry_status ~= "!!" and key:match(matcher) then
|
|
||||||
node.git_status = entry_status
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
node.git_status = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if with_redraw then
|
|
||||||
require'nvim-tree.lib'.redraw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---Check if the given path is ignored by git.
|
|
||||||
---@param path string Absolute path
|
|
||||||
---@return boolean
|
|
||||||
function M.should_gitignore(path)
|
|
||||||
for _, paths in pairs(gitignore_map) do
|
|
||||||
if paths[path] == true then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
86
lua/nvim-tree/git/init.lua
Normal file
86
lua/nvim-tree/git/init.lua
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
local git_utils = require'nvim-tree.git.utils'
|
||||||
|
local Runner = require'nvim-tree.git.runner'
|
||||||
|
|
||||||
|
local M = {
|
||||||
|
config = nil,
|
||||||
|
projects = {},
|
||||||
|
cwd_to_project_root = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function M.reload(callback)
|
||||||
|
local num_projects = vim.tbl_count(M.projects)
|
||||||
|
if not M.config.enable or num_projects == 0 then
|
||||||
|
return callback({})
|
||||||
|
end
|
||||||
|
|
||||||
|
local done = 0
|
||||||
|
for project_root in pairs(M.projects) do
|
||||||
|
M.projects[project_root] = {}
|
||||||
|
Runner.run {
|
||||||
|
project_root = project_root,
|
||||||
|
list_untracked = git_utils.should_show_untracked(project_root),
|
||||||
|
list_ignored = M.config.ignore,
|
||||||
|
timeout = M.config.timeout,
|
||||||
|
on_end = function(git_status)
|
||||||
|
M.projects[project_root] = {
|
||||||
|
files = git_status,
|
||||||
|
dirs = git_utils.file_status_to_dir_status(git_status, project_root)
|
||||||
|
}
|
||||||
|
done = done + 1
|
||||||
|
if done == num_projects then
|
||||||
|
callback(M.projects)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get_project_root(cwd)
|
||||||
|
if M.cwd_to_project_root[cwd] then
|
||||||
|
return M.cwd_to_project_root[cwd]
|
||||||
|
end
|
||||||
|
|
||||||
|
if M.cwd_to_project_root[cwd] == false then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local project_root = git_utils.get_toplevel(cwd)
|
||||||
|
return project_root
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.load_project_status(cwd, callback)
|
||||||
|
if not M.config.enable then
|
||||||
|
return callback({})
|
||||||
|
end
|
||||||
|
|
||||||
|
local project_root = M.get_project_root(cwd)
|
||||||
|
if not project_root then
|
||||||
|
M.cwd_to_project_root[cwd] = false
|
||||||
|
return callback({})
|
||||||
|
end
|
||||||
|
|
||||||
|
local status = M.projects[project_root]
|
||||||
|
if status then
|
||||||
|
return callback(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
Runner.run {
|
||||||
|
project_root = project_root,
|
||||||
|
list_untracked = git_utils.should_show_untracked(project_root),
|
||||||
|
list_ignored = M.config.ignore,
|
||||||
|
timeout = M.config.timeout,
|
||||||
|
on_end = function(git_status)
|
||||||
|
M.projects[project_root] = {
|
||||||
|
files = git_status,
|
||||||
|
dirs = git_utils.file_status_to_dir_status(git_status, project_root)
|
||||||
|
}
|
||||||
|
callback(M.projects[project_root])
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup(opts)
|
||||||
|
M.config = opts.git
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
99
lua/nvim-tree/git/runner.lua
Normal file
99
lua/nvim-tree/git/runner.lua
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
local uv = vim.loop
|
||||||
|
local utils = require'nvim-tree.utils'
|
||||||
|
|
||||||
|
local Runner = {}
|
||||||
|
Runner.__index = Runner
|
||||||
|
|
||||||
|
function Runner:_parse_status_output(line)
|
||||||
|
local status = line:sub(1, 2)
|
||||||
|
-- removing `"` when git is returning special file status containing spaces
|
||||||
|
local path = line:sub(4, -2):gsub('^"', ''):gsub('"$', '')
|
||||||
|
if #status > 0 and #path > 0 then
|
||||||
|
self.output[utils.path_remove_trailing(utils.path_join({self.project_root,path}))] = status
|
||||||
|
end
|
||||||
|
return #line
|
||||||
|
end
|
||||||
|
|
||||||
|
function Runner:_handle_incoming_data(prev_output, incoming)
|
||||||
|
if incoming and utils.str_find(incoming, '\n') then
|
||||||
|
local prev = prev_output..incoming
|
||||||
|
local i = 1
|
||||||
|
for line in prev:gmatch('[^\n]*\n') do
|
||||||
|
i = i + self:_parse_status_output(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
return prev:sub(i, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if incoming then
|
||||||
|
return prev_output..incoming
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in prev_output:gmatch('[^\n]*\n') do
|
||||||
|
self._parse_status_output(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function Runner:_getopts(stdout_handle)
|
||||||
|
local untracked = self.list_untracked and '-u' or nil
|
||||||
|
local ignored = self.list_ignored and '--ignored=matching' or '--ignored=no'
|
||||||
|
return {
|
||||||
|
args = {"status", "--porcelain=v1", ignored, untracked},
|
||||||
|
cwd = self.project_root,
|
||||||
|
stdio = { nil, stdout_handle, nil },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Runner:_run_git_job()
|
||||||
|
local handle, pid
|
||||||
|
local stdout = uv.new_pipe(false)
|
||||||
|
local timer = uv.new_timer()
|
||||||
|
|
||||||
|
local function on_finish(output)
|
||||||
|
if timer:is_closing() or stdout:is_closing() or handle:is_closing() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
timer:stop()
|
||||||
|
timer:close()
|
||||||
|
stdout:read_stop()
|
||||||
|
stdout:close()
|
||||||
|
handle:close()
|
||||||
|
pcall(uv.kill, pid)
|
||||||
|
|
||||||
|
self.on_end(output or self.output)
|
||||||
|
end
|
||||||
|
|
||||||
|
handle, pid = uv.spawn(
|
||||||
|
"git",
|
||||||
|
self:_getopts(stdout),
|
||||||
|
vim.schedule_wrap(function() on_finish() end)
|
||||||
|
)
|
||||||
|
|
||||||
|
timer:start(self.timeout, 0, vim.schedule_wrap(function() on_finish({}) end))
|
||||||
|
|
||||||
|
local output_leftover = ''
|
||||||
|
local function manage_output(err, data)
|
||||||
|
if err then return end
|
||||||
|
output_leftover = self:_handle_incoming_data(output_leftover, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
uv.read_start(stdout, vim.schedule_wrap(manage_output))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This module runs a git process, which will be killed if it takes more than timeout which defaults to 400ms
|
||||||
|
function Runner.run(opts)
|
||||||
|
local self = setmetatable({
|
||||||
|
project_root = opts.project_root,
|
||||||
|
list_untracked = opts.list_untracked,
|
||||||
|
list_ignored = opts.list_ignored,
|
||||||
|
timeout = opts.timeout or 400,
|
||||||
|
output = {},
|
||||||
|
on_end = opts.on_end,
|
||||||
|
}, Runner)
|
||||||
|
|
||||||
|
self:_run_git_job()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Runner
|
||||||
54
lua/nvim-tree/git/utils.lua
Normal file
54
lua/nvim-tree/git/utils.lua
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.get_toplevel(cwd)
|
||||||
|
local cmd = "git -C " .. vim.fn.shellescape(cwd) .. " rev-parse --show-toplevel"
|
||||||
|
local toplevel = vim.fn.system(cmd)
|
||||||
|
|
||||||
|
if not toplevel or #toplevel == 0 or toplevel:match('fatal') then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- git always returns path with forward slashes
|
||||||
|
if vim.fn.has('win32') == 1 then
|
||||||
|
toplevel = toplevel:gsub("/", "\\")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- remove newline
|
||||||
|
return toplevel:sub(0, -2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local untracked = {}
|
||||||
|
|
||||||
|
function M.should_show_untracked(cwd)
|
||||||
|
if untracked[cwd] ~= nil then
|
||||||
|
return untracked[cwd]
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = "git -C "..cwd.." config --type=bool status.showUntrackedFiles"
|
||||||
|
local has_untracked = vim.fn.system(cmd)
|
||||||
|
untracked[cwd] = vim.trim(has_untracked) ~= 'false'
|
||||||
|
return untracked[cwd]
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.file_status_to_dir_status(status, cwd)
|
||||||
|
local dirs = {}
|
||||||
|
for p, s in pairs(status) do
|
||||||
|
if s ~= '!!' then
|
||||||
|
local modified = vim.fn.fnamemodify(p, ':h')
|
||||||
|
dirs[modified] = 'dirty'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for dirname, _ in pairs(dirs) do
|
||||||
|
local modified = dirname
|
||||||
|
while modified ~= cwd and modified ~= '/' do
|
||||||
|
modified = vim.fn.fnamemodify(modified, ':h')
|
||||||
|
dirs[modified] = 'dirty'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return dirs
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
@ -3,12 +3,12 @@ local luv = vim.loop
|
|||||||
|
|
||||||
local renderer = require'nvim-tree.renderer'
|
local renderer = require'nvim-tree.renderer'
|
||||||
local config = require'nvim-tree.config'
|
local config = require'nvim-tree.config'
|
||||||
local git = require'nvim-tree.git'
|
|
||||||
local diagnostics = require'nvim-tree.diagnostics'
|
local diagnostics = require'nvim-tree.diagnostics'
|
||||||
local pops = require'nvim-tree.populate'
|
local pops = require'nvim-tree.populate'
|
||||||
local utils = require'nvim-tree.utils'
|
local utils = require'nvim-tree.utils'
|
||||||
local view = require'nvim-tree.view'
|
local view = require'nvim-tree.view'
|
||||||
local events = require'nvim-tree.events'
|
local events = require'nvim-tree.events'
|
||||||
|
local git = require'nvim-tree.git'
|
||||||
local populate = pops.populate
|
local populate = pops.populate
|
||||||
local refresh_entries = pops.refresh_entries
|
local refresh_entries = pops.refresh_entries
|
||||||
|
|
||||||
@ -19,33 +19,25 @@ local M = {}
|
|||||||
M.Tree = {
|
M.Tree = {
|
||||||
entries = {},
|
entries = {},
|
||||||
cwd = nil,
|
cwd = nil,
|
||||||
loaded = false,
|
|
||||||
target_winid = nil,
|
target_winid = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
function M.init(with_open, with_reload)
|
local function load_children(cwd, children, parent)
|
||||||
M.Tree.entries = {}
|
git.load_project_status(cwd, function(git_statuses)
|
||||||
if not M.Tree.cwd then
|
populate(children, cwd, parent, git_statuses)
|
||||||
M.Tree.cwd = luv.cwd()
|
M.redraw()
|
||||||
end
|
end)
|
||||||
if config.use_git() then
|
end
|
||||||
git.git_root(M.Tree.cwd)
|
|
||||||
end
|
|
||||||
populate(M.Tree.entries, M.Tree.cwd)
|
|
||||||
|
|
||||||
local stat = luv.fs_stat(M.Tree.cwd)
|
function M.init(with_open, foldername)
|
||||||
M.Tree.last_modified = stat.mtime.sec
|
M.Tree.entries = {}
|
||||||
|
M.Tree.cwd = foldername or luv.cwd()
|
||||||
|
|
||||||
if with_open then
|
if with_open then
|
||||||
M.open()
|
M.open()
|
||||||
elseif view.win_open() then
|
|
||||||
M.refresh_tree()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if with_reload then
|
load_children(M.Tree.cwd, M.Tree.entries)
|
||||||
renderer.draw(M.Tree, true)
|
|
||||||
M.Tree.loaded = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if not first_init_done then
|
if not first_init_done then
|
||||||
events._dispatch_ready()
|
events._dispatch_ready()
|
||||||
@ -85,7 +77,7 @@ local function get_line_from_node(node, find_parent)
|
|||||||
local function iter(entries, recursive)
|
local function iter(entries, recursive)
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
local n = M.get_last_group_node(entry)
|
local n = M.get_last_group_node(entry)
|
||||||
if node_path:match('^'..n.match_path..'$') ~= nil then
|
if node_path == n.absolute_path then
|
||||||
return line, entry
|
return line, entry
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -132,73 +124,75 @@ function M.get_last_group_node(node)
|
|||||||
return next
|
return next
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.unroll_dir(node)
|
function M.expand_or_collapse(node)
|
||||||
node.open = not node.open
|
node.open = not node.open
|
||||||
if node.has_children then node.has_children = false end
|
if node.has_children then node.has_children = false end
|
||||||
if #node.entries > 0 then
|
if #node.entries == 0 then
|
||||||
renderer.draw(M.Tree, true)
|
load_children(
|
||||||
|
node.link_to or node.absolute_path,
|
||||||
|
node.entries,
|
||||||
|
node
|
||||||
|
)
|
||||||
else
|
else
|
||||||
if config.use_git() then
|
M.redraw()
|
||||||
git.git_root(node.absolute_path)
|
|
||||||
end
|
|
||||||
populate(node.entries, node.link_to or node.absolute_path, node)
|
|
||||||
|
|
||||||
renderer.draw(M.Tree, true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
diagnostics.update()
|
diagnostics.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function refresh_git(node)
|
local function refresh_nodes(node, projects)
|
||||||
if not node then node = M.Tree end
|
local project_root = git.get_project_root(node.absolute_path or node.cwd)
|
||||||
git.update_status(node.entries, node.absolute_path or node.cwd, node, false)
|
refresh_entries(node.entries, node.absolute_path or node.cwd, node, projects[project_root] or {})
|
||||||
for _, entry in pairs(node.entries) do
|
|
||||||
if entry.entries and #entry.entries > 0 then
|
|
||||||
refresh_git(entry)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO update only entries where directory has changed
|
|
||||||
local function refresh_nodes(node)
|
|
||||||
refresh_entries(node.entries, node.absolute_path or node.cwd, node)
|
|
||||||
for _, entry in ipairs(node.entries) do
|
for _, entry in ipairs(node.entries) do
|
||||||
if entry.entries and entry.open then
|
if entry.entries and entry.open then
|
||||||
refresh_nodes(entry)
|
refresh_nodes(entry, projects)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- this variable is used to bufferize the refresh actions
|
local event_running = false
|
||||||
-- so only one happens every second at most
|
function M.refresh_tree()
|
||||||
local refreshing = false
|
if event_running or not M.Tree.cwd or vim.v.exiting ~= vim.NIL then
|
||||||
|
|
||||||
function M.refresh_tree(disable_clock)
|
|
||||||
if not M.Tree.cwd or (not disable_clock and refreshing) or vim.v.exiting ~= vim.NIL then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
refreshing = true
|
event_running = true
|
||||||
|
|
||||||
refresh_nodes(M.Tree)
|
git.reload(function(projects)
|
||||||
|
refresh_nodes(M.Tree, projects)
|
||||||
local use_git = config.use_git()
|
if view.win_open() then
|
||||||
if use_git then
|
|
||||||
vim.schedule(function()
|
|
||||||
git.reload_roots()
|
|
||||||
refresh_git(M.Tree)
|
|
||||||
M.redraw()
|
M.redraw()
|
||||||
end)
|
end
|
||||||
|
diagnostics.update()
|
||||||
|
event_running = false
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function reload_node_status(parent_node, projects)
|
||||||
|
local project_root = git.get_project_root(parent_node.absolute_path or parent_node.cwd)
|
||||||
|
local status = projects[project_root] or {}
|
||||||
|
for _, node in ipairs(parent_node.entries) do
|
||||||
|
if node.entries then
|
||||||
|
node.git_status = status.dirs and status.dirs[node.absolute_path]
|
||||||
|
else
|
||||||
|
node.git_status = status.files and status.files[node.absolute_path]
|
||||||
|
end
|
||||||
|
if node.entries and #node.entries > 0 then
|
||||||
|
reload_node_status(node, projects)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
vim.schedule(diagnostics.update)
|
function M.reload_git()
|
||||||
|
if not git.config.enable or event_running then
|
||||||
if view.win_open() then
|
return
|
||||||
renderer.draw(M.Tree, true)
|
|
||||||
else
|
|
||||||
M.Tree.loaded = false
|
|
||||||
end
|
end
|
||||||
|
event_running = true
|
||||||
|
|
||||||
vim.defer_fn(function() refreshing = false end, vim.g.nvim_tree_refresh_wait or 1000)
|
git.reload(function(projects)
|
||||||
|
reload_node_status(M.Tree, projects)
|
||||||
|
M.redraw()
|
||||||
|
event_running = false
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.set_index_and_redraw(fname)
|
function M.set_index_and_redraw(fname)
|
||||||
@ -209,40 +203,46 @@ function M.set_index_and_redraw(fname)
|
|||||||
else
|
else
|
||||||
i = 1
|
i = 1
|
||||||
end
|
end
|
||||||
local reload = false
|
|
||||||
|
|
||||||
local function iter(entries)
|
local tree_altered = false
|
||||||
for _, entry in ipairs(entries) do
|
|
||||||
|
local function iterate_nodes(nodes)
|
||||||
|
for _, node in ipairs(nodes) do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if entry.absolute_path == fname then
|
if node.absolute_path == fname then
|
||||||
return i
|
return i
|
||||||
end
|
end
|
||||||
|
|
||||||
if fname:match(entry.match_path..utils.path_separator) ~= nil then
|
local path_matches = utils.str_find(fname, node.absolute_path..utils.path_separator)
|
||||||
if #entry.entries == 0 then
|
if path_matches then
|
||||||
reload = true
|
if #node.entries == 0 then
|
||||||
populate(entry.entries, entry.absolute_path, entry)
|
node.open = true
|
||||||
|
populate(node.entries, node.absolute_path, node, {})
|
||||||
|
git.load_project_status(node.absolute_path, function(status)
|
||||||
|
if status.dirs or status.files then
|
||||||
|
reload_node_status(node, git.projects)
|
||||||
|
M.redraw()
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
if entry.open == false then
|
if node.open == false then
|
||||||
reload = true
|
node.open = true
|
||||||
entry.open = true
|
tree_altered = true
|
||||||
end
|
end
|
||||||
if iter(entry.entries) ~= nil then
|
if iterate_nodes(node.entries) ~= nil then
|
||||||
return i
|
return i
|
||||||
end
|
end
|
||||||
elseif entry.open == true then
|
elseif node.open == true then
|
||||||
iter(entry.entries)
|
iterate_nodes(node.entries)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local index = iter(M.Tree.entries)
|
local index = iterate_nodes(M.Tree.entries)
|
||||||
if not view.win_open() then
|
if tree_altered then
|
||||||
M.Tree.loaded = false
|
M.redraw()
|
||||||
return
|
|
||||||
end
|
end
|
||||||
renderer.draw(M.Tree, reload)
|
if index and view.win_open() then
|
||||||
if index then
|
|
||||||
view.set_cursor({index, 0})
|
view.set_cursor({index, 0})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -405,8 +405,6 @@ function M.open_file(mode, filename)
|
|||||||
if vim.g.nvim_tree_quit_on_open == 1 then
|
if vim.g.nvim_tree_quit_on_open == 1 then
|
||||||
view.close()
|
view.close()
|
||||||
end
|
end
|
||||||
|
|
||||||
renderer.draw(M.Tree, true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.open_file_in_tab(filename)
|
function M.open_file_in_tab(filename)
|
||||||
@ -467,8 +465,7 @@ function M.change_dir(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
vim.cmd('lcd '..vim.fn.fnameescape(foldername))
|
vim.cmd('lcd '..vim.fn.fnameescape(foldername))
|
||||||
M.Tree.cwd = foldername
|
M.init(false, foldername)
|
||||||
M.init(false, true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.set_target_win()
|
function M.set_target_win()
|
||||||
@ -486,18 +483,19 @@ function M.open()
|
|||||||
M.set_target_win()
|
M.set_target_win()
|
||||||
|
|
||||||
local cwd = vim.fn.getcwd()
|
local cwd = vim.fn.getcwd()
|
||||||
view.open()
|
local should_redraw = view.open()
|
||||||
|
|
||||||
local respect_buf_cwd = vim.g.nvim_tree_respect_buf_cwd or 0
|
local respect_buf_cwd = vim.g.nvim_tree_respect_buf_cwd or 0
|
||||||
if M.Tree.loaded and (respect_buf_cwd == 1 and cwd ~= M.Tree.cwd) then
|
if respect_buf_cwd == 1 and cwd ~= M.Tree.cwd then
|
||||||
M.change_dir(cwd)
|
M.change_dir(cwd)
|
||||||
end
|
end
|
||||||
renderer.draw(M.Tree, not M.Tree.loaded)
|
if should_redraw then
|
||||||
M.Tree.loaded = true
|
M.redraw()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.sibling(node, direction)
|
function M.sibling(node, direction)
|
||||||
if not direction then return end
|
if node.name == '..' or not direction then return end
|
||||||
|
|
||||||
local iter = get_line_from_node(node, true)
|
local iter = get_line_from_node(node, true)
|
||||||
local node_path = node.absolute_path
|
local node_path = node.absolute_path
|
||||||
@ -507,7 +505,7 @@ function M.sibling(node, direction)
|
|||||||
|
|
||||||
-- Check if current node is already at root entries
|
-- Check if current node is already at root entries
|
||||||
for index, entry in ipairs(M.Tree.entries) do
|
for index, entry in ipairs(M.Tree.entries) do
|
||||||
if node_path:match('^'..entry.match_path..'$') ~= nil then
|
if node_path == entry.absolute_path then
|
||||||
line = index
|
line = index
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -534,7 +532,6 @@ function M.sibling(node, direction)
|
|||||||
|
|
||||||
line, _ = get_line_from_node(target_node)(M.Tree.entries, true)
|
line, _ = get_line_from_node(target_node)(M.Tree.entries, true)
|
||||||
view.set_cursor({line, 0})
|
view.set_cursor({line, 0})
|
||||||
renderer.draw(M.Tree, true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.close_node(node)
|
function M.close_node(node)
|
||||||
@ -543,11 +540,14 @@ end
|
|||||||
|
|
||||||
function M.parent_node(node, should_close)
|
function M.parent_node(node, should_close)
|
||||||
if node.name == '..' then return end
|
if node.name == '..' then return end
|
||||||
|
|
||||||
should_close = should_close or false
|
should_close = should_close or false
|
||||||
|
local altered_tree = false
|
||||||
|
|
||||||
local iter = get_line_from_node(node, true)
|
local iter = get_line_from_node(node, true)
|
||||||
if node.open == true and should_close then
|
if node.open == true and should_close then
|
||||||
node.open = false
|
node.open = false
|
||||||
|
altered_tree = true
|
||||||
else
|
else
|
||||||
local line, parent = iter(M.Tree.entries, true)
|
local line, parent = iter(M.Tree.entries, true)
|
||||||
if parent == nil then
|
if parent == nil then
|
||||||
@ -555,9 +555,12 @@ function M.parent_node(node, should_close)
|
|||||||
elseif should_close then
|
elseif should_close then
|
||||||
parent.open = false
|
parent.open = false
|
||||||
end
|
end
|
||||||
api.nvim_win_set_cursor(view.get_winnr(), {line, 0})
|
view.set_cursor({line, 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if altered_tree then
|
||||||
|
M.redraw()
|
||||||
end
|
end
|
||||||
renderer.draw(M.Tree, true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.toggle_ignored()
|
function M.toggle_ignored()
|
||||||
|
|||||||
@ -1,17 +1,13 @@
|
|||||||
local config = require'nvim-tree.config'
|
|
||||||
local git = require'nvim-tree.git'
|
|
||||||
|
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
local luv = vim.loop
|
local luv = vim.loop
|
||||||
|
|
||||||
|
local utils = require'nvim-tree.utils'
|
||||||
|
|
||||||
local M = {
|
local M = {
|
||||||
ignore_list = {}
|
ignore_list = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
local utils = require'nvim-tree.utils'
|
local function dir_new(cwd, name, status, parent_ignored)
|
||||||
local path_to_matching_str = utils.path_to_matching_str
|
|
||||||
|
|
||||||
local function dir_new(cwd, name)
|
|
||||||
local absolute_path = utils.path_join({cwd, name})
|
local absolute_path = utils.path_join({cwd, name})
|
||||||
local stat = luv.fs_stat(absolute_path)
|
local stat = luv.fs_stat(absolute_path)
|
||||||
local handle = luv.fs_scandir(absolute_path)
|
local handle = luv.fs_scandir(absolute_path)
|
||||||
@ -28,16 +24,15 @@ local function dir_new(cwd, name)
|
|||||||
absolute_path = absolute_path,
|
absolute_path = absolute_path,
|
||||||
-- TODO: last modified could also involve atime and ctime
|
-- TODO: last modified could also involve atime and ctime
|
||||||
last_modified = last_modified,
|
last_modified = last_modified,
|
||||||
match_name = path_to_matching_str(name),
|
|
||||||
match_path = path_to_matching_str(absolute_path),
|
|
||||||
open = false,
|
open = false,
|
||||||
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
||||||
has_children = has_children,
|
has_children = has_children,
|
||||||
entries = {}
|
entries = {},
|
||||||
|
git_status = parent_ignored and '!!' or (status.dirs and status.dirs[absolute_path]) or (status.files and status.files[absolute_path]),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function file_new(cwd, name)
|
local function file_new(cwd, name, status, parent_ignored)
|
||||||
local absolute_path = utils.path_join({cwd, name})
|
local absolute_path = utils.path_join({cwd, name})
|
||||||
local is_exec = luv.fs_access(absolute_path, 'X')
|
local is_exec = luv.fs_access(absolute_path, 'X')
|
||||||
return {
|
return {
|
||||||
@ -45,8 +40,7 @@ local function file_new(cwd, name)
|
|||||||
absolute_path = absolute_path,
|
absolute_path = absolute_path,
|
||||||
executable = is_exec,
|
executable = is_exec,
|
||||||
extension = string.match(name, ".?[^.]+%.(.*)") or "",
|
extension = string.match(name, ".?[^.]+%.(.*)") or "",
|
||||||
match_name = path_to_matching_str(name),
|
git_status = parent_ignored and '!!' or status.files and status.files[absolute_path],
|
||||||
match_path = path_to_matching_str(absolute_path),
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -55,8 +49,7 @@ end
|
|||||||
-- links (for instance libr2.so in /usr/lib) and thus even with a C program realpath fails
|
-- links (for instance libr2.so in /usr/lib) and thus even with a C program realpath fails
|
||||||
-- when it has no real reason to. Maybe there is a reason, but errno is definitely wrong.
|
-- when it has no real reason to. Maybe there is a reason, but errno is definitely wrong.
|
||||||
-- So we need to check for link_to ~= nil when adding new links to the main tree
|
-- So we need to check for link_to ~= nil when adding new links to the main tree
|
||||||
local function link_new(cwd, name)
|
local function link_new(cwd, name, status, parent_ignored)
|
||||||
|
|
||||||
--- I dont know if this is needed, because in my understanding, there isnt hard links in windows, but just to be sure i changed it.
|
--- I dont know if this is needed, because in my understanding, there isnt hard links in windows, but just to be sure i changed it.
|
||||||
local absolute_path = utils.path_join({ cwd, name })
|
local absolute_path = utils.path_join({ cwd, name })
|
||||||
local link_to = luv.fs_realpath(absolute_path)
|
local link_to = luv.fs_realpath(absolute_path)
|
||||||
@ -80,8 +73,7 @@ local function link_new(cwd, name)
|
|||||||
open = open,
|
open = open,
|
||||||
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
group_next = nil, -- If node is grouped, this points to the next child dir/link node
|
||||||
entries = entries,
|
entries = entries,
|
||||||
match_name = path_to_matching_str(name),
|
git_status = parent_ignored and '!!' or status.files and status.files[absolute_path],
|
||||||
match_path = path_to_matching_str(absolute_path),
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -105,6 +97,9 @@ local function should_group(cwd, dirs, files, links)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function node_comparator(a, b)
|
local function node_comparator(a, b)
|
||||||
|
if not (a and b) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
if a.entries and not b.entries then
|
if a.entries and not b.entries then
|
||||||
return true
|
return true
|
||||||
elseif not a.entries and b.entries then
|
elseif not a.entries and b.entries then
|
||||||
@ -130,12 +125,6 @@ local function should_ignore(path)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if vim.g.nvim_tree_gitignore == 1 then
|
|
||||||
if git.should_gitignore(path) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local relpath = utils.path_relative(path, vim.loop.cwd())
|
local relpath = utils.path_relative(path, vim.loop.cwd())
|
||||||
if M.ignore_list[relpath] == true or M.ignore_list[basename] == true then
|
if M.ignore_list[relpath] == true or M.ignore_list[basename] == true then
|
||||||
return true
|
return true
|
||||||
@ -151,7 +140,11 @@ local function should_ignore(path)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.refresh_entries(entries, cwd, parent_node)
|
local function should_ignore_git(path, status)
|
||||||
|
return M.config.filter_ignored and (status and status[path] == '!!')
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.refresh_entries(entries, cwd, parent_node, status)
|
||||||
local handle = luv.fs_scandir(cwd)
|
local handle = luv.fs_scandir(cwd)
|
||||||
if type(handle) == 'string' then
|
if type(handle) == 'string' then
|
||||||
api.nvim_err_writeln(handle)
|
api.nvim_err_writeln(handle)
|
||||||
@ -162,6 +155,9 @@ function M.refresh_entries(entries, cwd, parent_node)
|
|||||||
local cached_entries = {}
|
local cached_entries = {}
|
||||||
local entries_idx = {}
|
local entries_idx = {}
|
||||||
for i, node in ipairs(entries) do
|
for i, node in ipairs(entries) do
|
||||||
|
node.git_status = (parent_node and parent_node.git_status == '!!' and '!!')
|
||||||
|
or (status.files and status.files[node.absolute_path])
|
||||||
|
or (status.dirs and status.dirs[node.absolute_path])
|
||||||
cached_entries[i] = node.name
|
cached_entries[i] = node.name
|
||||||
entries_idx[node.name] = i
|
entries_idx[node.name] = i
|
||||||
named_entries[node.name] = node
|
named_entries[node.name] = node
|
||||||
@ -179,7 +175,7 @@ function M.refresh_entries(entries, cwd, parent_node)
|
|||||||
num_new_entries = num_new_entries + 1
|
num_new_entries = num_new_entries + 1
|
||||||
|
|
||||||
local abs = utils.path_join({cwd, name})
|
local abs = utils.path_join({cwd, name})
|
||||||
if not should_ignore(abs) then
|
if not should_ignore(abs) and not should_ignore_git(abs, status.files) then
|
||||||
if not t then
|
if not t then
|
||||||
local stat = luv.fs_stat(abs)
|
local stat = luv.fs_stat(abs)
|
||||||
t = stat and stat.type
|
t = stat and stat.type
|
||||||
@ -208,7 +204,7 @@ function M.refresh_entries(entries, cwd, parent_node)
|
|||||||
parent_node.group_next = nil
|
parent_node.group_next = nil
|
||||||
named_entries[next_node.name] = next_node
|
named_entries[next_node.name] = next_node
|
||||||
else
|
else
|
||||||
M.refresh_entries(entries, next_node.absolute_path, next_node)
|
M.refresh_entries(entries, next_node.absolute_path, next_node, status)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -245,7 +241,7 @@ function M.refresh_entries(entries, cwd, parent_node)
|
|||||||
for _, name in ipairs(e.entries) do
|
for _, name in ipairs(e.entries) do
|
||||||
change_prev = true
|
change_prev = true
|
||||||
if not named_entries[name] then
|
if not named_entries[name] then
|
||||||
local n = e.fn(cwd, name)
|
local n = e.fn(cwd, name, status)
|
||||||
if e.check(n.link_to, n.absolute_path) then
|
if e.check(n.link_to, n.absolute_path) then
|
||||||
new_nodes_added = true
|
new_nodes_added = true
|
||||||
idx = 1
|
idx = 1
|
||||||
@ -274,7 +270,7 @@ function M.refresh_entries(entries, cwd, parent_node)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.populate(entries, cwd, parent_node)
|
function M.populate(entries, cwd, parent_node, status)
|
||||||
local handle = luv.fs_scandir(cwd)
|
local handle = luv.fs_scandir(cwd)
|
||||||
if type(handle) == 'string' then
|
if type(handle) == 'string' then
|
||||||
api.nvim_err_writeln(handle)
|
api.nvim_err_writeln(handle)
|
||||||
@ -290,7 +286,7 @@ function M.populate(entries, cwd, parent_node)
|
|||||||
if not name then break end
|
if not name then break end
|
||||||
|
|
||||||
local abs = utils.path_join({cwd, name})
|
local abs = utils.path_join({cwd, name})
|
||||||
if not should_ignore(abs) then
|
if not should_ignore(abs) and not should_ignore_git(abs, status.files) then
|
||||||
if not t then
|
if not t then
|
||||||
local stat = luv.fs_stat(abs)
|
local stat = luv.fs_stat(abs)
|
||||||
t = stat and stat.type
|
t = stat and stat.type
|
||||||
@ -306,52 +302,42 @@ function M.populate(entries, cwd, parent_node)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create Nodes --
|
local parent_node_ignored = parent_node and parent_node.git_status == '!!'
|
||||||
|
|
||||||
-- Group empty dirs
|
-- Group empty dirs
|
||||||
if parent_node and vim.g.nvim_tree_group_empty == 1 then
|
if parent_node and vim.g.nvim_tree_group_empty == 1 then
|
||||||
if should_group(cwd, dirs, files, links) then
|
if should_group(cwd, dirs, files, links) then
|
||||||
local child_node
|
local child_node
|
||||||
if dirs[1] then child_node = dir_new(cwd, dirs[1]) end
|
if dirs[1] then child_node = dir_new(cwd, dirs[1], status, parent_node_ignored) end
|
||||||
if links[1] then child_node = link_new(cwd, links[1]) end
|
if links[1] then child_node = link_new(cwd, links[1], status, parent_node_ignored) end
|
||||||
if luv.fs_access(child_node.absolute_path, 'R') then
|
if luv.fs_access(child_node.absolute_path, 'R') then
|
||||||
parent_node.group_next = child_node
|
parent_node.group_next = child_node
|
||||||
child_node.git_status = parent_node.git_status
|
child_node.git_status = parent_node.git_status
|
||||||
M.populate(entries, child_node.absolute_path, child_node)
|
M.populate(entries, child_node.absolute_path, child_node, status)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, dirname in ipairs(dirs) do
|
for _, dirname in ipairs(dirs) do
|
||||||
local dir = dir_new(cwd, dirname)
|
local dir = dir_new(cwd, dirname, status, parent_node_ignored)
|
||||||
if luv.fs_access(dir.absolute_path, 'R') then
|
if luv.fs_access(dir.absolute_path, 'R') then
|
||||||
table.insert(entries, dir)
|
table.insert(entries, dir)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, linkname in ipairs(links) do
|
for _, linkname in ipairs(links) do
|
||||||
local link = link_new(cwd, linkname)
|
local link = link_new(cwd, linkname, status, parent_node_ignored)
|
||||||
if link.link_to ~= nil then
|
if link.link_to ~= nil then
|
||||||
table.insert(entries, link)
|
table.insert(entries, link)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, filename in ipairs(files) do
|
for _, filename in ipairs(files) do
|
||||||
local file = file_new(cwd, filename)
|
local file = file_new(cwd, filename, status, parent_node_ignored)
|
||||||
table.insert(entries, file)
|
table.insert(entries, file)
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.merge_sort(entries, node_comparator)
|
utils.merge_sort(entries, node_comparator)
|
||||||
|
|
||||||
local icon_config = config.get_icon_state()
|
|
||||||
if (not icon_config.show_git_icon) and vim.g.nvim_tree_git_hl ~= 1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if config.use_git() then
|
|
||||||
vim.schedule(function() git.update_status(entries, cwd, parent_node, true) end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.setup(opts)
|
function M.setup(opts)
|
||||||
|
|||||||
@ -163,7 +163,7 @@ if vim.g.nvim_tree_git_hl == 1 then
|
|||||||
local icons = git_hl[git_status]
|
local icons = git_hl[git_status]
|
||||||
|
|
||||||
if icons == nil then
|
if icons == nil then
|
||||||
utils.echo_warning('Unrecognized git state "'..git_status..'". Please open up an issue on https://github.com/kyazdani42/nvim-tree.lua/issues with this message.')
|
utils.warn('Unrecognized git state "'..git_status..'". Please open up an issue on https://github.com/kyazdani42/nvim-tree.lua/issues with this message.')
|
||||||
icons = git_hl.dirty
|
icons = git_hl.dirty
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ if icon_state.show_git_icon then
|
|||||||
local icons = git_icon_state[git_status]
|
local icons = git_icon_state[git_status]
|
||||||
if not icons then
|
if not icons then
|
||||||
if vim.g.nvim_tree_git_hl ~= 1 then
|
if vim.g.nvim_tree_git_hl ~= 1 then
|
||||||
utils.echo_warning('Unrecognized git state "'..git_status..'". Please open up an issue on https://github.com/kyazdani42/nvim-tree.lua/issues with this message.')
|
utils.warn('Unrecognized git state "'..git_status..'". Please open up an issue on https://github.com/kyazdani42/nvim-tree.lua/issues with this message.')
|
||||||
end
|
end
|
||||||
icons = git_icon_state.dirty
|
icons = git_icon_state.dirty
|
||||||
end
|
end
|
||||||
|
|||||||
@ -6,12 +6,16 @@ function M.path_to_matching_str(path)
|
|||||||
return path:gsub('(%-)', '(%%-)'):gsub('(%.)', '(%%.)'):gsub('(%_)', '(%%_)')
|
return path:gsub('(%-)', '(%%-)'):gsub('(%.)', '(%%.)'):gsub('(%_)', '(%%_)')
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.echo_warning(msg)
|
function M.warn(msg)
|
||||||
api.nvim_command('echohl WarningMsg')
|
api.nvim_command('echohl WarningMsg')
|
||||||
api.nvim_command("echom '[NvimTree] "..msg:gsub("'", "''").."'")
|
api.nvim_command("echom '[NvimTree] "..msg:gsub("'", "''").."'")
|
||||||
api.nvim_command('echohl None')
|
api.nvim_command('echohl None')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.str_find(haystack, needle)
|
||||||
|
return vim.fn.stridx(haystack, needle) ~= -1
|
||||||
|
end
|
||||||
|
|
||||||
function M.read_file(path)
|
function M.read_file(path)
|
||||||
local fd = uv.fs_open(path, "r", 438)
|
local fd = uv.fs_open(path, "r", 438)
|
||||||
if not fd then return '' end
|
if not fd then return '' end
|
||||||
|
|||||||
@ -304,7 +304,9 @@ local function is_buf_valid(bufnr)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.open(options)
|
function M.open(options)
|
||||||
|
local should_redraw = false
|
||||||
if not is_buf_valid(M.View.bufnr) then
|
if not is_buf_valid(M.View.bufnr) then
|
||||||
|
should_redraw = true
|
||||||
create_buffer()
|
create_buffer()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -322,6 +324,7 @@ function M.open(options)
|
|||||||
if not opts.focus_tree then
|
if not opts.focus_tree then
|
||||||
vim.cmd("wincmd p")
|
vim.cmd("wincmd p")
|
||||||
end
|
end
|
||||||
|
return should_redraw
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_existing_buffers()
|
local function get_existing_buffers()
|
||||||
|
|||||||
@ -24,7 +24,8 @@ local out_config = {
|
|||||||
"nvim_tree_disable_keybindings",
|
"nvim_tree_disable_keybindings",
|
||||||
"nvim_tree_disable_default_keybindings",
|
"nvim_tree_disable_default_keybindings",
|
||||||
"nvim_tree_hide_dotfiles",
|
"nvim_tree_hide_dotfiles",
|
||||||
"nvim_tree_ignore"
|
"nvim_tree_ignore",
|
||||||
|
"nvim_tree_gitignore"
|
||||||
}
|
}
|
||||||
|
|
||||||
local x = vim.tbl_filter(function(v)
|
local x = vim.tbl_filter(function(v)
|
||||||
@ -33,5 +34,5 @@ end, out_config)
|
|||||||
|
|
||||||
if #x > 0 then
|
if #x > 0 then
|
||||||
local msg = "Following options were moved to setup, see git.io/JPhyt: "
|
local msg = "Following options were moved to setup, see git.io/JPhyt: "
|
||||||
require'nvim-tree.utils'.echo_warning(msg..table.concat(x, ", "))
|
require'nvim-tree.utils'.warn(msg..table.concat(x, ", "))
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user