From 04354d617dd01ad8fbc5f27f33afa543509c7bd4 Mon Sep 17 00:00:00 2001 From: Gustavo Sampaio Date: Sat, 4 Jun 2022 18:24:15 -0300 Subject: [PATCH 1/6] feat: add file following support to the `files` section re #38 --- lua/sidebar-nvim/builtin/files.lua | 92 ++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/lua/sidebar-nvim/builtin/files.lua b/lua/sidebar-nvim/builtin/files.lua index b0c56d8..d09d69c 100644 --- a/lua/sidebar-nvim/builtin/files.lua +++ b/lua/sidebar-nvim/builtin/files.lua @@ -17,6 +17,7 @@ local icons = { local yanked_files = {} local cut_files = {} local open_directories = {} +local focused_file_path = nil local history = { position = 0, groups = {} } local trash_dir = luv.os_homedir() .. "/.local/share/Trash/files/" @@ -113,11 +114,19 @@ local function build_loclist(group, directory, level) selected = { text = " *", hl = "SidebarNvimFilesCut" } end + local name_hl = nil + if + focused_file_path ~= nil + and vim.fn.fnamemodify(node.path, ":.") == vim.fn.fnamemodify(focused_file_path, ":.") + then + name_hl = "SidebarNvimFocusedFile" + end + loclist_items[#loclist_items + 1] = { group = group, left = { { text = string.rep(" ", level) .. icon.text .. " ", hl = icon.hl }, - { text = node.name }, + { text = node.name, hl = name_hl }, selected, }, name = node.name, @@ -167,12 +176,58 @@ local function build_loclist(group, directory, level) return loclist_items end -local function update(group, directory) +local function update_current_dir(group, directory) local node = { path = directory, children = scan_dir(directory) } loclist:set_items(build_loclist(group, node, 0), { remove_groups = true }) end +local function update(_) + local cwd = vim.fn.getcwd() + local group = utils.shortest_path(cwd) + + open_directories[cwd] = true + + update_current_dir(group, cwd) +end + +local function focus(filename) + local parent_path + + if filename == nil then + filename = vim.fn.expand("%:p") + parent_path = vim.fn.expand("%:p:h") + else + filename = vim.fn.fnamemodify(filename, ":p") + parent_path = vim.fn.fnamemodify(filename, ":p:h") + end + + local cwd = vim.fn.getcwd() + local relative_path = vim.fn.fnamemodify(parent_path, ":.") + + -- if the file is at the root, then ignore parent folders + if cwd ~= relative_path then + local components = vim.split(relative_path, "/") + + local sub_path = cwd + + for _, component in ipairs(components) do + sub_path = sub_path .. "/" .. component + + -- check wether we are outside of the current dir, in this case we have nothing to show. exit + if vim.fn.isdirectory(sub_path) == 0 then + return + end + + open_directories[sub_path] = true + end + end + + focused_file_path = filename + + update() --P(path) +end + local function exec(group) for _, op in ipairs(group.operations) do op.exec() @@ -264,25 +319,23 @@ return { title = "Files", icon = config["files"].icon, setup = function(_) - vim.api.nvim_exec( - [[ - augroup sidebar_nvim_files_update - autocmd! - autocmd ShellCmdPost * lua require'sidebar-nvim.builtin.files'.update() - autocmd BufLeave term://* lua require'sidebar-nvim.builtin.files'.update() - augroup END - ]], - false + vim.api.nvim_create_augroup("sidebar_nvim_files_update", { clear = true }) + vim.api.nvim_create_autocmd({ "ShellCmdPost" }, { group = "sidebar_nvim_files_update", callback = update }) + vim.api.nvim_create_autocmd( + { "BufLeave" }, + { group = "sidebar_nvim_files_update", pattern = "term://*", callback = update } ) - end, - update = function(_) - local cwd = vim.fn.getcwd() - local group = utils.shortest_path(cwd) - - open_directories[cwd] = true - update(group, cwd) + if config["files"].follow then + vim.api.nvim_create_autocmd({ "BufEnter", "BufLeave" }, { + group = "sidebar_nvim_files_update", + callback = function(event) + focus(event.file) + end, + }) + end end, + update = update, draw = function(ctx) local lines = {} local hl = {} @@ -298,9 +351,12 @@ return { SidebarNvimFilesDirectory = "SidebarNvimSectionTitle", SidebarNvimFilesYanked = "SidebarNvimLabel", SidebarNvimFilesCut = "DiagnosticError", + SidebarNvimFocusedFile = "CursorLine", }, }, + focus = focus, + bindings = { -- delete ["d"] = function(line) From 6593b24612be2a4cd3ded1cd0e9639ede567dc67 Mon Sep 17 00:00:00 2001 From: Gustavo Sampaio Date: Sun, 5 Jun 2022 17:10:09 -0300 Subject: [PATCH 2/6] feat: move cursor to files - wip --- lua/sidebar-nvim.lua | 1 + lua/sidebar-nvim/builtin/files.lua | 16 ++++++++++ lua/sidebar-nvim/components/loclist.lua | 20 +++++++++++- lua/sidebar-nvim/lib.lua | 42 ++++++++++++++++++------- lua/sidebar-nvim/updater.lua | 9 +++--- lua/sidebar-nvim/utils_sections.lua | 21 +++++++++++++ 6 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 lua/sidebar-nvim/utils_sections.lua diff --git a/lua/sidebar-nvim.lua b/lua/sidebar-nvim.lua index 0d41e49..9421180 100644 --- a/lua/sidebar-nvim.lua +++ b/lua/sidebar-nvim.lua @@ -112,6 +112,7 @@ end -- @param opts table -- @param opts.section_index number -- @param opts.cursor_at_content boolean +-- @param opts.query table data sent to the section (if found) to refine the location function M.focus(opts) lib.focus(opts) end diff --git a/lua/sidebar-nvim/builtin/files.lua b/lua/sidebar-nvim/builtin/files.lua index d09d69c..b437b37 100644 --- a/lua/sidebar-nvim/builtin/files.lua +++ b/lua/sidebar-nvim/builtin/files.lua @@ -134,6 +134,7 @@ local function build_loclist(group, directory, level) type = node.type, parent = node.parent, node = node, + id = node.path, } elseif node.type == "directory" then local icon @@ -355,6 +356,21 @@ return { }, }, + query_line = function(query) + if not query or not query.filename then + return nil + end + + -- TODO: we are focusing, but when the parents are not open, we can't jump to that location + focus(query.filename) + + local filename = vim.fn.fnamemodify(vim.fn.expand(query.filename), ":p") + + local line = loclist:get_line_at_id(filename) + + return line + end, + focus = focus, bindings = { diff --git a/lua/sidebar-nvim/components/loclist.lua b/lua/sidebar-nvim/components/loclist.lua index 53f4814..0dfbf27 100644 --- a/lua/sidebar-nvim/components/loclist.lua +++ b/lua/sidebar-nvim/components/loclist.lua @@ -1,3 +1,4 @@ +local utils = require("sidebar-nvim.utils") local Component = require("sidebar-nvim.components.basic") local Loclist = {} @@ -31,8 +32,10 @@ function Loclist:new(o) o = vim.tbl_deep_extend("force", vim.deepcopy(Loclist.DEFAULT_OPTIONS), o or {}, { -- table(line_number -> group ref) _group_indexes = {}, - -- table(line__number -> item ref) + -- table(line_number -> item ref) _location_indexes = {}, + -- table(id -> line_number) + _line_by_ids = {}, -- used to keep the group list stable _group_keys = {}, }) @@ -56,6 +59,7 @@ end -- |--|- (string) item.left[n].text = "abc" -- |--|- (string) item.left[n].hl = "" -- |- (number) item.order items are sorted based on order within each group +-- |- (any) item.id a unique id across the whole loclist, this is used for fast lookups. The id must also be unique between groups. If no id is specified, a random id is assigned function Loclist:add_item(item) if not self.groups[item.group] then self.groups[item.group] = { is_closed = self.groups_initially_closed or false } @@ -65,6 +69,10 @@ function Loclist:add_item(item) table.insert(self._group_keys, item.group) end + if item.id == nil then + item.id = math.random(1000000) + end + local group_tbl = self.groups[item.group] group_tbl[#group_tbl + 1] = item @@ -169,6 +177,7 @@ function Loclist:draw_group(ctx, group_name, with_label, section_lines, section_ for _, item in ipairs(group) do self._location_indexes[#section_lines] = item + self._line_by_ids[item.id] = #section_lines local line = "" if with_label then @@ -241,6 +250,7 @@ end function Loclist:draw(ctx, section_lines, section_hl) self._group_indexes = {} self._location_indexes = {} + self._line_by_ids = {} if #self._group_keys == 1 and self.omit_single_group then self:draw_group(ctx, self._group_keys[1], false, section_lines, section_hl) @@ -260,6 +270,14 @@ function Loclist:get_location_at(line) return location end +-- returns the line of where the item id is placed +-- if there is no item with the requested id, return nil. +-- @see Loclist:add_item +-- @param (any) id +function Loclist:get_line_at_id(id) + return self._line_by_ids[id] +end + -- toggles the group open/close that is printed on line `line` -- if there is no group at `line`, then do nothing -- @param (number) line diff --git a/lua/sidebar-nvim/lib.lua b/lua/sidebar-nvim/lib.lua index 447420a..575674e 100644 --- a/lua/sidebar-nvim/lib.lua +++ b/lua/sidebar-nvim/lib.lua @@ -8,6 +8,7 @@ local updater = require("sidebar-nvim.updater") local config = require("sidebar-nvim.config") local bindings = require("sidebar-nvim.bindings") local utils = require("sidebar-nvim.utils") +local utils_sections = require("sidebar-nvim.utils_sections") local first_init_done = false @@ -115,13 +116,9 @@ end -- @param opts table -- @param opts.section_index number -- @param opts.cursor_at_content boolean +-- @param opts.query table data sent to the section (if found) to refine the location function M.focus(opts) - if view.is_win_open() then - local winnr = view.get_winnr() - view.focus(winnr) - else - M.open({ focus = true }) - end + local cursor = nil if opts and opts.section_index then local content_only = true @@ -130,11 +127,18 @@ function M.focus(opts) content_only = false end - local cursor = M.find_cursor_at_section_index(opts.section_index, { content_only = content_only }) + cursor = M.find_cursor_at_section_index(opts.section_index, { content_only = content_only, query = opts.query }) + end - if cursor then - api.nvim_win_set_cursor(0, cursor) - end + if view.is_win_open() then + local winnr = view.get_winnr() + view.focus(winnr) + else + M.open({ focus = true }) + end + + if cursor then + api.nvim_win_set_cursor(0, cursor) end end @@ -206,16 +210,30 @@ end -- @param index number -- @param opts table -- @param |- opts.content_only boolean whether the cursor should be placed at the first line of content or the section title +-- @param |- opts.query table data sent to the section (if found) to refine the location -- @return table with cursor {line: number, col: number} -- @return nil function M.find_cursor_at_section_index(index, opts) - opts = opts or { content_only = false } + opts = vim.tbl_deep_extend("force", { content_only = false, query = nil }, opts or {}) local cursor = { 0, 0 } for section_index, section_line_index in ipairs(M.State.section_line_indexes) do if section_index == index then - local start_line = get_start_line(opts.content_only, section_line_index) + local content_only = opts.content_only + local section_offset = 0 + + if opts.query then + content_only = true + + local section = utils_sections.get_section_at_index(section_index) + + if section and section.query_line then + section_offset = section.query_line(opts.query) or 0 + end + end + + local start_line = get_start_line(content_only, section_line_index) + section_offset cursor[1] = start_line return cursor diff --git a/lua/sidebar-nvim/updater.lua b/lua/sidebar-nvim/updater.lua index b982aa9..4127fa0 100644 --- a/lua/sidebar-nvim/updater.lua +++ b/lua/sidebar-nvim/updater.lua @@ -1,4 +1,5 @@ local utils = require("sidebar-nvim.utils") +local utils_sections = require("sidebar-nvim.utils_sections") local view = require("sidebar-nvim.view") local config = require("sidebar-nvim.config") local profile = require("sidebar-nvim.profile") @@ -17,8 +18,8 @@ function M.setup() local ctx = { width = view.get_width() } - for section_index, section_data in ipairs(config.sections) do - local section = utils.resolve_section(section_index, section_data) + for section_index, section in utils_sections.section_iterator() do + ctx.section_index = section_index if section then local hl_def = section.highlights or {} @@ -65,9 +66,7 @@ function M.draw() local draw_ctx = { width = view.View.width } - for section_index, section_data in pairs(config.sections) do - local section = utils.resolve_section(section_index, section_data) - + for section_index, section in utils_sections.section_iterator() do if section ~= nil then local section_lines = profile.run("draw.sections." .. section_index, section.draw, draw_ctx) local data = { lines = section_lines, section = section } diff --git a/lua/sidebar-nvim/utils_sections.lua b/lua/sidebar-nvim/utils_sections.lua new file mode 100644 index 0000000..c06cd45 --- /dev/null +++ b/lua/sidebar-nvim/utils_sections.lua @@ -0,0 +1,21 @@ +local utils = require("sidebar-nvim.utils") +local config = require("sidebar-nvim.config") + +local M = {} + +function M.section_iterator() + local i = 0 + return function() + i = i + 1 + if i <= #config.sections then + local section = utils.resolve_section(i, config.sections[i]) + return i, section + end + end +end + +function M.get_section_at_index(index) + return utils.resolve_section(index, config.sections[index]) +end + +return M From 62fa41866eed73f6ecf32d5f6c6bd46e41c0b35c Mon Sep 17 00:00:00 2001 From: Davysson Silva Date: Sun, 5 Jun 2022 20:09:38 -0300 Subject: [PATCH 3/6] fix: reset open_directories on focus --- lua/sidebar-nvim/builtin/files.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lua/sidebar-nvim/builtin/files.lua b/lua/sidebar-nvim/builtin/files.lua index b437b37..473ca50 100644 --- a/lua/sidebar-nvim/builtin/files.lua +++ b/lua/sidebar-nvim/builtin/files.lua @@ -194,6 +194,8 @@ end local function focus(filename) local parent_path + -- reset the open directories + open_directories = {} if filename == nil then filename = vim.fn.expand("%:p") From dfb246d01a98d926ba839255abea64f3e769855e Mon Sep 17 00:00:00 2001 From: Gustavo Sampaio Date: Sun, 19 Jun 2022 11:51:50 -0300 Subject: [PATCH 4/6] fix: file tree not jumping if folders were not open --- lua/sidebar-nvim.lua | 1 + lua/sidebar-nvim/builtin/files.lua | 37 ++++++++++++++++------------- lua/sidebar-nvim/lib.lua | 38 +++++++++++++++--------------- plugin/sidebar-nvim.vim | 1 + 4 files changed, 41 insertions(+), 36 deletions(-) diff --git a/lua/sidebar-nvim.lua b/lua/sidebar-nvim.lua index b92a7be..99f5277 100644 --- a/lua/sidebar-nvim.lua +++ b/lua/sidebar-nvim.lua @@ -116,6 +116,7 @@ end -- @param opts.section_index number -- @param opts.cursor_at_content boolean -- @param opts.query table data sent to the section (if found) to refine the location +-- @param opts.section_line_offset number function M.focus(opts) lib.focus(opts) end diff --git a/lua/sidebar-nvim/builtin/files.lua b/lua/sidebar-nvim/builtin/files.lua index 473ca50..0fe882b 100644 --- a/lua/sidebar-nvim/builtin/files.lua +++ b/lua/sidebar-nvim/builtin/files.lua @@ -19,6 +19,8 @@ local cut_files = {} local open_directories = {} local focused_file_path = nil +local section_index = nil + local history = { position = 0, groups = {} } local trash_dir = luv.os_homedir() .. "/.local/share/Trash/files/" @@ -192,7 +194,9 @@ local function update(_) update_current_dir(group, cwd) end -local function focus(filename) +local function focus(filename, opts) + opts = vim.tbl_deep_extend("force", { move_cursor = false }, opts or {}) + local parent_path -- reset the open directories open_directories = {} @@ -201,6 +205,7 @@ local function focus(filename) filename = vim.fn.expand("%:p") parent_path = vim.fn.expand("%:p:h") else + filename = vim.fn.expand(filename) filename = vim.fn.fnamemodify(filename, ":p") parent_path = vim.fn.fnamemodify(filename, ":p:h") end @@ -228,7 +233,19 @@ local function focus(filename) focused_file_path = filename + if opts.move_cursor then + require("sidebar-nvim.lib").run_after_next_draw(function() + local line = loclist:get_line_at_id(filename) + require("sidebar-nvim.lib").focus({ + section_index = section_index, + cursor_at_content = true, + section_line_offset = line, + }) + end) + end + update() --P(path) + require("sidebar-nvim.lib").update() end local function exec(group) @@ -321,7 +338,8 @@ end return { title = "Files", icon = config["files"].icon, - setup = function(_) + setup = function(ctx) + section_index = ctx.section_index vim.api.nvim_create_augroup("sidebar_nvim_files_update", { clear = true }) vim.api.nvim_create_autocmd({ "ShellCmdPost" }, { group = "sidebar_nvim_files_update", callback = update }) vim.api.nvim_create_autocmd( @@ -358,21 +376,6 @@ return { }, }, - query_line = function(query) - if not query or not query.filename then - return nil - end - - -- TODO: we are focusing, but when the parents are not open, we can't jump to that location - focus(query.filename) - - local filename = vim.fn.fnamemodify(vim.fn.expand(query.filename), ":p") - - local line = loclist:get_line_at_id(filename) - - return line - end, - focus = focus, bindings = { diff --git a/lua/sidebar-nvim/lib.lua b/lua/sidebar-nvim/lib.lua index 575674e..afd890d 100644 --- a/lua/sidebar-nvim/lib.lua +++ b/lua/sidebar-nvim/lib.lua @@ -14,7 +14,7 @@ local first_init_done = false local M = {} -M.State = { section_line_indexes = {} } +M.State = { section_line_indexes = {}, after_draw_call_queue = {} } M.timer = nil @@ -33,6 +33,11 @@ local function loop() updater.draw() _redraw() + + for _, fn in ipairs(M.State.after_draw_call_queue) do + fn() + end + M.State.after_draw_call_queue = {} end local function _start_timer(should_delay) @@ -79,6 +84,10 @@ function M.update() _start_timer(true) end +function M.run_after_next_draw(fn) + table.insert(M.State.after_draw_call_queue, fn) +end + function M.open(opts) view.open(opts or { focus = false }) M.update() @@ -116,7 +125,7 @@ end -- @param opts table -- @param opts.section_index number -- @param opts.cursor_at_content boolean --- @param opts.query table data sent to the section (if found) to refine the location +-- @param opts.section_line_offset number function M.focus(opts) local cursor = nil @@ -127,11 +136,14 @@ function M.focus(opts) content_only = false end - cursor = M.find_cursor_at_section_index(opts.section_index, { content_only = content_only, query = opts.query }) + cursor = M.find_cursor_at_section_index( + opts.section_index, + { content_only = content_only, section_line_offset = opts.section_line_offset } + ) end if view.is_win_open() then - local winnr = view.get_winnr() + local winnr = view.get_winnr(nil) view.focus(winnr) else M.open({ focus = true }) @@ -210,30 +222,18 @@ end -- @param index number -- @param opts table -- @param |- opts.content_only boolean whether the cursor should be placed at the first line of content or the section title --- @param |- opts.query table data sent to the section (if found) to refine the location +-- @param |- opts.section_line_offset number -- @return table with cursor {line: number, col: number} -- @return nil function M.find_cursor_at_section_index(index, opts) - opts = vim.tbl_deep_extend("force", { content_only = false, query = nil }, opts or {}) + opts = vim.tbl_deep_extend("force", { content_only = false, section_line_offset = 0 }, opts or {}) local cursor = { 0, 0 } for section_index, section_line_index in ipairs(M.State.section_line_indexes) do if section_index == index then local content_only = opts.content_only - local section_offset = 0 - - if opts.query then - content_only = true - - local section = utils_sections.get_section_at_index(section_index) - - if section and section.query_line then - section_offset = section.query_line(opts.query) or 0 - end - end - - local start_line = get_start_line(content_only, section_line_index) + section_offset + local start_line = get_start_line(content_only, section_line_index) + opts.section_line_offset cursor[1] = start_line return cursor diff --git a/plugin/sidebar-nvim.vim b/plugin/sidebar-nvim.vim index 2978550..6e0e935 100644 --- a/plugin/sidebar-nvim.vim +++ b/plugin/sidebar-nvim.vim @@ -20,6 +20,7 @@ command! SidebarNvimClose lua require'sidebar-nvim'.close() command! SidebarNvimToggle lua require'sidebar-nvim'.toggle() command! SidebarNvimUpdate lua require'sidebar-nvim'.update() command! SidebarNvimFocus lua require'sidebar-nvim'.focus() +command! SidebarNvimFilesFind lua require'sidebar-nvim.builtin.files'.focus('%', { move_cursor = true }) command! -nargs=1 SidebarNvimResize lua require'sidebar-nvim'.resize() let &cpo = s:save_cpo From 16b3e0089b77e6cfa7a03489edcbdf0757134e93 Mon Sep 17 00:00:00 2001 From: Gustavo Sampaio Date: Sun, 19 Jun 2022 11:54:13 -0300 Subject: [PATCH 5/6] feat: improve after_draw_call to better handle errors --- lua/sidebar-nvim/lib.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/sidebar-nvim/lib.lua b/lua/sidebar-nvim/lib.lua index afd890d..91fc684 100644 --- a/lua/sidebar-nvim/lib.lua +++ b/lua/sidebar-nvim/lib.lua @@ -35,7 +35,10 @@ local function loop() _redraw() for _, fn in ipairs(M.State.after_draw_call_queue) do - fn() + local ret = pcall(fn) + if not ret then + utils.echo_warning("after_draw_call failed") + end end M.State.after_draw_call_queue = {} end From f9801773f2ba32049fe83d20a4d269f5e458478e Mon Sep 17 00:00:00 2001 From: Gustavo Sampaio Date: Sun, 19 Jun 2022 11:56:54 -0300 Subject: [PATCH 6/6] chore: fix linting --- lua/sidebar-nvim/components/loclist.lua | 1 - lua/sidebar-nvim/lib.lua | 1 - 2 files changed, 2 deletions(-) diff --git a/lua/sidebar-nvim/components/loclist.lua b/lua/sidebar-nvim/components/loclist.lua index 0dfbf27..4ebe0c6 100644 --- a/lua/sidebar-nvim/components/loclist.lua +++ b/lua/sidebar-nvim/components/loclist.lua @@ -1,4 +1,3 @@ -local utils = require("sidebar-nvim.utils") local Component = require("sidebar-nvim.components.basic") local Loclist = {} diff --git a/lua/sidebar-nvim/lib.lua b/lua/sidebar-nvim/lib.lua index 91fc684..a130f1a 100644 --- a/lua/sidebar-nvim/lib.lua +++ b/lua/sidebar-nvim/lib.lua @@ -8,7 +8,6 @@ local updater = require("sidebar-nvim.updater") local config = require("sidebar-nvim.config") local bindings = require("sidebar-nvim.bindings") local utils = require("sidebar-nvim.utils") -local utils_sections = require("sidebar-nvim.utils_sections") local first_init_done = false