Skip to content
Sarah edited this page Nov 29, 2022 · 85 revisions

h, j, k, l Style Navigation And Editing

@eddiebergman

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

Git Stage Unstage Files And Directories From The Tree

@Hubro

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.

Find File From Node In Telescope

@kyazdani42

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" },
            }
        }
    }
}

Filter Directories With Live Filter

@kay-adamof

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
  }
}

Fix tab titles when opening file in new tab

@sarahkittyy

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 },
Clone this wiki locally