echasnovski / mini.nvim

Library of 40+ independent Lua modules improving overall Neovim (version 0.8 and higher) experience with minimal effort
MIT License
5.01k stars 185 forks source link

Save and restore explorer branches (close to bookmarks) #928

Closed echasnovski closed 1 month ago

echasnovski commented 4 months ago

Contributing guidelines

Module(s)

mini.files

Description

Consider adding functions and mappings for saving and restoring explorer branches for quick navigation to known destinations.

Common problem during file system manipulations is the need to hhhhjjjllll in order to navigate back and forth between two directories. This can be solved by "save current branch under identifier x" and "restore branch under identifier x" actions. Similar to how built-in marks work inside and across files.

Saving branches (like { '/home/user/.config', '/home/user/.config/nvim' }) instead of single destination directory (like '/home/user/.config/nvim') seems to both provide more flexibility and be more aligned with internal implementation.

This can also solve the problem of #572 by saving predefined set of branches inside autocommand for MiniFilesExplorerOpen event.

Notes:

echasnovski commented 1 month ago

This is now resolved on latest main. The final feature set of built-in bookmark support is a result of several iterations:

MariaSolOs commented 1 month ago

Small suggestion for focusing a bookmark: It would be nice for there to also be a `mark keymap (as the familiar mark behavior has) that focuses the bookmark window (and maybe also jumps to the file where the bookmark was set).

echasnovski commented 4 weeks ago

Small suggestion for focusing a bookmark: It would be nice for there to also be a `mark keymap (as the familiar mark behavior has) that focuses the bookmark window (and maybe also jumps to the file where the bookmark was set).

Thanks for the suggestion!

This sounds reasonable to me only if having bookmark and built-in mark be as similar as possible. I am afraid, this is not the goal. If not accounting for that, having two mappings do the same thing sounds a bit of a stretch.

Same action under different LHS can be achieved by custom buffer-local mappings. The most robust way is to create own function to use as RHS in buffer-local mapping. Something like this:

local mark_goto = function()
  local ok, id = pcall(vim.fn.getcharstr)
  if not ok or id == '\27' or id == '' then return end
  local data = MiniFiles.get_explorer_state().bookmarks[id]
  if data == nil then return error('No bookmark with id ' .. vim.inspect(id)) end
  local path = vim.is_callable(data.path) and data.path() or data.path

  local state = MiniFiles.get_explorer_state()
  MiniFiles.set_bookmark('`', state.branch[state.depth_focus], { desc = 'Before latest ` jump' })

  MiniFiles.set_branch({ path })
end
local make_keymaps = function(args)
  vim.keymap.set('n', '`', mark_goto, { buffer = args.data.buf_id, desc = 'Go to bookmark #2' })
end
vim.api.nvim_create_autocmd('User', { pattern = 'MiniFilesBufferCreate', callback = make_keymaps })

A less robust method (because there is no guarantee that 'MiniFilesBufferCreate' will be executed after built-in mappings are created) is to duplicate the existing ' mapping:

local duplicate_mark_goto = function()
  local map_data = vim.fn.maparg("'", 'n', false, true)
  map_data.lhs, map_data.lhsraw, map_data.desc = '`', '`', 'Go to bookmark (also)'
  vim.fn.mapset(map_data)
end
vim.api.nvim_create_autocmd('User', { pattern = 'MiniFilesBufferCreate', callback = duplicate_mark_goto })

... (and maybe also jumps to the file where the bookmark was set)

This part is a bit tricky to implement on the user side, as it requires also keeping track of focused file system entry when bookmark was created. One of the bookmark iterations was with similar behavior (if preview was enabled) and I found it really confusing in action. Preserving the state just as it was before leaving seems to be the better default.

Do you have any particular workflow that makes this behavior easier?


Edit: added automatic creation of ` bookmark to point to the "before latest ` jump" path. Similar to how it is now done in built-in mark_goto action, but keeping them separate (i.e. not setting ' bookmark when jumping with `). Adjust this to fit personal taste.

MariaSolOs commented 4 weeks ago

@echasnovski Thanks for the suggestions! I understand if the ` idea goes beyond the minimality of the plugin.

Do you have any particular workflow that makes this behavior easier?

Are you referring to a case where I find bookmarking the file a better default? If so, I often deal with folders containing lots of files, often with very similar names, and scrolling through the explorer window is tedious. Bookmarking a specific file saves the scrolling, and also allows for multiple bookmarks in the same directory.

That is definitely a "nice to have" though, and I'm fine with living without it if introducing it adds unnecessary complexity to the plugin.

echasnovski commented 4 weeks ago

Are you referring to a case where I find bookmarking the file a better default? If so, I often deal with folders containing lots of files, often with very similar names, and scrolling through the explorer window is tedious. Bookmarking a specific file saves the scrolling, and also allows for multiple bookmarks in the same directory.

Ah, I see. I slightly misunderstood the scope of the suggestion. I'll think about supporting setting bookmark on a particular directory entry.

The slight problem with this feature is that it has to be done as a separate bookmark property (like fs_entry or something). Otherwise it becomes ambiguous in case of a directory (is directory itself a bookmark or its parent with with it as entry). I'll take a look.

echasnovski commented 3 weeks ago

@MariaSolOs, after thinking about it more, it still seems to be a better compromise to suggest users to implement this themselves. The main reason for this is mostly because immediately navigating to a specific file seems to indeed be only for "try navigating to a specific part of directory" use case.

Most of the parts to implement this are present: set_branch() to show the target directory and get_fs_entry() to find the target file (via iterating through buffer lines; might need to be done in a vim.schedule()d function).

MariaSolOs commented 3 weeks ago

@echasnovski that's fair. As said before, I don't want the suggestion to unnecessarily complicate mini.files. Thank you for the hints on how to implement it!