diff --git a/lua/sidebar-nvim.lua b/lua/sidebar-nvim.lua index e47ca3c..99f5277 100644 --- a/lua/sidebar-nvim.lua +++ b/lua/sidebar-nvim.lua @@ -115,6 +115,8 @@ 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) lib.focus(opts) end diff --git a/lua/sidebar-nvim/builtin/files.lua b/lua/sidebar-nvim/builtin/files.lua index b0c56d8..0fe882b 100644 --- a/lua/sidebar-nvim/builtin/files.lua +++ b/lua/sidebar-nvim/builtin/files.lua @@ -17,6 +17,9 @@ local icons = { local yanked_files = {} 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/" @@ -113,11 +116,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, @@ -125,6 +136,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 @@ -167,12 +179,75 @@ 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, opts) + opts = vim.tbl_deep_extend("force", { move_cursor = false }, opts or {}) + + local parent_path + -- reset the open directories + open_directories = {} + + if filename == nil then + 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 + + 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 + + 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) for _, op in ipairs(group.operations) do op.exec() @@ -263,26 +338,25 @@ end 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 + 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( + { "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 +372,12 @@ return { SidebarNvimFilesDirectory = "SidebarNvimSectionTitle", SidebarNvimFilesYanked = "SidebarNvimLabel", SidebarNvimFilesCut = "DiagnosticError", + SidebarNvimFocusedFile = "CursorLine", }, }, + focus = focus, + bindings = { -- delete ["d"] = function(line) diff --git a/lua/sidebar-nvim/components/loclist.lua b/lua/sidebar-nvim/components/loclist.lua index 53f4814..4ebe0c6 100644 --- a/lua/sidebar-nvim/components/loclist.lua +++ b/lua/sidebar-nvim/components/loclist.lua @@ -31,8 +31,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 +58,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 +68,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 +176,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 +249,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 +269,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..a130f1a 100644 --- a/lua/sidebar-nvim/lib.lua +++ b/lua/sidebar-nvim/lib.lua @@ -13,7 +13,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 @@ -32,6 +32,14 @@ local function loop() updater.draw() _redraw() + + for _, fn in ipairs(M.State.after_draw_call_queue) do + local ret = pcall(fn) + if not ret then + utils.echo_warning("after_draw_call failed") + end + end + M.State.after_draw_call_queue = {} end local function _start_timer(should_delay) @@ -78,6 +86,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() @@ -115,13 +127,9 @@ end -- @param opts table -- @param opts.section_index number -- @param opts.cursor_at_content boolean +-- @param opts.section_line_offset number 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 +138,21 @@ 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, section_line_offset = opts.section_line_offset } + ) + end - if cursor then - api.nvim_win_set_cursor(0, cursor) - end + if view.is_win_open() then + local winnr = view.get_winnr(nil) + view.focus(winnr) + else + M.open({ focus = true }) + end + + if cursor then + api.nvim_win_set_cursor(0, cursor) end end @@ -206,16 +224,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.section_line_offset number -- @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, 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 start_line = get_start_line(opts.content_only, section_line_index) + local content_only = opts.content_only + local start_line = get_start_line(content_only, section_line_index) + opts.section_line_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 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