-
-
Notifications
You must be signed in to change notification settings - Fork 625
Recipes
Hotkeys that keep your fingers using h, j, k, l for navigation and opening.
- Ctrl + h | Open tree
- h | Collapse current containing folder
- H | Collapse Tree
- l | Open node if it is a folder, else edit the file and close tree
- L | Open node if it is a folder, else create vsplit of file and keep cursor focus on tree
In particular, L is quite nice for opening a few files in quick succession without losing focus of the tree while using l for just editing a file in the current buffer.
To do so, you'll want to add some mappings with your own action_cb
:
local config = {
view = {
mappings = {
custom_only = false,
list = {
{ key = "l", action = "edit", action_cb = edit_or_open },
{ key = "L", action = "vsplit_preview", action_cb = vsplit_preview },
{ key = "h", action = "close_node" },
{ key = "H", action = "collapse_all", action_cb = collapse_all }
}
},
},
actions = {
open_file = {
quit_on_open = false
}
}
}
vim.api.nvim_set_keymap("n", "<C-h>", ":NvimTreeToggle<cr>" ,{silent = true, noremap = true})
require('nvim-tree').setup(config)
You'll also need to define these callbacks that nvim-tree
will use, they'll need to be defined above the snippet above:
local lib = require("nvim-tree.lib")
local view = require("nvim-tree.view")
local function collapse_all()
require("nvim-tree.actions.tree-modifiers.collapse-all").fn()
end
local function edit_or_open()
-- open as vsplit on current node
local action = "edit"
local node = lib.get_node_at_cursor()
-- Just copy what's done normally with vsplit
if node.link_to and not node.nodes then
require('nvim-tree.actions.node.open-file').fn(action, node.link_to)
view.close() -- Close the tree if file was opened
elseif node.nodes ~= nil then
lib.expand_or_collapse(node)
else
require('nvim-tree.actions.node.open-file').fn(action, node.absolute_path)
view.close() -- Close the tree if file was opened
end
end
local function vsplit_preview()
-- open as vsplit on current node
local action = "vsplit"
local node = lib.get_node_at_cursor()
-- Just copy what's done normally with vsplit
if node.link_to and not node.nodes then
require('nvim-tree.actions.node.open-file').fn(action, node.link_to)
elseif node.nodes ~= nil then
lib.expand_or_collapse(node)
else
require('nvim-tree.actions.node.open-file').fn(action, node.absolute_path)
end
-- Finally refocus on tree if it was lost
view.focus()
end
You can stage and unstage files directly in the tree view with this config:
local lib = require("nvim-tree.lib")
local git_add = function()
local node = lib.get_node_at_cursor()
local gs = node.git_status
-- If the file is untracked, unstaged or partially staged, we stage it
if gs == "??" or gs == "MM" or gs == "AM" or gs == " M" then
vim.cmd("silent !git add " .. node.absolute_path)
-- If the file is staged, we unstage
elseif gs == "M " or gs == "A " then
vim.cmd("silent !git restore --staged " .. node.absolute_path)
end
lib.refresh_tree()
end
require("nvim-tree").setup {
view = {
mappings = {
list = {
{ key = "ga", action = "git_add", action_cb = git_add },
}
},
}
}
If you target a file or directory in the tree and press ga
, it will be git staged. If it's already staged, it will instead be unstaged.
Here is a small snippet of code to open the file under the cursor in telescope
File named 'treeutils.lua'
local lib = require'nvim-tree.lib'
local openfile = require'nvim-tree.actions.node.open-file'
local actions = require'telescope.actions'
local action_state = require'telescope.actions.state'
local M = {}
local view_selection = function(prompt_bufnr, map)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
local filename = selection.filename
if (filename == nil) then
filename = selection[1]
end
openfile.fn('preview', filename)
end)
return true
end
function M.launch_live_grep(opts)
return M.launch_telescope("live_grep", opts)
end
function M.launch_find_files(opts)
return M.launch_telescope("find_files", opts)
end
function M.launch_telescope(func_name, opts)
local telescope_status_ok, _ = pcall(require, "telescope")
if not telescope_status_ok then
return
end
local lib_status_ok, lib = pcall(require, "nvim-tree.lib")
if not lib_status_ok then
return
end
local node = lib.get_node_at_cursor()
local is_folder = node.fs_stat and node.fs_stat.type == 'directory' or false
local basedir = is_folder and node.absolute_path or vim.fn.fnamemodify(node.absolute_path, ":h")
if (node.name == '..' and TreeExplorer ~= nil) then
basedir = TreeExplorer.cwd
end
opts = opts or {}
opts.cwd = basedir
opts.search_dirs = { basedir }
opts.attach_mappings = view_selection
return require("telescope.builtin")[func_name](opts)
end
return M
And then to use it, add something like this depending on your configuration structure
local function custom_callback(callback_name)
return string.format(":lua require('treeutils').%s()<CR>", callback_name)
end
-- this is passed in the call to setup
require 'nvim-tree'.setup {
view = {
mappings = {
list = {
{ key = "<c-f>", cb = custom_callback "launch_find_files" },
{ key = "<c-g>", cb = custom_callback "launch_live_grep" },
}
}
}
}
By default, the live filter
doesn't filter the directory names but it's so painful if you have many folders in your project.
Put this code to your init.lua.
require("nvim-tree").setup {
live_filter = {
prefix = "[FILTER]: ",
always_show_folders = false, -- Turn into false from true by default
}
}
By default, opening a file in a new tab leaves NvimTree_1 as the text in the tab bar. This function will run <C-w>l
before opening a new tab to set the tab title to the split adjacent to the tree.
local swap_then_open_tab = function()
local node = lib.get_node_at_cursor()
vim.cmd("wincmd l")
api.node.open.tab(node)
end
-- { key = "t", action = "swap_then_open_tab", action_cb = swap_then_open_tab },
For nvim-tree to be a centered floating window, add this to your config:
local HEIGHT_RATIO = 0.8 -- You can change this
local WIDTH_RATIO = 0.5 -- You can change this too
require('nvim-tree').setup({
view = {
float = {
enable = true,
open_win_config = function()
local screen_w = vim.opt.columns:get()
local screen_h = vim.opt.lines:get() - vim.opt.cmdheight:get()
local window_w = screen_w * WIDTH_RATIO
local window_h = screen_h * HEIGHT_RATIO
local window_w_int = math.floor(window_w)
local window_h_int = math.floor(window_h)
local center_x = (screen_w - window_w) / 2
local center_y = ((vim.opt.lines:get() - window_h) / 2)
- vim.opt.cmdheight:get()
return {
border = 'rounded',
relative = 'editor',
row = center_y,
col = center_x,
width = window_w_int,
height = window_h_int,
}
end,
},
width = function()
return math.floor(vim.opt.columns:get() * WIDTH_RATIO)
end,
},
})
Original solution by @davidsierradz and @alex-courtis (see #1538). Recipe written by @Kryzar.
It is possible to use Telescope as a menu, listing custom items and executing a callback on item selection. Following is a minimal example.
First of all, you will need a list of labels to show in the menu, along with their associated callbacks.
Note that nvim-tree actions are directly requirable from nvim-tree.api
.
local tree_actions = {
{
name = "Create node",
handler = require("nvim-tree.api").fs.create,
},
{
name = "Remove node",
handler = require("nvim-tree.api").fs.remove,
},
{
name = "Trash node",
handler = require("nvim-tree.api").fs.trash,
},
{
name = "Rename node",
handler = require("nvim-tree.api").fs.rename,
},
{
name = "Fully rename node",
handler = require("nvim-tree.api").fs.rename_sub,
},
{
name = "Copy",
handler = require("nvim-tree.api").fs.copy.node,
},
-- ... other custom actions you may want to display in the menu
}
You will then need a function used to display the actions in a Telescope picker.
The function will be keymapped inside on_attach
nvim-tree config option and as such it will receive the highlighted node.
local function tree_actions_menu(node)
local entry_maker = function(menu_item)
return {
value = menu_item,
ordinal = menu_item.name,
display = menu_item.name,
}
end
local finder = require("telescope.finders").new_table({
results = tree_actions,
entry_maker = entry_maker,
})
local sorter = require("telescope.sorters").get_generic_fuzzy_sorter()
local default_options = {
finder = finder,
sorter = sorter,
attach_mappings = function(prompt_buffer_number)
local actions = require("telescope.actions")
-- On item select
actions.select_default:replace(function()
local state = require("telescope.actions.state")
local selection = state.get_selected_entry()
-- Closing the picker
actions.close(prompt_buffer_number)
-- Executing the callback
selection.value.handler(node)
end)
-- The following actions are disabled in this example
-- You may want to map them too depending on your needs though
actions.add_selection:replace(function() end)
actions.remove_selection:replace(function() end)
actions.toggle_selection:replace(function() end)
actions.select_all:replace(function() end)
actions.drop_all:replace(function() end)
actions.toggle_all:replace(function() end)
return true
end,
}
-- Opening the menu
require("telescope.pickers").new({ prompt_title = "Tree menu" }, default_options):find()
end
Finally, you will need to add a keymap for the created function.
require("nvim-tree").setup({
-- ... other nvim-tree configs
on_attach = function(buffer)
vim.keymap.set(
"n",
"<C-Space>",
tree_actions_menu,
{ buffer = buffer, noremap = true, silent = true }
)
end,
end
})
Since the function receives the currently highlighted node, it is possible to create menu variations depending on it.
It is also possible to integrate custom functionalities by passing them as callbacks. In the latter case, it could be useful to make use of require("nvim-tree.utils").inject_node
and wrap your custom function to receive the node.