diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index fd535502..0eb4e547 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -5,13 +5,15 @@ local colors = require'nvim-tree.colors' local renderer = require'nvim-tree.renderer' local fs = require'nvim-tree.fs' local utils = require'nvim-tree.utils' +local view = require'nvim-tree.view' + local api = vim.api local M = {} function M.toggle() - if lib.win_open() then - lib.close() + if view.win_open() then + view.close() else if vim.g.nvim_tree_follow == 1 then M.find_file(true) @@ -22,27 +24,27 @@ function M.toggle() end function M.close() - if lib.win_open() then - lib.close() + if view.win_open() then + view.close() return true end end function M.open() - if not lib.win_open() then + if not view.win_open() then lib.open() else lib.set_target_win() end end -local winopts = config.window_options() +-- this is completely broken, but i'm not sure why +-- this is definitely upstream related, but i could find a workaround function M.tab_change() -- we need defer_fn to make sure we close/open after we enter the tab vim.defer_fn(function() if M.close() then M.open() - api.nvim_command('wincmd '..winopts.open_command) end end, 1) end @@ -148,7 +150,7 @@ function M.find_file(with_open) if with_open then M.open() - lib.win_focus() + view.focus() if not is_file_readable(filepath) then return end lib.set_index_and_redraw(filepath) return @@ -194,6 +196,7 @@ function M.reset_highlight() renderer.render_hl(lib.Tree.bufnr) end +view.setup() colors.setup() vim.defer_fn(M.on_enter, 1) diff --git a/lua/nvim-tree/config.lua b/lua/nvim-tree/config.lua index f4be8466..c1d2b670 100644 --- a/lua/nvim-tree/config.lua +++ b/lua/nvim-tree/config.lua @@ -57,52 +57,13 @@ function M.nvim_tree_callback(callback_name) return string.format(":lua require'nvim-tree'.on_keypress('%s')", callback_name) end -function M.get_bindings() - local keybindings = vim.g.nvim_tree_bindings or {} - return vim.tbl_extend('force', { - [""] = M.nvim_tree_callback("edit"), - ["o"] = M.nvim_tree_callback("edit"), - ["<2-LeftMouse>"] = M.nvim_tree_callback("edit"), - ["<2-RightMouse>"] = M.nvim_tree_callback("cd"), - [""] = M.nvim_tree_callback("cd"), - [""] = 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"), - ["a"] = M.nvim_tree_callback("create"), - ["d"] = M.nvim_tree_callback("remove"), - ["r"] = M.nvim_tree_callback("rename"), - [""] = M.nvim_tree_callback("full_rename"), - ["x"] = M.nvim_tree_callback("cut"), - ["c"] = M.nvim_tree_callback("copy"), - ["p"] = M.nvim_tree_callback("paste"), - ["[c"] = M.nvim_tree_callback("prev_git_item"), - ["]c"] = M.nvim_tree_callback("next_git_item"), - ["-"] = M.nvim_tree_callback("dir_up"), - ["q"] = M.nvim_tree_callback("close"), - }, keybindings) -end - function M.window_options() local opts = {} - opts.winhl = 'EndOfBuffer:NvimTreeEndOfBuffer,Normal:NvimTreeNormal,CursorLine:NvimTreeCursorLine,VertSplit:NvimTreeVertSplit' if vim.g.nvim_tree_side == 'right' then - opts.side = 'L' opts.open_command = 'h' opts.preview_command = 'l' opts.split_command = 'nosplitright' else - opts.side = 'H' opts.open_command = 'l' opts.preview_command = 'h' opts.split_command = 'splitright' diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 3db1103b..6bc03e29 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -21,7 +21,6 @@ local function highlight_node(node, linenr) a.nvim_buf_add_highlight(buf, -1, 'NvimTreeLspDiagnostics', linenr, starts_at, -1) end - function M.update() local buffer_severity = {} diff --git a/lua/nvim-tree/lib.lua b/lua/nvim-tree/lib.lua index 75c2266a..69584624 100644 --- a/lua/nvim-tree/lib.lua +++ b/lua/nvim-tree/lib.lua @@ -6,6 +6,7 @@ local config = require'nvim-tree.config' local git = require'nvim-tree.git' local pops = require'nvim-tree.populate' local utils = require'nvim-tree.utils' +local view = require'nvim-tree.view' local populate = pops.populate local refresh_entries = pops.refresh_entries @@ -15,36 +16,12 @@ local M = {} M.Tree = { entries = {}, - buf_name = 'NvimTree', cwd = nil, - win_width = vim.g.nvim_tree_width or 30, - win_width_allow_resize = vim.g.nvim_tree_width_allow_resize, loaded = false, - bufnr = nil, target_winid = nil, - winnr = function() - for _, i in ipairs(api.nvim_list_wins()) do - if api.nvim_buf_get_name(api.nvim_win_get_buf(i)):match('.*'..utils.path_separator..M.Tree.buf_name..'$') then - return i - end - end - end, - options = { - 'noswapfile', - 'norelativenumber', - 'nonumber', - 'nolist', - 'winfixwidth', - 'winfixheight', - 'nofoldenable', - 'nospell', - 'signcolumn=yes', - 'foldmethod=manual', - 'foldcolumn=0' - } } -function M.init(with_open, with_render) +function M.init(with_open, with_reload) M.Tree.cwd = luv.cwd() git.git_root(M.Tree.cwd) git.update_gitignore_map_sync() @@ -57,7 +34,7 @@ function M.init(with_open, with_render) M.open() end - if with_render then + if with_reload then renderer.draw(M.Tree, true) M.Tree.loaded = true end @@ -105,7 +82,7 @@ local function get_line_from_node(node, find_parent) end function M.get_node_at_cursor() - local cursor = api.nvim_win_get_cursor(M.Tree.winnr()) + local cursor = api.nvim_win_get_cursor(view.View.winnr) local line = cursor[1] if line == 1 and M.Tree.cwd ~= "/" then return { name = ".." } @@ -171,7 +148,7 @@ function M.refresh_tree() git.reload_roots() refresh_git(M.Tree) end - if M.win_open() then + if view.win_open() then renderer.draw(M.Tree, true) else M.Tree.loaded = false @@ -213,13 +190,13 @@ function M.set_index_and_redraw(fname) end local index = iter(M.Tree.entries) - if not M.win_open() then + if not view.win_open() then M.Tree.loaded = false return end renderer.draw(M.Tree, reload) if index then - api.nvim_win_set_cursor(M.Tree.winnr(), {index, 0}) + view.set_cursor({index, 0}) end end @@ -235,7 +212,7 @@ function M.open_file(mode, filename) for _, win in ipairs(api.nvim_list_wins()) do if filename == api.nvim_buf_get_name(api.nvim_win_get_buf(win)) then found = true - ecmd = function() M.win_focus(win) end + ecmd = function() view.focus(win) end end end @@ -257,7 +234,7 @@ function M.open_file(mode, filename) if mode == 'preview' then if not found then M.set_target_win() end - M.win_focus() + view.focus() return end @@ -265,12 +242,8 @@ function M.open_file(mode, filename) return end - if not M.Tree.win_width_allow_resize then - local cur_win = api.nvim_get_current_win() - M.win_focus() - api.nvim_command('vertical resize '..M.Tree.win_width) - M.win_focus(cur_win) - end + view.resize() + if vim.g.nvim_tree_quit_on_open == 1 and mode ~= 'preview' then M.close() end @@ -285,56 +258,7 @@ function M.change_dir(foldername) api.nvim_command('cd '..foldername) M.Tree.entries = {} - M.init(false, M.Tree.bufnr ~= nil) -end - -local function set_mapping(buf, key, cb) - api.nvim_buf_set_keymap(buf, 'n', key, cb, { - nowait = true, noremap = true, silent = true - }) -end - -local function set_mappings() - if vim.g.nvim_tree_disable_keybindings == 1 then - return - end - - local buf = M.Tree.bufnr - local bindings = config.get_bindings() - - for key,cb in pairs(bindings) do - set_mapping(buf, key, cb) - end -end - -local function create_buf() - local options = { - buftype = 'nofile'; - modifiable = false; - } - - M.Tree.bufnr = api.nvim_create_buf(false, true) - api.nvim_buf_set_name(M.Tree.bufnr, M.Tree.buf_name) - api.nvim_buf_set_var(M.Tree.bufnr, "nvim_tree_buffer_ready", 1) - - for opt, val in pairs(options) do - api.nvim_buf_set_option(M.Tree.bufnr, opt, val) - end - set_mappings() -end - -local function create_win() - api.nvim_command("vsplit") - api.nvim_command("wincmd "..window_opts.side) - api.nvim_command("vertical resize "..M.Tree.win_width) - api.nvim_win_set_option(0, 'winhl', window_opts.winhl) -end - -function M.close() - if #api.nvim_list_wins() == 1 then - return vim.cmd ':q!' - end - api.nvim_win_close(M.Tree.winnr(), true) + M.init(false, true) end function M.set_target_win() @@ -344,25 +268,13 @@ end function M.open() M.set_target_win() - if not M.buf_exists() then - create_buf() - end - - create_win() - api.nvim_win_set_buf(M.Tree.winnr(), M.Tree.bufnr) - - for _, opt in pairs(M.Tree.options) do - api.nvim_command('setlocal '..opt) - end + view.open() if M.Tree.loaded then M.change_dir(vim.fn.getcwd()) end renderer.draw(M.Tree, not M.Tree.loaded) M.Tree.loaded = true - - api.nvim_buf_set_option(M.Tree.bufnr, 'filetype', M.Tree.buf_name) - api.nvim_command('setlocal '..window_opts.split_command) end function M.sibling(node, direction) @@ -401,7 +313,7 @@ function M.sibling(node, direction) 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}) + view.set_cursor({line, 0}) renderer.draw(M.Tree, true) end @@ -428,41 +340,6 @@ function M.parent_node(node, should_close) renderer.draw(M.Tree, true) end -function M.win_open() - return M.Tree.winnr() ~= nil -end - -function M.win_focus(winnr, open_if_closed) - local wnr = winnr or M.Tree.winnr() - - if vim.api.nvim_win_get_tabpage(wnr) ~= vim.api.nvim_win_get_tabpage(0) then - M.close() - M.open() - wnr = M.Tree.winnr() - elseif open_if_closed and not M.win_open() then - M.open() - end - - api.nvim_set_current_win(wnr) -end - -function M.buf_exists() - local status, exists = pcall(function () - return ( - M.Tree.bufnr ~= nil - and vim.api.nvim_buf_is_valid(M.Tree.bufnr) - and vim.api.nvim_buf_get_var(M.Tree.bufnr, "nvim_tree_buffer_ready") == 1 - and vim.fn.bufname(M.Tree.bufnr) == M.Tree.buf_name - ) - end) - - if not status then - return false - else - return exists - end -end - function M.toggle_ignored() pops.show_ignored = not pops.show_ignored return M.refresh_tree() diff --git a/lua/nvim-tree/populate.lua b/lua/nvim-tree/populate.lua index 4a9d69f0..07089327 100644 --- a/lua/nvim-tree/populate.lua +++ b/lua/nvim-tree/populate.lua @@ -14,7 +14,6 @@ local utils = require'nvim-tree.utils' 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 stat = luv.fs_stat(absolute_path) local handle = luv.fs_scandir(absolute_path) diff --git a/lua/nvim-tree/renderer.lua b/lua/nvim-tree/renderer.lua index b2ae55de..797a382f 100644 --- a/lua/nvim-tree/renderer.lua +++ b/lua/nvim-tree/renderer.lua @@ -1,5 +1,6 @@ local config = require'nvim-tree.config' local utils = require'nvim-tree.utils' +local view = require'nvim-tree.view' local api = vim.api @@ -315,14 +316,12 @@ end local M = {} -local function is_bufnr_valid(bufnr) - return vim.fn.bufexists(bufnr) == 1 and vim.fn.bufloaded(bufnr) == 1 -end - function M.draw(tree, reload) - if not is_bufnr_valid(tree.bufnr) then return end - api.nvim_buf_set_option(tree.bufnr, 'modifiable', true) - local cursor = api.nvim_win_get_cursor(tree.winnr()) + api.nvim_buf_set_option(view.View.bufnr, 'modifiable', true) + local cursor + if view.win_open() then + cursor = api.nvim_win_get_cursor(view.View.winnr) + end if reload then index = 0 lines = {} @@ -330,17 +329,18 @@ function M.draw(tree, reload) update_draw_data(tree, 0, {}) end - api.nvim_buf_set_lines(tree.bufnr, 0, -1, false, lines) - M.render_hl(tree.bufnr) - if #lines >= cursor[1] then - api.nvim_win_set_cursor(tree.winnr(), cursor) + api.nvim_buf_set_lines(view.View.bufnr, 0, -1, false, lines) + M.render_hl(view.View.bufnr) + if cursor and #lines >= cursor[1] then + api.nvim_win_set_cursor(view.View.winnr, cursor) + end + api.nvim_buf_set_option(view.View.bufnr, 'modifiable', false) + if cursor then + api.nvim_win_set_option(view.View.winnr, 'wrap', false) end - api.nvim_buf_set_option(tree.bufnr, 'modifiable', false) - api.nvim_win_set_option(tree.winnr(), 'wrap', false) end function M.render_hl(bufnr) - if not is_bufnr_valid(bufnr) then return end api.nvim_buf_clear_namespace(bufnr, namespace_id, 0, -1) for _, data in ipairs(hl) do api.nvim_buf_add_highlight(bufnr, namespace_id, data[1], data[2], data[3], data[4]) diff --git a/lua/nvim-tree/view.lua b/lua/nvim-tree/view.lua new file mode 100644 index 00000000..86182a60 --- /dev/null +++ b/lua/nvim-tree/view.lua @@ -0,0 +1,152 @@ +local a = vim.api + +local M = {} + +function M.nvim_tree_callback(callback_name) + return string.format(":lua require'nvim-tree'.on_keypress('%s')", callback_name) +end + +M.View = { + bufnr = nil, + winnr = nil, + width = 30, + side = 'left', + auto_resize = false, + winopts = { + relativenumber = false, + number = false, + list = false, + winfixwidth = true, + winfixheight = true, + foldenable = false, + spell = false, + signcolumn = 'yes', + foldmethod = 'manual', + foldcolumn = '0', + winhl = 'EndOfBuffer:NvimTreeEndOfBuffer,Normal:NvimTreeNormal,CursorLine:NvimTreeCursorLine,VertSplit:NvimTreeVertSplit' + }, + bufopts = { + swapfile = false, + buftype = 'nofile'; + modifiable = false; + filetype = 'NvimTree'; + }, + bindings = { + [""] = M.nvim_tree_callback("edit"), + ["o"] = M.nvim_tree_callback("edit"), + ["<2-LeftMouse>"] = M.nvim_tree_callback("edit"), + ["<2-RightMouse>"] = M.nvim_tree_callback("cd"), + [""] = M.nvim_tree_callback("cd"), + [""] = 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"), + ["a"] = M.nvim_tree_callback("create"), + ["d"] = M.nvim_tree_callback("remove"), + ["r"] = M.nvim_tree_callback("rename"), + [""] = M.nvim_tree_callback("full_rename"), + ["x"] = M.nvim_tree_callback("cut"), + ["c"] = M.nvim_tree_callback("copy"), + ["p"] = M.nvim_tree_callback("paste"), + ["[c"] = M.nvim_tree_callback("prev_git_item"), + ["]c"] = M.nvim_tree_callback("next_git_item"), + ["-"] = M.nvim_tree_callback("dir_up"), + ["q"] = M.nvim_tree_callback("close"), + } +} + +-- set user options and create tree buffer (should never be wiped) +function M.setup() + M.View.bindings = vim.tbl_extend( + 'force', + M.View.bindings, + vim.g.nvim_tree_bindings or {} + ) + M.View.auto_resize = vim.g.nvim_tree_auto_resize or M.View.auto_resize + M.View.side = vim.g.nvim_tree_side or M.View.side + M.View.width = vim.g.nvim_tree_width or M.View.width + + M.View.bufnr = a.nvim_create_buf(false, false) + for k, v in pairs(M.View.bufopts) do + a.nvim_buf_set_option(M.View.bufnr, k, v) + end + a.nvim_buf_set_name(M.View.bufnr, 'NvimTree') + + if not vim.g.nvim_tree_disable_keybindings then + for key, cb in pairs(M.View.bindings) do + a.nvim_buf_set_keymap(M.View.bufnr, 'n', key, cb, { noremap = true, silent = true }) + end + end +end + +function M.win_open() + return M.View.winnr ~= nil and a.nvim_win_is_valid(M.View.winnr) +end + +function M.set_cursor(opts) + if M.win_open() then + a.nvim_win_set_cursor(M.View.winnr, opts) + end +end + +function M.focus(winnr, open_if_closed) + local wnr = winnr or M.View.winnr + + if a.nvim_win_get_tabpage(wnr) ~= a.nvim_win_get_tabpage(0) then + M.close() + M.open() + wnr = M.View.winnr + elseif open_if_closed and not M.win_open() then + M.open() + end + + a.nvim_set_current_win(wnr) +end + +function M.resize() + if not M.View.auto_resize then + return + end + + a.nvim_win_set_width(M.View.winnr, M.View.width) +end + +local move_tbl = { + left = 'H', + right = 'L', + bottom = 'J', + top = 'K', +} + +function M.open() + a.nvim_command("vsplit") + local move_to = move_tbl[M.View.side] + a.nvim_command("wincmd "..move_to) + a.nvim_command("vertical resize "..M.View.width) + M.View.winnr = a.nvim_get_current_win() + for k, v in pairs(M.View.winopts) do + a.nvim_win_set_option(M.View.winnr, k, v) + end + a.nvim_win_set_buf(M.View.winnr, M.View.bufnr) + vim.cmd ":wincmd =" +end + +function M.close() + if not M.win_open() then return end + if #a.nvim_list_wins() == 1 then + return vim.cmd ':q!' + end + a.nvim_win_hide(M.View.winnr) +end + +return M