diff --git a/README.md b/README.md index fac5e39e..5020c8ad 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This plugin requires [neovim nightly (>=0.5.0)](https://github.com/neovim/neovim Install with [vim-plug](https://github.com/junegunn/vim-plug): ```vim -" requires +" requires Plug 'kyazdani42/nvim-web-devicons' " for file icons Plug 'kyazdani42/nvim-tree.lua' ``` @@ -136,6 +136,8 @@ lua <"] = tree_cb("vsplit"), [""] = tree_cb("split"), [""] = tree_cb("tabnew"), + ["<"] = tree_cb("prev_sibling"), + [">"] = tree_cb("next_sibling"), [""] = tree_cb("close_node"), [""] = tree_cb("close_node"), [""] = tree_cb("preview"), diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index c2b92eed..d52c2b14 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -229,6 +229,7 @@ INFORMATIONS *nvim-tree-info* - '' on '..' will cd in the above directory - typing '' will cd in the directory under the cursor - typing '' will close current opened directory or parent +- typing 'P' will move cursor to the parent directory - type 'a' to add a file - type 'r' to rename a file @@ -241,6 +242,10 @@ INFORMATIONS *nvim-tree-info* - type ']c' to go to next git item - type '[c' to go to prev git item - type '-' to navigate up one directory +- type '<' to navigate to the previous sibling of current file/directory +- type '>' to navigate to the next sibling of current file/directory +- type 'J' to navigate to the first sibling of current file/directory +- type 'K' to navigate to the last sibling of current file/directory - if the file is a directory, '' will open the directory - otherwise it will open the file in the buffer near the tree @@ -280,9 +285,14 @@ Default keybindings can be overriden. You can also define your own keymappings f [""] = tree_cb("vsplit"), [""] = tree_cb("split"), [""] = tree_cb("tabnew"), + ["<"] = tree_cb("prev_sibling"), + [">"] = tree_cb("next_sibling"), + ["P"] = tree_cb("parent_node"), [""] = tree_cb("close_node"), [""] = tree_cb("close_node"), [""] = tree_cb("preview"), + ["K"] = tree_cb("first_sibling"), + ["J"] = tree_cb("last_sibling"), ["I"] = tree_cb("toggle_ignored"), ["H"] = tree_cb("toggle_dotfiles"), ["R"] = tree_cb("refresh"), diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index b85183c4..542ef8f4 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -71,9 +71,14 @@ local keypress_funcs = { cut = fs.cut, paste = fs.paste, close_node = lib.close_node, + parent_node = lib.parent_node, toggle_ignored = lib.toggle_ignored, toggle_dotfiles = lib.toggle_dotfiles, refresh = lib.refresh_tree, + first_sibling = function(node) lib.sibling(node, -math.huge) end, + last_sibling = function(node) lib.sibling(node, math.huge) end, + prev_sibling = function(node) lib.sibling(node, -1) end, + next_sibling = function(node) lib.sibling(node, 1) end, prev_git_item = gen_go_to('prev_git_item'), next_git_item = gen_go_to('next_git_item'), dir_up = lib.dir_up, diff --git a/lua/nvim-tree/config.lua b/lua/nvim-tree/config.lua index 52572ee4..71f3c954 100644 --- a/lua/nvim-tree/config.lua +++ b/lua/nvim-tree/config.lua @@ -67,9 +67,14 @@ function M.get_bindings() [""] = M.nvim_tree_callback("vsplit"), [""] = M.nvim_tree_callback("split"), [""] = M.nvim_tree_callback("tabnew"), + ["<"] = M.nvim_tree_callback("prev_sibling"), + [">"] = M.nvim_tree_callback("next_sibling"), + ["P"] = M.nvim_tree_callback("parent_node"), [""] = M.nvim_tree_callback("close_node"), [""] = M.nvim_tree_callback("close_node"), [""] = M.nvim_tree_callback("preview"), + ["K"] = M.nvim_tree_callback("first_sibling"), + ["J"] = M.nvim_tree_callback("last_sibling"), ["I"] = M.nvim_tree_callback("toggle_ignored"), ["H"] = M.nvim_tree_callback("toggle_dotfiles"), ["R"] = M.nvim_tree_callback("refresh"), diff --git a/lua/nvim-tree/lib.lua b/lua/nvim-tree/lib.lua index a5d14ec8..74e2b9b4 100644 --- a/lua/nvim-tree/lib.lua +++ b/lua/nvim-tree/lib.lua @@ -78,6 +78,30 @@ local function get_node_at_line(line) return iter end +local function get_line_from_node(node, find_parent) + local node_path = node.absolute_path + + if find_parent then + node_path = node.absolute_path:match("(.*)"..utils.path_separator) + end + + local line = 2 + local function iter(entries, recursive) + for _, entry in ipairs(entries) do + if node_path:match('^'..entry.match_path..'$') ~= nil then + return line, entry + end + + line = line + 1 + if entry.open == true and recursive then + local _, child = iter(entry.entries, recursive) + if child ~= nil then return line, child end + end + end + end + return iter +end + function M.get_node_at_cursor() local cursor = api.nvim_win_get_cursor(M.Tree.winnr()) local line = cursor[1] @@ -334,37 +358,65 @@ function M.open() api.nvim_command('setlocal '..window_opts.split_command) end -function M.close_node(node) - if node.name == '..' then return end +function M.sibling(node, direction) + if not direction then return end - local sep = package.config:sub(1,1) - local dname = node.absolute_path:match("(.*"..sep..")") - local index = 2 + local iter = get_line_from_node(node, true) + local node_path = node.absolute_path - local function iter(entries) - for _, entry in ipairs(entries) do - if dname:match('^'..entry.match_path..sep..'$') ~= nil then - return entry - end + local line, parent = 0, nil - index = index + 1 - if entry.open == true then - local child = iter(entry.entries) - if child ~= nil then return child end - end + -- Check if current node is already at root entries + for index, entry in ipairs(M.Tree.entries) do + if node_path:match('^'..entry.match_path..'$') ~= nil then + line = index end end - if node.open == true then + if line > 0 then + parent = M.Tree + else + _, parent = iter(M.Tree.entries, true) + if parent ~= nil and #parent.entries > 1 then + line, _ = get_line_from_node(node)(parent.entries) + end + + -- Ignore parent line count + line = line - 1 + end + + local index = line + direction + if index < 1 then + index = 1 + elseif index > #parent.entries then + index = #parent.entries + end + local target_node = parent.entries[index] + + line, _ = get_line_from_node(target_node)(M.Tree.entries, true) + api.nvim_win_set_cursor(M.Tree.winnr(), {line, 0}) + renderer.draw(M.Tree, true) +end + +function M.close_node(node) + M.parent_node(node, true) +end + +function M.parent_node(node, should_close) + if node.name == '..' then return end + should_close = should_close or false + + local iter = get_line_from_node(node, true) + if node.open == true and should_close then node.open = false else - local parent = iter(M.Tree.entries) + local line, parent = iter(M.Tree.entries, true) if parent == nil then - index = 1 - else + line = 1 + elseif should_close then parent.open = false end - api.nvim_win_set_cursor(M.Tree.winnr(), {index, 0}) + api.nvim_win_set_cursor(M.Tree.winnr(), {line, 0}) end renderer.draw(M.Tree, true) end