Skip to content

feat(#3119): Expose filter functions to the api #3121

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

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

gracepetryk
Copy link

@gracepetryk gracepetryk commented May 6, 2025

Addresses #3119. Filter methods have all been normalized to accept a single path variable so that they may be easily called during a call to custom. Results are cached based on the path argument during each render loop to avoid computing filters more than once if the filter is enabled and also called in custom.

This enables filter configurations that were not possible before, for example combining filters so that items are only filtered if they match both:

require('nvim-tree').setup({
  filters = {
    custom = function (path)
      local filter_api = require('nvim-tree.api').filters

      return filter_api.git_clean(path) and filter_api.no_buffer(path)
    end
  }
})

@alex-courtis
Copy link
Member

Nice work! I'll get to a proper review / test in the next couple of days.

@alex-courtis
Copy link
Member

Apologies for the delay, my time is limited right now.

This deserves considerable review and test time which I hope to have in the next few days.

@@ -23,7 +23,7 @@ local function search(search_dir, input_path)
local function iter(dir)
local realpath, path, name, stat, handle, _

local filter_status = explorer.filters:prepare()
explorer.filters:prepare()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great clean up! All the state is in filters now.

---@field private explorer Explorer
---@field private exclude_list string[] filters.exclude
---@field private ignore_list table<string, boolean> filters.custom string table
---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function
---@field protected status FilterStatus
---@field private filter_cache table<string, FILTER_REASON>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
---@field private filter_cache table<string, FILTER_REASON>
---@field private filter_cache table<string, FILTER_REASON>
---@field private custom fun(self: Filters, path: string): boolean
---@field private dotfile fun(self: Filters, path: string): boolean
---@field private git_ignored fun(self: Filters, path: string): boolean
---@field private git_clean fun(self: Filters, path: string): boolean
---@field private buf fun(self: Filters, path: string): boolean
---@field private bookmark fun(self: Filters, path: string): boolean

This is annoying, but it will keep the language server from complaining about inject-field

You could even wrap them in the constructor, although I'm not sure how clean that would look.

@alex-courtis
Copy link
Member

It's working nicely with a variety of composed filters and tests on path, with toggle_custom_filter working as expected.

Unfortunately this one's not updating until we manually refresh:

  filters = {
    enable = true,
    git_ignored = false,
    dotfiles = false,
    git_clean = false,
    no_buffer = false,
    no_bookmark = false,
    custom = function(path)
      return path:match(".*Makefile") and not api.filters.no_buffer(path)
    end,
    exclude = {},

no_buffer requires the Buf:filter_buffer_ event in Explorer to apply the filter, and it's only fired when no_buffer = true.

Possible solution: fire the event when the user has a custom function. That's not the best for performance, happy for any other ideas.

@alex-courtis
Copy link
Member

This wasn't you, but we need to change the description of the default mapping Hidden->Custom

  vim.keymap.set("n", "U",              api.tree.toggle_custom_filter,      opts("Toggle Filter: Hidden"))

---@param self Filters
---@param path string
---@return FILTER_REASON
---@diagnostic disable:invisible
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had no success in resolving it. I'll keep at it...

---@field private explorer Explorer
---@field private exclude_list string[] filters.exclude
---@field private ignore_list table<string, boolean> filters.custom string table
---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function
---@field protected status FilterStatus
---@field private filter_cache table<string, FILTER_REASON>
Copy link
Member

@alex-courtis alex-courtis May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
---@field private filter_cache table<string, FILTER_REASON>
---@field private filter_cache table<FILTER_REASON, table<string, boolean>>

@alex-courtis
Copy link
Member

I think we can go one step further with the custom function: pass a node instead of a path, allowing the user to see the type, stat, children etc.

All of the filters could take a node, for consistency and ease of use, as well as aiding any future builtins.

There is extra complexity: we need to supply the user with a sanitised node rather than the actual, as per Decorator. See their cached creation:
https://github.com/gracepetryk/nvim-tree.lua/blob/ea5097a1e2702b4827cb7380e7fa0bd6da87699c/lua/nvim-tree/renderer/builder.lua#L236-L238

Copy link
Member

@alex-courtis alex-courtis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking fantastic, very straight-forward for the user.

Please

Apologies for the volume of comments; this is a significant change.

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

Successfully merging this pull request may close these issues.

2 participants