Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(picker): new file explorer Snacks.picker.explorer() #793

Merged
merged 28 commits into from
Jan 30, 2025
Merged

feat(picker): new file explorer Snacks.picker.explorer() #793

merged 28 commits into from
Jan 30, 2025

Conversation

folke
Copy link
Owner

@folke folke commented Jan 30, 2025

Description

File explorer picker.

Testing

{
  { "nvim-neo-tree/neo-tree.nvim", enabled = false },
  {
    "folke/snacks.nvim",
    keys = {
      { "<leader>e", function() Snacks.picker.explorer() end, desc = "Explorer Snacks" },
    },
    init = function()
      vim.api.nvim_create_autocmd("BufEnter", {
        group = vim.api.nvim_create_augroup("snacks_explorer_start_directory", { clear = true }),
        desc = "Start Snacks Explorer with directory",
        once = true,
        callback = function()
          local dir = vim.fn.argv(0) --[[@as string]]
          if dir ~= "" and vim.fn.isdirectory(dir) == 1 then
            Snacks.picker.explorer({ cwd = dir })
          end
        end,
      })
    end,
  },
}

Screenshots

image

folke added 26 commits January 30, 2025 10:34
…o focus (if anything) when showing the picker
…for the next render. Target clears when reached, or when finders finishes.
@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@dpetka2001 this part might cause issues:

close = function(p, _)
                vim.cmd("noh")
                p:close()
              end,

You're probably unaware but this will override the Snacks.win close action (which is different from picker actions that get wrapped into win actions)

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

However, even with that I still can't reproduce it.

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

Just tried it with a minimal repro and still can't reproduce.

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

Are you using a winbar plugin by any chance?

@dpetka2001
Copy link
Contributor

No winbar. Just tried a fresh LazyVim installation and it works there. Will dig in further to find out what's causing this, so please disregard for the time being until I manage to come up with new info or not.

@nikbrunner
Copy link

nikbrunner commented Jan 30, 2025

A question: Would it be possible that the Explorer Sidebar stays open after I select an item? I personally would like that. But I also can live without it. I have found the auto_close config option, which firstly I assumed was for this usecase, but the annotation told me, its for leaving the window open when navigating out of it:

This auto_close is set to false in the default config for the explorer.

---@field auto_close? boolean automatically close the picker when focusing another window (defaults to true)

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

Just added opts.jump.close which defaults to true for all, but not for the explorer.
Makes more sense to keep it open indeed.

@nikbrunner
Copy link

Thats awesome. Thank you 🙏

@dpetka2001
Copy link
Contributor

I found the culprit for being dropped into insert mode. It's the plugin barbar.nvim that I replaced bufferline.nvim with. Now need to find out the reason it happens. I don't know if that's what you meant by winbar plugin, but I thought you meant for example a plugin like dropbar that adds breadcrumbs in the winbar, that's why I said I didn't have any. And I only use barbar to show me the buffers just like bufferline did.

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

That's odd indeed! No idea what that plugin does or why it would cause issues.
Prompt buffers however really behave weird and different from normal buffers with regards to stopinsert.
I'm still doing a stopinsert, but that's actually not needed because Neovim does that automatically when you leave a prompt buffer.

I added the extra stopinserts to fix weird issues with cursor jumps being off by one mostly. and for some users that get put in INSERT mode.

Let me know if you do find anhything!

@JensPauwels
Copy link

Hey @folke,

Noticed an error in the explorer.

Steps to reproduce:

  1. Open explorer
  2. Close file window
  3. Open file
  4. Open explorer again
Screen.Recording.2025-01-30.at.22.07.45.mov

@nikbrunner
Copy link

nikbrunner commented Jan 30, 2025

Just added opts.jump.close which defaults to true for all, but not for the explorer. Makes more sense to keep it open indeed.

This works now, but now the chosen file doesnt open when picked from the list. It does open when searched for in the input.

Screen.Recording.2025-01-30.at.22.03.09.mp4

I also should find a way to disable the blink completion when inside that explorer input.. 🤔

Edit - Found a way:
https://github.com/nikbrunner/nbr.nvim/blob/244ec9317429a8efc253c2603324abc5a606a501/lua/nbr/specs/blink.lua?plain=1#L9-L11

@JensPauwels
Copy link

Just added opts.jump.close which defaults to true for all, but not for the explorer. Makes more sense to keep it open indeed.

This works now, but now the chosen file doesnt open when picked from the list. It does open when searched for in the input.

Screen.Recording.2025-01-30.at.22.03.09.mp4
I also should find a way to disable the blink completion when inside that explorer input.. 🤔

Indeed, would be nice to have an easy way to disable the blink completion. Had the same feeling

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

blink is disabled by default in prompt buffers and the snacks picker input is a prompt buffer, so if that's not working for you it means you probably changed your blink config.

@nikbrunner
Copy link

I did it like this. working fine for me now: https://github.com/nikbrunner/nbr.nvim/blob/244ec9317429a8efc253c2603324abc5a606a501/lua/nbr/specs/blink.lua?plain=1#L9-L11

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@JensPauwels fixed that issue with closing the last window.

@dpetka2001
Copy link
Contributor

I haven't been able to find out what's causing this. Will probably try taking another look tomorrow. For the time being I ended up defining my own close action for the explorer like this

explorer = {
            win = {
              input = {
                keys = {
                  ["<Esc>"] = { "my_close", mode = "i" },
                },
              },
            },
            actions = {
              my_close = function(picker)
                vim.cmd("stopinsert")
                picker.input.win:close()
              end,
            },
          },

Is this better than doing picker:close() like you mentioned in your previous post?

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@nikbrunner like I said blink excludes prompt buffers by default, so you can just not confiugure that setting.
See here https://cmp.saghen.dev/configuration/general.html

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@dpetka2001 does it also work if you change it to just picker:close()?

@JensPauwels
Copy link

JensPauwels commented Jan 30, 2025

Is there a way to disable opening a previous file in the tree explorer? (using )

Screen.Recording.2025-01-30.at.22.28.06.mov

@dpetka2001
Copy link
Contributor

dpetka2001 commented Jan 30, 2025

Yes, it does. I just did it like this because of what you mentioned in your previous comment about overwriting the default

close = function(p, _)
                vim.cmd("noh")
                p:close()
              end,

You're probably unaware but this will override the Snacks.win close action (which is different from picker actions that get wrapped into win actions)

And also in the other custom picker files_with_symbols I haven't seen any weird behavior with having p:close() there as well.

@fdschmidt93
Copy link

fdschmidt93 commented Jan 30, 2025

As someone who's also done something similar (in fact, sole user of tree-branch in telescope-file-browser.nvim for 2 years 😅) and is in the process of switching to Snacks I thought I might leave a few suggestions for additional actions/slight behavior changes (just opinionated ideas, please feel free to ignore).

  1. When being in the prompt, finding a directory, hitting enter could clear the prompt, open the directory node, and then jumping to the directory node in list window

I tried something locally like this

  confirm = function(picker)
    local state = M.get_state(picker)
    local item = picker:current()
    if not item then
      return
    elseif item.dir then
      state:toggle(item)
      if vim.api.nvim_get_current_win() == picker.input.win.win then
        picker.input:set("", "")
        vim.defer_fn(function()
          for idx, item_ in ipairs(picker.list.items) do
            if item_.file == item.file then
              picker.list:move(idx)
              break
            end
          end
        end, 100)
      end
    else
      picker:action("jump")
    end
  end,

though couldn't quite get it to work (it doesn't jump to correct node for some reason). My telescope expertise doesn't translate 1:1 to the Snacks picker yet I'm afraid ;) but maybe that idea is welcome as a default.

  1. Copying / moving files/dirs

The way I did this in my file-browser tree branch above is that a user

  • Select files/dirs
  • Search in prompt for directory (or for a file in a directory) to move or copy selections to (i.e., execute copy/move when target is 'selected'). From a quick glance that is not supported. I really liked this because it was fast.

That said, in my file browser the user practically speaking only interacted with the prompt as telescope didn't support interacting with the results (list) window. So it's quite a different UX.

  1. Incremental (recursively) unrolling / collapsing of (sub-)trees
    (I realize entering "." in prompt unrolls all trees, but this is more a QoL idea)
    When hovering a directory, ">" opens all directories within a directory, "<" does the reverse. Could also be global. Helps with quick targeted directory tree exploration.

Edit: here's an implementation for anyone curious

local function open_subdirs(picker)
  local state = require("snacks.picker.source.explorer").get_state(picker)
  local parent = picker:current().parent
  local file = parent.file
  for _, item_ in ipairs(picker.list.items) do
    if item_.parent and item_.parent.file == file then
      state:expand(item_.file)
    end
  end
  state:update {
    on_done = function()
      for item_, idx in picker:iter() do
        if item_.file == file then
          picker.list:view(idx)
          break
        end
      end
    end,
  }
end

local function collapse_subdirs(picker)
  local state = require("snacks.picker.source.explorer").get_state(picker)
  local item = picker:current()
  if not item then
    return
  end
  if item.dir then
    state.expanded[item.file] = nil
  end
  state.expanded[item.parent.file] = nil
  local file = item.parent.file
  state:update {
    on_done = function()
      for item_, idx in picker:iter() do
        if item_.file == file then
          picker.list:view(idx)
          break
        end
      end
    end,
  }
end
  1. Batch file renaming of selections in a separate modifiable buffer. Though I fully understand if that would be out of scope.

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@fdschmidt93 you can select multiple files with <Tab> or first visual select them and press <Tab>.
Then go to a dir and press m to move them there.

Good idea on that open of dirs from search. Will add

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@fdschmidt93 added in 605f745

@folke
Copy link
Owner Author

folke commented Jan 30, 2025

@dpetka2001 added that explicit close function, so your work-around should no longer be needed.
All very weird.
Has probably something to do with WinClosed events not being triggered recursively.
I added some work-arounds for that in the win class, but I can't account for all situations, especially not the weird ones from prompt buffers.

folke pushed a commit that referenced this pull request Jan 30, 2025
🤖 I have created a release *beep* *boop*
---


##
[2.17.0](v2.16.0...v2.17.0)
(2025-01-30)


### Features

* **picker.actions:** allow selecting the visual selection with
`&lt;Tab&gt;`
([96c76c6](96c76c6))
* **picker.explorer:** focus dir on confirm from search
([605f745](605f745))


### Bug Fixes

* **git:** basic support for git work trees
([d76d9aa](d76d9aa))
* **picker.preview:** properly refresh the preview after layout changes.
Fixes [#802](#802)
([47993f9](47993f9))
* **picker:** add proper close
([15a9411](15a9411))
* **picker:** make jumping work again...
([f40f338](f40f338))
* **picker:** show help for input / list window with `?`.
([87dab7e](87dab7e))
* **win:** properly handle closing the last window. Fixes
[#793](#793)
([18de5bb](18de5bb))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@UtsavBalar1231
Copy link

@folke can you expose an optional configuration for just showing the basename of the directory thats currently visiable on cwd.

screenshot_2025-01-31_11-35-15

This takes away way too much space, just the basename of directory should be enough. and maybe an explorer_help method to get some additional information about the cwd?.

If possible inserting a ../ on top just like other file_explorers would be nice.

@folke
Copy link
Owner Author

folke commented Jan 31, 2025

@UtsavBalar1231 that's because you disabled the tree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug: (picker) iskeyword not set in help pages feature: toggles/flags rework feature: Picker File Browser
7 participants