Compare commits

..

20 Commits

Author SHA1 Message Date
github-actions[bot]
ad0b95dee5 chore(master): release nvim-tree 1.6.0 (#2845)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-10 12:26:42 +10:00
Michael Härtl
466fbed3e4 fix(#2859): make sure window still exists when restoring options (#2863)
Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-08-10 12:21:48 +10:00
Mateusz Russak
15942df2bb refactor(#2827): multi instance nvim-tree.live-filter (#2849)
* feat(#2827): Multi Instance: Refactor: nvim-tree.live-filter

* refactor: all usages going through the explorer

* fix: api and filtration

* fix: style

* Update lua/nvim-tree/api.lua

Co-authored-by: Alexander Courtis <alex@courtis.org>

* docs: add missing live filter luadocs

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-08-10 12:02:13 +10:00
Everton Jr.
e25eb7fa83 feat(#2225): add renderer.hidden_display to show a summary of hidden files below the tree (#2856)
* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): consolidate doc

* fix: extra namespace added to avoid colision between right_align and full_name features

* feat(hidden_display): Allow fine grained rendering of hidden files in
a folder

* feat(hidden_display): update defaults in Builder to allow rendering

* feat(hidden_display): Rename opts function name for the feature

* feat(#2349): add "right_align" option for renderer.icons.*_placement (#2846)

* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): consolidate doc

* fix: extra namespace added to avoid colision between right_align and full_name features

* style: rename namespace_id

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>

* docs: update docs

* feat(hidden_display): Simplification and better performance by not sorting and grouping virtual lines

* Update doc/nvim-tree-lua.txt

Co-authored-by: Alexander Courtis <alex@courtis.org>

* style: hidden_stats is better

* docs: change to hidden_stats

* add separate namespace for virtual lines

* help: add highlight group

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-08-10 11:36:30 +10:00
Everton Jr.
48d0e82f94 feat(#2349): add "right_align" option for renderer.icons.*_placement (#2846)
* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): consolidate doc

* fix: extra namespace added to avoid colision between right_align and full_name features

* style: rename namespace_id

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-28 13:26:22 +10:00
Mateusz Russak
82ba116bbd refactor(#2829): multi instance nvim-tree.explorer.sorters (#2835)
* refactor: multi instance nvim-tree.explorer.sorters

* fix: linter errors

* fix: style

* fix: according to code review

* chore: removed comment

* fix: missing cfg params in sorters

* tidy following rebase

* tidy following rebase

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-27 13:54:40 +10:00
Mateusz Russak
908478a0e0 refactor(#2828): multi instance nvim-tree.explorer.filters (#2841)
* refactor(#2828): multi instance nvim-tree.explorer.filters

* fix: style

* fix: apply suggestions from code review

Co-authored-by: Alexander Courtis <alex@courtis.org>

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-27 13:29:27 +10:00
Mateusz Russak
1aa9852cad docs: removed entry about macos rename (#2848) 2024-07-27 12:58:02 +10:00
Alexander Courtis
4e396b2624 refactor(#2830): multi instance nvim-tree.marks (#2838)
refactor(#2380): multi instance nvim-tree.marks
2024-07-21 16:12:42 +10:00
Everton Jr.
48a9290757 feat: add renderer.highlight_hidden, renderer.icons.show.hidden and renderer.icons.hidden_placement for dotfile icons/highlights (#2840)
* feat(hidden_decorator): Allow hidden (dotfiles) to be highlighted, both icon and name (this not related to git highlights).

Better defaults

squashed

docs(hidden)

docs(hidden)

docs(hidden)

* fix(typo): small typo on hl groups

* feat(hidden_dotfile_highlight): make a file that has a dotfile parent be also a dotfile

* docs: update docs on hidden highlight

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-21 16:00:34 +10:00
Alexander Courtis
b2640685a8 Revert "feat(#2349): add "right_align" option for renderer.icons.*_placement (#2839)"
This reverts commit 1d629a5d3f.
2024-07-21 15:22:24 +10:00
Everton Jr.
1d629a5d3f feat(#2349): add "right_align" option for renderer.icons.*_placement (#2839)
* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

feat(icon_placement): Allow right_align icon_placemente for decorator using ext_marks nvim api

* feat(icon_placement): consolidate doc

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-21 14:49:10 +10:00
github-actions[bot]
f9ff00bc06 chore(master): release nvim-tree 1.5.0 (#2810)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-11 16:17:05 +10:00
Samuel Durante
abfd1d1b67 fix(#2813): macos: enable file renaming with changed capitalization (#2814)
* fix(#2813): enable file renaming in `nvim-tree` with changed capitalization

* fix(#2813): check if is macos

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-11 16:15:03 +10:00
Vladimir Levin
2ede0de67b feat(#2598): add api.tree.resize (#2811)
* feat(#2598): Implemented API `tree.resize`

* rely on  when resize

* Fix docs

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-11 15:15:40 +10:00
Epheien
12a9a995a4 fix(#2819): experimental.actions.open_file.relative_path issue following change directory (#2820)
fix issue with the description of epheien in #2819

Co-authored-by: eph <eph@MacBook-Pro.local>
2024-07-07 15:53:04 +10:00
dependabot[bot]
d1957d3472 chore(deps): bump nvim-neorocks/luarocks-tag-release from 5 to 7 (#2808)
Bumps [nvim-neorocks/luarocks-tag-release](https://github.com/nvim-neorocks/luarocks-tag-release) from 5 to 7.
- [Release notes](https://github.com/nvim-neorocks/luarocks-tag-release/releases)
- [Changelog](https://github.com/nvim-neorocks/luarocks-tag-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nvim-neorocks/luarocks-tag-release/compare/v5...v7)

---
updated-dependencies:
- dependency-name: nvim-neorocks/luarocks-tag-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-07 12:55:47 +10:00
Epheien
869c064721 feat(#2127): add experimental.actions.open_file.relative_path to open files with a relative path rather than absolute (#2805)
* temp workaround for issue #2803

* fix #2127 and #2803

* chore(#2127): read the configuration correctly

* feat(#2127): add help

* feat(#2127): normalise relative_path in config hierarchy

* feat(#2127): update help

---------

Co-authored-by: eph <eph@MacBook-Pro.local>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-07-07 12:51:43 +10:00
dependabot[bot]
74e94625b1 chore(deps): bump amannn/action-semantic-pull-request from 5.5.2 to 5.5.3 (#2812)
chore(deps): bump amannn/action-semantic-pull-request

Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 5.5.2 to 5.5.3.
- [Release notes](https://github.com/amannn/action-semantic-pull-request/releases)
- [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amannn/action-semantic-pull-request/compare/v5.5.2...v5.5.3)

---
updated-dependencies:
- dependency-name: amannn/action-semantic-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-07 11:23:00 +10:00
Alexander Courtis
8b2c5c678b feat(#2799): filesystem_watchers.ignore_dirs and git.disable_for_dirs may be functions (#2800)
feat(#2799): filesystem_watchers.ignore_dirs and git.disable_for_dirs may be functions
2024-06-23 11:44:45 +10:00
40 changed files with 1070 additions and 335 deletions

View File

@@ -10,7 +10,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: LuaRocks Upload - name: LuaRocks Upload
uses: nvim-neorocks/luarocks-tag-release@v5 uses: nvim-neorocks/luarocks-tag-release@v7
env: env:
LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
with: with:

View File

@@ -14,6 +14,6 @@ jobs:
semantic-pr-subject: semantic-pr-subject:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: amannn/action-semantic-pull-request@v5.5.2 - uses: amannn/action-semantic-pull-request@v5.5.3
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
/luals-out/ /luals-out/
/luals/ /luals/
# backup vim files
*~

View File

@@ -1,3 +1,3 @@
{ {
".": "1.4.0" ".": "1.6.0"
} }

View File

@@ -1,5 +1,36 @@
# Changelog # Changelog
## [1.6.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.5.0...nvim-tree-v1.6.0) (2024-08-10)
### Features
* **#2225:** add renderer.hidden_display to show a summary of hidden files below the tree ([#2856](https://github.com/nvim-tree/nvim-tree.lua/issues/2856)) ([e25eb7f](https://github.com/nvim-tree/nvim-tree.lua/commit/e25eb7fa83f7614bb23d762e91d2de44fcd7103b))
* **#2349:** add "right_align" option for renderer.icons.*_placement ([#2839](https://github.com/nvim-tree/nvim-tree.lua/issues/2839)) ([1d629a5](https://github.com/nvim-tree/nvim-tree.lua/commit/1d629a5d3f7d83d516494c221a2cfc079f43bc47))
* **#2349:** add "right_align" option for renderer.icons.*_placement ([#2846](https://github.com/nvim-tree/nvim-tree.lua/issues/2846)) ([48d0e82](https://github.com/nvim-tree/nvim-tree.lua/commit/48d0e82f9434691cc50d970898142a8c084a49d6))
* add renderer.highlight_hidden, renderer.icons.show.hidden and renderer.icons.hidden_placement for dotfile icons/highlights ([#2840](https://github.com/nvim-tree/nvim-tree.lua/issues/2840)) ([48a9290](https://github.com/nvim-tree/nvim-tree.lua/commit/48a92907575df1dbd7242975a04e98169cb3a115))
### Bug Fixes
* **#2859:** make sure window still exists when restoring options ([#2863](https://github.com/nvim-tree/nvim-tree.lua/issues/2863)) ([466fbed](https://github.com/nvim-tree/nvim-tree.lua/commit/466fbed3e4b61fcc23a48fe99de7bfa264a9fee8))
## [1.5.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.4.0...nvim-tree-v1.5.0) (2024-07-11)
### Features
* **#2127:** add experimental.actions.open_file.relative_path to open files with a relative path rather than absolute ([#2805](https://github.com/nvim-tree/nvim-tree.lua/issues/2805)) ([869c064](https://github.com/nvim-tree/nvim-tree.lua/commit/869c064721a6c2091f22c3541e8f0ff958361771))
* **#2598:** add api.tree.resize ([#2811](https://github.com/nvim-tree/nvim-tree.lua/issues/2811)) ([2ede0de](https://github.com/nvim-tree/nvim-tree.lua/commit/2ede0de67b47e89e2b4cb488ea3f58b8f5a8c90a))
* **#2799:** `filesystem_watchers.ignore_dirs` and `git.disable_for_dirs` may be functions ([#2800](https://github.com/nvim-tree/nvim-tree.lua/issues/2800)) ([8b2c5c6](https://github.com/nvim-tree/nvim-tree.lua/commit/8b2c5c678be4b49dff6a2df794877000113fd77b))
* **#2799:** filesystem_watchers.ignore_dirs and git.disable_for_dirs may be functions ([8b2c5c6](https://github.com/nvim-tree/nvim-tree.lua/commit/8b2c5c678be4b49dff6a2df794877000113fd77b))
### Bug Fixes
* **#2813:** macos: enable file renaming with changed capitalization ([#2814](https://github.com/nvim-tree/nvim-tree.lua/issues/2814)) ([abfd1d1](https://github.com/nvim-tree/nvim-tree.lua/commit/abfd1d1b6772540364743531cc0331e08a0027a9))
* **#2819:** experimental.actions.open_file.relative_path issue following change directory ([#2820](https://github.com/nvim-tree/nvim-tree.lua/issues/2820)) ([12a9a99](https://github.com/nvim-tree/nvim-tree.lua/commit/12a9a995a455d2c2466e47140663275365a5d2fc))
## [1.4.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.3...nvim-tree-v1.4.0) (2024-06-09) ## [1.4.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.3...nvim-tree-v1.4.0) (2024-06-09)
### Notice ### Notice

View File

@@ -423,11 +423,13 @@ Following is the default configuration. See |nvim-tree-opts| for details.
root_folder_label = ":~:s?$?/..?", root_folder_label = ":~:s?$?/..?",
indent_width = 2, indent_width = 2,
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" }, special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none",
symlink_destination = true, symlink_destination = true,
highlight_git = "none", highlight_git = "none",
highlight_diagnostics = "none", highlight_diagnostics = "none",
highlight_opened_files = "none", highlight_opened_files = "none",
highlight_modified = "none", highlight_modified = "none",
highlight_hidden = "none",
highlight_bookmarks = "none", highlight_bookmarks = "none",
highlight_clipboard = "name", highlight_clipboard = "name",
indent_markers = { indent_markers = {
@@ -454,6 +456,7 @@ Following is the default configuration. See |nvim-tree-opts| for details.
}, },
git_placement = "before", git_placement = "before",
modified_placement = "after", modified_placement = "after",
hidden_placement = "after",
diagnostics_placement = "signcolumn", diagnostics_placement = "signcolumn",
bookmarks_placement = "signcolumn", bookmarks_placement = "signcolumn",
padding = " ", padding = " ",
@@ -464,6 +467,7 @@ Following is the default configuration. See |nvim-tree-opts| for details.
folder_arrow = true, folder_arrow = true,
git = true, git = true,
modified = true, modified = true,
hidden = false,
diagnostics = true, diagnostics = true,
bookmarks = true, bookmarks = true,
}, },
@@ -472,6 +476,7 @@ Following is the default configuration. See |nvim-tree-opts| for details.
symlink = "", symlink = "",
bookmark = "󰆤", bookmark = "󰆤",
modified = "●", modified = "●",
hidden = "󰜌",
folder = { folder = {
arrow_closed = "", arrow_closed = "",
arrow_open = "", arrow_open = "",
@@ -620,7 +625,13 @@ Following is the default configuration. See |nvim-tree-opts| for details.
default_yes = false, default_yes = false,
}, },
}, },
experimental = {}, experimental = {
actions = {
open_file = {
relative_path = false,
},
},
},
log = { log = {
enable = false, enable = false,
truncate = false, truncate = false,
@@ -868,6 +879,49 @@ Number of spaces for an each tree nesting level. Minimum 1.
A list of filenames that gets highlighted with `NvimTreeSpecialFile`. A list of filenames that gets highlighted with `NvimTreeSpecialFile`.
Type: `table`, Default: `{ "Cargo.toml", "Makefile", "README.md", "readme.md", }` Type: `table`, Default: `{ "Cargo.toml", "Makefile", "README.md", "readme.md", }`
*nvim-tree.renderer.hidden_display*
Show a summary of hidden files below the tree using `NvimTreeHiddenDisplay
Type: `function | string`, Default: `"none"`
Possible string values are:
- `"none"`: Doesn't inform anything about hidden files.
- `"simple"`: Shows how many hidden files are in a folder.
- `"all"`: Shows how many files are hidden and the number of hidden
files per reason why they're hidden.
Example `"all"`:
If a folder has 14 hidden items for various reasons, the display might
show: >
(14 total git: 5, dotfile: 9)
<
If a function is provided, it receives a table `hidden_stats` where keys are
reasons and values are the count of hidden files for that reason.
The `hidden_stats` argument is structured as follows, where <num> is the
number of hidden files related to the field: >
hidden_stats = {
bookmark = <num>,
buf = <num>,
custom = <num>,
dotfile = <num>,
git = <num>,
live_filter = <num>,
}
<
Example of function that can be passed: >
function(hidden_stats)
local total_count = 0
for reason, count in pairs(hidden_stats) do
total_count = total_count + count
end
if total_count > 0 then
return "(" .. tostring(total_count) .. " hidden)"
end
return nil
end
<
*nvim-tree.renderer.symlink_destination* *nvim-tree.renderer.symlink_destination*
Whether to show the destination of the symlink. Whether to show the destination of the symlink.
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
@@ -898,6 +952,13 @@ Requires |nvim-tree.modified.enable|
Value can be `"none"`, `"icon"`, `"name"` or `"all"` Value can be `"none"`, `"icon"`, `"name"` or `"all"`
Type: `string`, Default `"none"` Type: `string`, Default `"none"`
*nvim-tree.renderer.highlight_hidden*
Highlight icons and/or names for hidden files (dotfiles) using the
`NvimTreeHiddenFileHL` highlight group.
Requires |nvim-tree.hidden.enable|
Value can be `"none"`, `"icon"`, `"name"` or `"all"`
Type: `string`, Default `"none"`
*nvim-tree.renderer.highlight_bookmarks* *nvim-tree.renderer.highlight_bookmarks*
Highlight bookmarked using the `NvimTreeBookmarkHL` group. Highlight bookmarked using the `NvimTreeBookmarkHL` group.
Value can be `"none"`, `"icon"`, `"name"` or `"all"` Value can be `"none"`, `"icon"`, `"name"` or `"all"`
@@ -936,7 +997,13 @@ Configuration options for tree indent markers.
Configuration options for icons. Configuration options for icons.
Icon order and sign column precedence: Icon order and sign column precedence:
git < modified < bookmarked < diagnostics git < hidden < modified < bookmarked < diagnostics
`renderer.icons.*_placement` options may be:
- `"before"` : before file/folder, after the file/folders icons
- `"after"` : after file/folder
- `"signcolumn"` : far left, requires |nvim-tree.view.signcolumn| enabled
- `"right_align"` : far right
*nvim-tree.renderer.icons.web_devicons* *nvim-tree.renderer.icons.web_devicons*
Configure optional plugin `"nvim-tree/nvim-web-devicons"` Configure optional plugin `"nvim-tree/nvim-web-devicons"`
@@ -966,27 +1033,23 @@ Icon order and sign column precedence:
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.git_placement* *nvim-tree.renderer.icons.git_placement*
Place where the git icons will be rendered. Git icons placement.
Can be `"after"` or `"before"` filename (after the file/folders icons)
or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled).
Type: `string`, Default: `"before"` Type: `string`, Default: `"before"`
*nvim-tree.renderer.icons.diagnostics_placement* *nvim-tree.renderer.icons.diagnostics_placement*
Place where the diagnostics icon will be rendered. Diganostic icon placement.
Can be `"after"` or `"before"` filename (after the file/folders icons)
or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled).
Type: `string`, Default: `"signcolumn"` Type: `string`, Default: `"signcolumn"`
*nvim-tree.renderer.icons.modified_placement* *nvim-tree.renderer.icons.modified_placement*
Place where the modified icon will be rendered. Modified icon placement.
Can be `"after"` or `"before"` filename (after the file/folders icons) Type: `string`, Default: `"after"`
or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled).
*nvim-tree.renderer.icons.hidden_placement*
Hidden icon placement.
Type: `string`, Default: `"after"` Type: `string`, Default: `"after"`
*nvim-tree.renderer.icons.bookmarks_placement* *nvim-tree.renderer.icons.bookmarks_placement*
Place where the bookmarks icon will be rendered. Bookmark icon placement.
Can be `"after"` or `"before"` filename (after the file/folders icons)
or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled).
Type: `string`, Default: `signcolumn` Type: `string`, Default: `signcolumn`
*nvim-tree.renderer.icons.padding* *nvim-tree.renderer.icons.padding*
@@ -999,7 +1062,7 @@ Icon order and sign column precedence:
*nvim-tree.renderer.icons.show* *nvim-tree.renderer.icons.show*
Configuration options for showing icon types. Configuration options for showing icon types.
Left to right order: file/folder, git, modified, diagnostics, bookmarked. Left to right order: file/folder, git, modified, hidden, diagnostics, bookmarked.
*nvim-tree.renderer.icons.show.file* *nvim-tree.renderer.icons.show.file*
Show an icon before the file name. Show an icon before the file name.
@@ -1024,6 +1087,11 @@ Icon order and sign column precedence:
Requires |modified.enable| `= true` Requires |modified.enable| `= true`
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.show.hidden*
Show a hidden icon, see |renderer.icons.hidden_placement|
Requires |hidden.enable| `= true`
Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.show.diagnostics* *nvim-tree.renderer.icons.show.diagnostics*
Show a diagnostics status icon, see |renderer.icons.diagnostics_placement| Show a diagnostics status icon, see |renderer.icons.diagnostics_placement|
Requires |diagnostics.enable| `= true` Requires |diagnostics.enable| `= true`
@@ -1051,6 +1119,10 @@ Icon order and sign column precedence:
Icon to display for modified files. Icon to display for modified files.
Type: `string`, Default: `"●"` Type: `string`, Default: `"●"`
*nvim-tree.renderer.icons.glyphs.hidden*
Icon to display for hidden files.
Type: `string`, Default: `"󰜌""`
*nvim-tree.renderer.icons.glyphs.folder* *nvim-tree.renderer.icons.glyphs.folder*
Glyphs for directories. Glyphs for directories.
Overridden by |nvim-tree.renderer.icons.web_devicons| if available. Overridden by |nvim-tree.renderer.icons.web_devicons| if available.
@@ -1170,8 +1242,9 @@ Only relevant when `git.show_on_dirs` is `true`.
*nvim-tree.git.disable_for_dirs* *nvim-tree.git.disable_for_dirs*
Disable git integration when git top-level matches these paths. Disable git integration when git top-level matches these paths.
May be relative, evaluated via |fnamemodify| `":p"` Strings may be relative, evaluated via |fnamemodify| `":p"`
Type: `table`, Default: `{}` Function is passed an absolute path and returns true for disable.
Type: `string[] | fun(path: string): boolean`, Default: `{}`
*nvim-tree.git.timeout* *nvim-tree.git.timeout*
Kills the git process after some time if it takes too long. Kills the git process after some time if it takes too long.
@@ -1333,10 +1406,12 @@ Idle milliseconds between filesystem change and action.
Type: `number`, Default: `50` (ms) Type: `number`, Default: `50` (ms)
*nvim-tree.filesystem_watchers.ignore_dirs* *nvim-tree.filesystem_watchers.ignore_dirs*
List of vim regex for absolute directory paths that will not be watched. List of vim regex for absolute directory paths that will not be watched or
Backslashes must be escaped e.g. `"my-project/\\.build$"`. See |string-match|. function returning whether a path should be ignored.
Strings must be backslash escaped e.g. `"my-proj/\\.build$"`. See |string-match|.
Function is passed an absolute path.
Useful when path is not in `.gitignore` or git integration is disabled. Useful when path is not in `.gitignore` or git integration is disabled.
Type: {string}, Default: `{}` Type: `string[] | fun(path: string): boolean`, Default: `{}`
============================================================================== ==============================================================================
5.13 OPTS: ACTIONS *nvim-tree-opts-actions* 5.13 OPTS: ACTIONS *nvim-tree-opts-actions*
@@ -1536,6 +1611,12 @@ Confirmation prompts.
Experimental features that may become default or optional functionality. Experimental features that may become default or optional functionality.
In the event of a problem please disable the experiment and raise an issue. In the event of a problem please disable the experiment and raise an issue.
*nvim-tree.experimental.actions.open_file.relative_path*
Buffers opened by nvim-tree will use with relative paths instead of
absolute.
Execute |:ls| to see the paths of all open buffers.
Type: `boolean`, Default: `false`
============================================================================== ==============================================================================
5.20 OPTS: LOG *nvim-tree-opts-log* 5.20 OPTS: LOG *nvim-tree-opts-log*
@@ -1655,6 +1736,22 @@ tree.focus() *nvim-tree-api.tree.focus()*
tree.reload() *nvim-tree-api.tree.reload()* tree.reload() *nvim-tree-api.tree.reload()*
Refresh the tree. Does nothing if closed. Refresh the tree. Does nothing if closed.
tree.resize({opts}) *nvim-tree-api.tree.resize()*
Resize the tree, persisting the new size.
Resets to |nvim-tree.view.width| when no {opts} provided.
See |:NvimTreeResize|
Parameters: ~
• {opts} (table) optional parameters
Options: ~
• {width} (table) new |nvim-tree.view.width| value
• {absolute} (number) set the width
• {relative} (number) increase or decrease the width
Only one option is supported, in the priority order above.
{absolute} and {relative} do nothing when {width} is a function.
tree.change_root({path}) *nvim-tree-api.tree.change_root()* tree.change_root({path}) *nvim-tree-api.tree.change_root()*
Change the tree's root to a path. Change the tree's root to a path.
@@ -2402,6 +2499,14 @@ Modified: >
NvimTreeModifiedIcon Type NvimTreeModifiedIcon Type
NvimTreeModifiedFileHL NvimTreeModifiedIcon NvimTreeModifiedFileHL NvimTreeModifiedIcon
NvimTreeModifiedFolderHL NvimTreeModifiedIcon NvimTreeModifiedFolderHL NvimTreeModifiedIcon
Hidden: >
NvimTreeModifiedIcon Conceal
NvimTreeModifiedFileHL NvimTreeHiddenIcon
NvimTreeModifiedFolderHL NvimTreeHiddenFileHL
<
Hidden Display: >
NvimTreeHiddenDisplay Conceal
< <
Opened: > Opened: >
NvimTreeOpenedHL Special NvimTreeOpenedHL Special
@@ -2629,10 +2734,6 @@ configurations for different types of prompts.
============================================================================== ==============================================================================
11. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific* 11. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific*
macOS
- Rename to different case is not possible when using a case insensitive file
system.
Windows WSL and PowerShell Windows WSL and PowerShell
- Trash is synchronized - Trash is synchronized
- Executable file detection is disabled as this is non-performant and can - Executable file detection is disabled as this is non-performant and can
@@ -2771,6 +2872,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree.diagnostics.show_on_open_dirs| |nvim-tree.diagnostics.show_on_open_dirs|
|nvim-tree.disable_netrw| |nvim-tree.disable_netrw|
|nvim-tree.experimental| |nvim-tree.experimental|
|nvim-tree.experimental.actions.open_file.relative_path|
|nvim-tree.filesystem_watchers.debounce_delay| |nvim-tree.filesystem_watchers.debounce_delay|
|nvim-tree.filesystem_watchers.enable| |nvim-tree.filesystem_watchers.enable|
|nvim-tree.filesystem_watchers.ignore_dirs| |nvim-tree.filesystem_watchers.ignore_dirs|
@@ -2817,10 +2919,12 @@ highlight group is not, hard linking as follows: >
|nvim-tree.renderer.add_trailing| |nvim-tree.renderer.add_trailing|
|nvim-tree.renderer.full_name| |nvim-tree.renderer.full_name|
|nvim-tree.renderer.group_empty| |nvim-tree.renderer.group_empty|
|nvim-tree.renderer.hidden_display|
|nvim-tree.renderer.highlight_bookmarks| |nvim-tree.renderer.highlight_bookmarks|
|nvim-tree.renderer.highlight_clipboard| |nvim-tree.renderer.highlight_clipboard|
|nvim-tree.renderer.highlight_diagnostics| |nvim-tree.renderer.highlight_diagnostics|
|nvim-tree.renderer.highlight_git| |nvim-tree.renderer.highlight_git|
|nvim-tree.renderer.highlight_hidden|
|nvim-tree.renderer.highlight_modified| |nvim-tree.renderer.highlight_modified|
|nvim-tree.renderer.highlight_opened_files| |nvim-tree.renderer.highlight_opened_files|
|nvim-tree.renderer.icons| |nvim-tree.renderer.icons|
@@ -2831,8 +2935,10 @@ highlight group is not, hard linking as follows: >
|nvim-tree.renderer.icons.glyphs.default| |nvim-tree.renderer.icons.glyphs.default|
|nvim-tree.renderer.icons.glyphs.folder| |nvim-tree.renderer.icons.glyphs.folder|
|nvim-tree.renderer.icons.glyphs.git| |nvim-tree.renderer.icons.glyphs.git|
|nvim-tree.renderer.icons.glyphs.hidden|
|nvim-tree.renderer.icons.glyphs.modified| |nvim-tree.renderer.icons.glyphs.modified|
|nvim-tree.renderer.icons.glyphs.symlink| |nvim-tree.renderer.icons.glyphs.symlink|
|nvim-tree.renderer.icons.hidden_placement|
|nvim-tree.renderer.icons.modified_placement| |nvim-tree.renderer.icons.modified_placement|
|nvim-tree.renderer.icons.padding| |nvim-tree.renderer.icons.padding|
|nvim-tree.renderer.icons.show| |nvim-tree.renderer.icons.show|
@@ -2842,6 +2948,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree.renderer.icons.show.folder| |nvim-tree.renderer.icons.show.folder|
|nvim-tree.renderer.icons.show.folder_arrow| |nvim-tree.renderer.icons.show.folder_arrow|
|nvim-tree.renderer.icons.show.git| |nvim-tree.renderer.icons.show.git|
|nvim-tree.renderer.icons.show.hidden|
|nvim-tree.renderer.icons.show.modified| |nvim-tree.renderer.icons.show.modified|
|nvim-tree.renderer.icons.symlink_arrow| |nvim-tree.renderer.icons.symlink_arrow|
|nvim-tree.renderer.icons.web_devicons| |nvim-tree.renderer.icons.web_devicons|
@@ -2985,6 +3092,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree-api.tree.is_visible()| |nvim-tree-api.tree.is_visible()|
|nvim-tree-api.tree.open()| |nvim-tree-api.tree.open()|
|nvim-tree-api.tree.reload()| |nvim-tree-api.tree.reload()|
|nvim-tree-api.tree.resize()|
|nvim-tree-api.tree.search_node()| |nvim-tree-api.tree.search_node()|
|nvim-tree-api.tree.toggle()| |nvim-tree-api.tree.toggle()|
|nvim-tree-api.tree.toggle_custom_filter()| |nvim-tree-api.tree.toggle_custom_filter()|

View File

@@ -9,7 +9,6 @@ local actions = require "nvim-tree.actions"
local legacy = require "nvim-tree.legacy" local legacy = require "nvim-tree.legacy"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local git = require "nvim-tree.git" local git = require "nvim-tree.git"
local filters = require "nvim-tree.explorer.filters"
local buffers = require "nvim-tree.buffers" local buffers = require "nvim-tree.buffers"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
@@ -210,7 +209,13 @@ local function setup_autocommands(opts)
create_nvim_tree_autocmd("BufReadPost", { create_nvim_tree_autocmd("BufReadPost", {
callback = function(data) callback = function(data)
-- update opened file buffers -- update opened file buffers
if (filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then local explorer = core.get_explorer()
if not explorer then
return
end
if
(explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function() utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer() actions.reloaders.reload_explorer()
end) end)
@@ -221,7 +226,13 @@ local function setup_autocommands(opts)
create_nvim_tree_autocmd("BufUnload", { create_nvim_tree_autocmd("BufUnload", {
callback = function(data) callback = function(data)
-- update opened file buffers -- update opened file buffers
if (filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then local explorer = core.get_explorer()
if not explorer then
return
end
if
(explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function() utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer() actions.reloaders.reload_explorer()
end) end)
@@ -387,11 +398,13 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
root_folder_label = ":~:s?$?/..?", root_folder_label = ":~:s?$?/..?",
indent_width = 2, indent_width = 2,
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" }, special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none",
symlink_destination = true, symlink_destination = true,
highlight_git = "none", highlight_git = "none",
highlight_diagnostics = "none", highlight_diagnostics = "none",
highlight_opened_files = "none", highlight_opened_files = "none",
highlight_modified = "none", highlight_modified = "none",
highlight_hidden = "none",
highlight_bookmarks = "none", highlight_bookmarks = "none",
highlight_clipboard = "name", highlight_clipboard = "name",
indent_markers = { indent_markers = {
@@ -418,6 +431,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
}, },
git_placement = "before", git_placement = "before",
modified_placement = "after", modified_placement = "after",
hidden_placement = "after",
diagnostics_placement = "signcolumn", diagnostics_placement = "signcolumn",
bookmarks_placement = "signcolumn", bookmarks_placement = "signcolumn",
padding = " ", padding = " ",
@@ -428,6 +442,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
folder_arrow = true, folder_arrow = true,
git = true, git = true,
modified = true, modified = true,
hidden = false,
diagnostics = true, diagnostics = true,
bookmarks = true, bookmarks = true,
}, },
@@ -436,6 +451,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
symlink = "", symlink = "",
bookmark = "󰆤", bookmark = "󰆤",
modified = "", modified = "",
hidden = "󰜌",
folder = { folder = {
arrow_closed = "", arrow_closed = "",
arrow_open = "", arrow_open = "",
@@ -584,7 +600,13 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
default_yes = false, default_yes = false,
}, },
}, },
experimental = {}, experimental = {
actions = {
open_file = {
relative_path = false,
},
},
},
log = { log = {
enable = false, enable = false,
truncate = false, truncate = false,
@@ -626,15 +648,22 @@ local ACCEPTED_TYPES = {
}, },
}, },
renderer = { renderer = {
hidden_display = { "function", "string" },
group_empty = { "boolean", "function" }, group_empty = { "boolean", "function" },
root_folder_label = { "function", "string", "boolean" }, root_folder_label = { "function", "string", "boolean" },
}, },
update_focused_file = { update_focused_file = {
exclude = { "function" }, exclude = { "function" },
}, },
git = {
disable_for_dirs = { "function" },
},
filters = { filters = {
custom = { "function" }, custom = { "function" },
}, },
filesystem_watchers = {
ignore_dirs = { "function" },
},
actions = { actions = {
open_file = { open_file = {
window_picker = { window_picker = {
@@ -653,17 +682,20 @@ local ACCEPTED_STRINGS = {
signcolumn = { "yes", "no", "auto" }, signcolumn = { "yes", "no", "auto" },
}, },
renderer = { renderer = {
hidden_display = { "none", "simple", "all" },
highlight_git = { "none", "icon", "name", "all" }, highlight_git = { "none", "icon", "name", "all" },
highlight_opened_files = { "none", "icon", "name", "all" }, highlight_opened_files = { "none", "icon", "name", "all" },
highlight_modified = { "none", "icon", "name", "all" }, highlight_modified = { "none", "icon", "name", "all" },
highlight_hidden = { "none", "icon", "name", "all" },
highlight_bookmarks = { "none", "icon", "name", "all" }, highlight_bookmarks = { "none", "icon", "name", "all" },
highlight_diagnostics = { "none", "icon", "name", "all" }, highlight_diagnostics = { "none", "icon", "name", "all" },
highlight_clipboard = { "none", "icon", "name", "all" }, highlight_clipboard = { "none", "icon", "name", "all" },
icons = { icons = {
git_placement = { "before", "after", "signcolumn" }, git_placement = { "before", "after", "signcolumn", "right_align" },
modified_placement = { "before", "after", "signcolumn" }, modified_placement = { "before", "after", "signcolumn", "right_align" },
diagnostics_placement = { "before", "after", "signcolumn" }, hidden_placement = { "before", "after", "signcolumn", "right_align" },
bookmarks_placement = { "before", "after", "signcolumn" }, diagnostics_placement = { "before", "after", "signcolumn", "right_align" },
bookmarks_placement = { "before", "after", "signcolumn", "right_align" },
}, },
}, },
help = { help = {
@@ -808,7 +840,6 @@ function M.setup(conf)
require("nvim-tree.view").setup(opts) require("nvim-tree.view").setup(opts)
require("nvim-tree.lib").setup(opts) require("nvim-tree.lib").setup(opts)
require("nvim-tree.renderer").setup(opts) require("nvim-tree.renderer").setup(opts)
require("nvim-tree.live-filter").setup(opts)
require("nvim-tree.marks").setup(opts) require("nvim-tree.marks").setup(opts)
require("nvim-tree.buffers").setup(opts) require("nvim-tree.buffers").setup(opts)
require("nvim-tree.help").setup(opts) require("nvim-tree.help").setup(opts)

View File

@@ -1,5 +1,4 @@
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local filters = require "nvim-tree.explorer.filters"
local find_file = require("nvim-tree.actions.finders.find-file").fn local find_file = require("nvim-tree.actions.finders.find-file").fn
local M = {} local M = {}
@@ -9,6 +8,11 @@ local M = {}
---@return string|nil ---@return string|nil
local function search(search_dir, input_path) local function search(search_dir, input_path)
local realpaths_searched = {} local realpaths_searched = {}
local explorer = core.get_explorer()
if not explorer then
return
end
if not search_dir then if not search_dir then
return return
@@ -19,7 +23,7 @@ local function search(search_dir, input_path)
local function iter(dir) local function iter(dir)
local realpath, path, name, stat, handle, _ local realpath, path, name, stat, handle, _
local filter_status = filters.prepare() local filter_status = explorer.filters:prepare()
handle, _ = vim.loop.fs_scandir(dir) handle, _ = vim.loop.fs_scandir(dir)
if not handle then if not handle then
@@ -42,7 +46,7 @@ local function search(search_dir, input_path)
break break
end end
if not filters.should_filter(path, stat, filter_status) then if not explorer.filters:should_filter(path, stat, filter_status) then
if string.find(path, "/" .. input_path .. "$") then if string.find(path, "/" .. input_path .. "$") then
return path return path
end end

View File

@@ -30,13 +30,25 @@ local function err_fmt(from, to, reason)
return string.format("Cannot rename %s -> %s: %s", from, to, reason) return string.format("Cannot rename %s -> %s: %s", from, to, reason)
end end
local function rename_file_exists(node, to)
if not utils.is_macos then
return utils.file_exists(to)
end
if string.lower(node) == string.lower(to) then
return false
end
return utils.file_exists(to)
end
---@param node Node ---@param node Node
---@param to string ---@param to string
function M.rename(node, to) function M.rename(node, to)
local notify_from = notify.render_path(node.absolute_path) local notify_from = notify.render_path(node.absolute_path)
local notify_to = notify.render_path(to) local notify_to = notify.render_path(to)
if utils.file_exists(to) then if rename_file_exists(notify_from, notify_to) then
notify.warn(err_fmt(notify_from, notify_to, "file already exists")) notify.warn(err_fmt(notify_from, notify_to, "file already exists"))
return return
end end
@@ -65,7 +77,7 @@ function M.rename(node, to)
notify.warn(err_fmt(notify_from, notify_to, err)) notify.warn(err_fmt(notify_from, notify_to, err))
return return
end end
elseif not utils.file_exists(path_to_create) then elseif not rename_file_exists(notify_from, path_to_create) then
local success = vim.loop.fs_mkdir(path_to_create, 493) local success = vim.loop.fs_mkdir(path_to_create, 493)
if not success then if not success then
notify.error("Could not create folder " .. notify.render_path(path_to_create)) notify.error("Could not create folder " .. notify.render_path(path_to_create))

View File

@@ -167,6 +167,8 @@ local function pick_win_id()
if laststatus == 3 then if laststatus == 3 then
for _, id in ipairs(not_selectable) do for _, id in ipairs(not_selectable) do
-- Ensure window still exists at this point
if vim.api.nvim_win_is_valid(id) then
for opt, value in pairs(win_opts[id]) do for opt, value in pairs(win_opts[id]) do
if vim.fn.has "nvim-0.10" == 1 then if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value(opt, value, { win = id }) vim.api.nvim_set_option_value(opt, value, { win = id })
@@ -176,6 +178,7 @@ local function pick_win_id()
end end
end end
end end
end
vim.o.laststatus = laststatus vim.o.laststatus = laststatus
@@ -190,6 +193,9 @@ local function open_file_in_tab(filename)
if M.quit_on_open then if M.quit_on_open then
view.close() view.close()
end end
if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("tabe " .. vim.fn.fnameescape(filename)) vim.cmd("tabe " .. vim.fn.fnameescape(filename))
end end
@@ -197,6 +203,9 @@ local function drop(filename)
if M.quit_on_open then if M.quit_on_open then
view.close() view.close()
end end
if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("drop " .. vim.fn.fnameescape(filename)) vim.cmd("drop " .. vim.fn.fnameescape(filename))
end end
@@ -204,6 +213,9 @@ local function tab_drop(filename)
if M.quit_on_open then if M.quit_on_open then
view.close() view.close()
end end
if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("tab :drop " .. vim.fn.fnameescape(filename)) vim.cmd("tab :drop " .. vim.fn.fnameescape(filename))
end end
@@ -310,7 +322,21 @@ local function open_in_new_window(filename, mode)
end end
end end
local fname = utils.escape_special_chars(vim.fn.fnameescape(filename)) if (mode == "preview" or mode == "preview_no_picker") and view.View.float.enable then
-- ignore "WinLeave" autocmd on preview
-- because the registered "WinLeave"
-- will kill the floating window immediately
set_current_win_no_autocmd(target_winid, { "WinLeave", "BufEnter" })
else
set_current_win_no_autocmd(target_winid, { "BufEnter" })
end
local fname
if M.relative_path then
fname = utils.escape_special_chars(vim.fn.fnameescape(utils.path_relative(filename, vim.fn.getcwd())))
else
fname = utils.escape_special_chars(vim.fn.fnameescape(filename))
end
local command local command
if create_new_window then if create_new_window then
@@ -322,15 +348,6 @@ local function open_in_new_window(filename, mode)
command = { cmd = "edit", args = { fname } } command = { cmd = "edit", args = { fname } }
end end
if (mode == "preview" or mode == "preview_no_picker") and view.View.float.enable then
-- ignore "WinLeave" autocmd on preview
-- because the registered "WinLeave"
-- will kill the floating window immediately
set_current_win_no_autocmd(target_winid, { "WinLeave", "BufEnter" })
else
set_current_win_no_autocmd(target_winid, { "BufEnter" })
end
pcall(vim.api.nvim_cmd, command, { output = false }) pcall(vim.api.nvim_cmd, command, { output = false })
lib.set_target_win() lib.set_target_win()
end end
@@ -346,6 +363,9 @@ end
local function edit_in_current_buf(filename) local function edit_in_current_buf(filename)
require("nvim-tree.view").abandon_current_window() require("nvim-tree.view").abandon_current_window()
if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("keepalt keepjumps edit " .. vim.fn.fnameescape(filename)) vim.cmd("keepalt keepjumps edit " .. vim.fn.fnameescape(filename))
end end
@@ -402,6 +422,7 @@ end
function M.setup(opts) function M.setup(opts)
M.quit_on_open = opts.actions.open_file.quit_on_open M.quit_on_open = opts.actions.open_file.quit_on_open
M.resize_window = opts.actions.open_file.resize_window M.resize_window = opts.actions.open_file.resize_window
M.relative_path = opts.experimental.actions.open_file.relative_path
if opts.actions.open_file.window_picker.chars then if opts.actions.open_file.window_picker.chars then
opts.actions.open_file.window_picker.chars = tostring(opts.actions.open_file.window_picker.chars):upper() opts.actions.open_file.window_picker.chars = tostring(opts.actions.open_file.window_picker.chars):upper()
end end

View File

@@ -4,12 +4,14 @@ M.find_file = require "nvim-tree.actions.tree.find-file"
M.modifiers = require "nvim-tree.actions.tree.modifiers" M.modifiers = require "nvim-tree.actions.tree.modifiers"
M.open = require "nvim-tree.actions.tree.open" M.open = require "nvim-tree.actions.tree.open"
M.toggle = require "nvim-tree.actions.tree.toggle" M.toggle = require "nvim-tree.actions.tree.toggle"
M.resize = require "nvim-tree.actions.tree.resize"
function M.setup(opts) function M.setup(opts)
M.find_file.setup(opts) M.find_file.setup(opts)
M.modifiers.setup(opts) M.modifiers.setup(opts)
M.open.setup(opts) M.open.setup(opts)
M.toggle.setup(opts) M.toggle.setup(opts)
M.resize.setup(opts)
end end
return M return M

View File

@@ -1,8 +1,7 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local filters = require "nvim-tree.explorer.filters"
local reloaders = require "nvim-tree.actions.reloaders" local reloaders = require "nvim-tree.actions.reloaders"
local core = require "nvim-tree.core"
local M = {} local M = {}
local function reload() local function reload()
@@ -11,39 +10,56 @@ local function reload()
utils.focus_node_or_parent(node) utils.focus_node_or_parent(node)
end end
function M.custom() local function wrap_explorer(fn)
filters.config.filter_custom = not filters.config.filter_custom return function(...)
local explorer = core.get_explorer()
if explorer then
return fn(explorer, ...)
end
end
end
local function custom(explorer)
explorer.filters.config.filter_custom = not explorer.filters.config.filter_custom
reload() reload()
end end
function M.git_ignored() local function git_ignored(explorer)
filters.config.filter_git_ignored = not filters.config.filter_git_ignored explorer.filters.config.filter_git_ignored = not explorer.filters.config.filter_git_ignored
reload() reload()
end end
function M.git_clean() local function git_clean(explorer)
filters.config.filter_git_clean = not filters.config.filter_git_clean explorer.filters.config.filter_git_clean = not explorer.filters.config.filter_git_clean
reload() reload()
end end
function M.no_buffer() local function no_buffer(explorer)
filters.config.filter_no_buffer = not filters.config.filter_no_buffer explorer.filters.config.filter_no_buffer = not explorer.filters.config.filter_no_buffer
reload() reload()
end end
function M.no_bookmark() local function no_bookmark(explorer)
filters.config.filter_no_bookmark = not filters.config.filter_no_bookmark explorer.filters.config.filter_no_bookmark = not explorer.filters.config.filter_no_bookmark
reload() reload()
end end
function M.dotfiles() local function dotfiles(explorer)
filters.config.filter_dotfiles = not filters.config.filter_dotfiles explorer.filters.config.filter_dotfiles = not explorer.filters.config.filter_dotfiles
reload() reload()
end end
function M.enable() local function enable(explorer)
filters.config.enable = not filters.config.enable explorer.filters.config.enable = not explorer.filters.config.enable
reload() reload()
end end
M.custom = wrap_explorer(custom)
M.git_ignored = wrap_explorer(git_ignored)
M.git_clean = wrap_explorer(git_clean)
M.no_buffer = wrap_explorer(no_buffer)
M.no_bookmark = wrap_explorer(no_bookmark)
M.dotfiles = wrap_explorer(dotfiles)
M.enable = wrap_explorer(enable)
return M return M

View File

@@ -0,0 +1,51 @@
local view = require "nvim-tree.view"
local M = {}
---Resize the tree, persisting the new size.
---@param opts ApiTreeResizeOpts|nil
function M.fn(opts)
if opts == nil then
-- reset to config values
view.configure_width()
view.resize()
return
end
local options = opts or {}
local width_cfg = options.width
if width_cfg ~= nil then
view.configure_width(width_cfg)
view.resize()
return
end
if not view.is_width_determined() then
-- {absolute} and {relative} do nothing when {width} is a function.
return
end
local absolute = options.absolute
if type(absolute) == "number" then
view.resize(absolute)
return
end
local relative = options.relative
if type(relative) == "number" then
local relative_size = tostring(relative)
if relative > 0 then
relative_size = "+" .. relative_size
end
view.resize(relative_size)
return
end
end
function M.setup(opts)
M.config = opts or {}
end
return M

View File

@@ -1,12 +1,11 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local core = require "nvim-tree.core"
local view = require "nvim-tree.view" local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local actions = require "nvim-tree.actions" local actions = require "nvim-tree.actions"
local appearance_diagnostics = require "nvim-tree.appearance.diagnostics" local appearance_diagnostics = require "nvim-tree.appearance.diagnostics"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
local help = require "nvim-tree.help" local help = require "nvim-tree.help"
local live_filter = require "nvim-tree.live-filter"
local marks = require "nvim-tree.marks"
local marks_navigation = require "nvim-tree.marks.navigation" local marks_navigation = require "nvim-tree.marks.navigation"
local marks_bulk_delete = require "nvim-tree.marks.bulk-delete" local marks_bulk_delete = require "nvim-tree.marks.bulk-delete"
local marks_bulk_trash = require "nvim-tree.marks.bulk-trash" local marks_bulk_trash = require "nvim-tree.marks.bulk-trash"
@@ -43,9 +42,10 @@ local Api = {
diagnostics = {}, diagnostics = {},
} }
--- Do nothing when setup not called. --- Print error when setup not called.
--- f function to invoke --- f function to invoke
---@param f function ---@param f function
---@return fun(...) : any
local function wrap(f) local function wrap(f)
return function(...) return function(...)
if vim.g.NvimTreeSetup == 1 then if vim.g.NvimTreeSetup == 1 then
@@ -56,13 +56,13 @@ local function wrap(f)
end end
end end
---Inject the node as the first argument if absent. ---Inject the node as the first argument if present otherwise do nothing.
---@param fn function function to invoke ---@param fn function function to invoke
local function wrap_node(fn) local function wrap_node(fn)
return function(node, ...) return function(node, ...)
node = node or lib.get_node_at_cursor() node = node or lib.get_node_at_cursor()
if node then if node then
fn(node, ...) return fn(node, ...)
end end
end end
end end
@@ -72,10 +72,36 @@ end
local function wrap_node_or_nil(fn) local function wrap_node_or_nil(fn)
return function(node, ...) return function(node, ...)
node = node or lib.get_node_at_cursor() node = node or lib.get_node_at_cursor()
fn(node, ...) return fn(node, ...)
end end
end end
---Inject the explorer as the first argument if present otherwise do nothing.
---@param fn function function to invoke
---@return fun(...) : any
local function wrap_explorer(fn)
return function(...)
local explorer = core.get_explorer()
if explorer then
return fn(explorer, ...)
end
end
end
---Invoke a member's method on the singleton explorer.
---Print error when setup not called.
---@param explorer_member string explorer member name
---@param member_method string method name to invoke on member
---@return fun(...) : any
local function wrap_explorer_member(explorer_member, member_method)
return wrap(function(...)
local explorer = core.get_explorer()
if explorer then
return explorer[explorer_member][member_method](explorer[explorer_member], ...)
end
end)
end
---@class ApiTreeOpenOpts ---@class ApiTreeOpenOpts
---@field path string|nil path ---@field path string|nil path
---@field current_window boolean|nil default false ---@field current_window boolean|nil default false
@@ -100,6 +126,13 @@ Api.tree.close_in_this_tab = wrap(view.close_this_tab_only)
Api.tree.close_in_all_tabs = wrap(view.close_all_tabs) Api.tree.close_in_all_tabs = wrap(view.close_all_tabs)
Api.tree.reload = wrap(actions.reloaders.reload_explorer) Api.tree.reload = wrap(actions.reloaders.reload_explorer)
---@class ApiTreeResizeOpts
---@field width string|function|number|table|nil
---@field absolute number|nil
---@field relative number|nil
Api.tree.resize = wrap(actions.tree.resize.fn)
Api.tree.change_root = wrap(function(...) Api.tree.change_root = wrap(function(...)
require("nvim-tree").change_dir(...) require("nvim-tree").change_dir(...)
end) end)
@@ -231,16 +264,16 @@ Api.git.reload = wrap(actions.reloaders.reload_git)
Api.events.subscribe = events.subscribe Api.events.subscribe = events.subscribe
Api.events.Event = events.Event Api.events.Event = events.Event
Api.live_filter.start = wrap(live_filter.start_filtering) Api.live_filter.start = wrap_explorer_member("live_filter", "start_filtering")
Api.live_filter.clear = wrap(live_filter.clear_filter) Api.live_filter.clear = wrap_explorer_member("live_filter", "clear_filter")
Api.marks.get = wrap_node(marks.get_mark) Api.marks.get = wrap_node(wrap_explorer_member("marks", "get_mark"))
Api.marks.list = wrap(marks.get_marks) Api.marks.list = wrap_explorer_member("marks", "get_marks")
Api.marks.toggle = wrap_node(marks.toggle_mark) Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle_mark"))
Api.marks.clear = wrap(marks.clear_marks) Api.marks.clear = wrap_explorer_member("marks", "clear_marks")
Api.marks.bulk.delete = wrap(marks_bulk_delete.bulk_delete) Api.marks.bulk.delete = wrap_explorer(marks_bulk_delete.bulk_delete)
Api.marks.bulk.trash = wrap(marks_bulk_trash.bulk_trash) Api.marks.bulk.trash = wrap_explorer(marks_bulk_trash.bulk_trash)
Api.marks.bulk.move = wrap(marks_bulk_move.bulk_move) Api.marks.bulk.move = wrap_explorer(marks_bulk_move.bulk_move)
Api.marks.navigate.next = wrap(marks_navigation.next) Api.marks.navigate.next = wrap(marks_navigation.next)
Api.marks.navigate.prev = wrap(marks_navigation.prev) Api.marks.navigate.prev = wrap(marks_navigation.prev)
Api.marks.navigate.select = wrap(marks_navigation.select) Api.marks.navigate.select = wrap(marks_navigation.select)

View File

@@ -14,6 +14,7 @@ M.HIGHLIGHT_GROUPS = {
-- Standard -- Standard
{ group = "NvimTreeNormal", link = "Normal" }, { group = "NvimTreeNormal", link = "Normal" },
{ group = "NvimTreeNormalFloat", link = "NormalFloat" }, { group = "NvimTreeNormalFloat", link = "NormalFloat" },
{ group = "NvimTreeNormalFloatBorder", link = "FloatBorder" },
{ group = "NvimTreeNormalNC", link = "NvimTreeNormal" }, { group = "NvimTreeNormalNC", link = "NvimTreeNormal" },
{ group = "NvimTreeLineNr", link = "LineNr" }, { group = "NvimTreeLineNr", link = "LineNr" },
@@ -76,6 +77,14 @@ M.HIGHLIGHT_GROUPS = {
{ group = "NvimTreeModifiedFileHL", link = "NvimTreeModifiedIcon" }, { group = "NvimTreeModifiedFileHL", link = "NvimTreeModifiedIcon" },
{ group = "NvimTreeModifiedFolderHL", link = "NvimTreeModifiedFileHL" }, { group = "NvimTreeModifiedFolderHL", link = "NvimTreeModifiedFileHL" },
-- Hidden
{ group = "NvimTreeHiddenIcon", link = "Conceal" },
{ group = "NvimTreeHiddenFileHL", link = "NvimTreeHiddenIcon" },
{ group = "NvimTreeHiddenFolderHL", link = "NvimTreeHiddenFileHL" },
-- Hidden Display
{ group = "NvimTreeHiddenDisplay", link = "Conceal" },
-- Opened -- Opened
{ group = "NvimTreeOpenedHL", link = "Special" }, { group = "NvimTreeOpenedHL", link = "Special" },

View File

@@ -1,6 +1,5 @@
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
local explorer = require "nvim-tree.explorer" local explorer = require "nvim-tree.explorer"
local live_filter = require "nvim-tree.live-filter"
local view = require "nvim-tree.view" local view = require "nvim-tree.view"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
@@ -45,7 +44,7 @@ function M.get_nodes_starting_line()
if view.is_root_folder_visible(M.get_cwd()) then if view.is_root_folder_visible(M.get_cwd()) then
offset = offset + 1 offset = offset + 1
end end
if live_filter.filter then if TreeExplorer and TreeExplorer.live_filter.filter then
return offset + 1 return offset + 1
end end
return offset return offset

View File

@@ -16,6 +16,18 @@ M.ICON_PLACEMENT = {
signcolumn = 1, signcolumn = 1,
before = 2, before = 2,
after = 3, after = 3,
right_align = 4,
}
---Reason for filter in filter.lua
---@enum FILTER_REASON
M.FILTER_REASON = {
none = 0, -- It's not filtered
git = 1,
buf = 2,
dotfile = 4,
custom = 8,
bookmark = 16,
} }
return M return M

View File

@@ -2,11 +2,9 @@ local utils = require "nvim-tree.utils"
local builders = require "nvim-tree.explorer.node-builders" local builders = require "nvim-tree.explorer.node-builders"
local explorer_node = require "nvim-tree.explorer.node" local explorer_node = require "nvim-tree.explorer.node"
local git = require "nvim-tree.git" local git = require "nvim-tree.git"
local sorters = require "nvim-tree.explorer.sorters"
local filters = require "nvim-tree.explorer.filters"
local live_filter = require "nvim-tree.live-filter"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
local Watcher = require "nvim-tree.watcher" local Watcher = require "nvim-tree.watcher"
local M = {} local M = {}
@@ -15,10 +13,21 @@ local M = {}
---@param cwd string ---@param cwd string
---@param node Node ---@param node Node
---@param git_status table ---@param git_status table
local function populate_children(handle, cwd, node, git_status) ---@param parent Explorer
local function populate_children(handle, cwd, node, git_status, parent)
local node_ignored = explorer_node.is_git_ignored(node) local node_ignored = explorer_node.is_git_ignored(node)
local nodes_by_path = utils.bool_record(node.nodes, "absolute_path") local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
local filter_status = filters.prepare(git_status)
local filter_status = parent.filters:prepare(git_status)
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
bookmark = 0,
})
while true do while true do
local name, t = vim.loop.fs_scandir_next(handle) local name, t = vim.loop.fs_scandir_next(handle)
if not name then if not name then
@@ -30,8 +39,8 @@ local function populate_children(handle, cwd, node, git_status)
---@type uv.fs_stat.result|nil ---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs) local stat = vim.loop.fs_stat(abs)
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
if not filters.should_filter(abs, stat, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
local child = nil local child = nil
if t == "directory" and vim.loop.fs_access(abs, "R") then if t == "directory" and vim.loop.fs_access(abs, "R") then
child = builders.folder(node, abs, name, stat) child = builders.folder(node, abs, name, stat)
@@ -48,6 +57,12 @@ local function populate_children(handle, cwd, node, git_status)
nodes_by_path[child.absolute_path] = true nodes_by_path[child.absolute_path] = true
explorer_node.update_git_status(child, node_ignored, git_status) explorer_node.update_git_status(child, node_ignored, git_status)
end end
else
for reason, value in pairs(FILTER_REASON) do
if filter_reason == value then
node.hidden_stats[reason] = node.hidden_stats[reason] + 1
end
end
end end
log.profile_end(profile) log.profile_end(profile)
@@ -56,8 +71,9 @@ end
---@param node Node ---@param node Node
---@param status table ---@param status table
---@param parent Explorer
---@return Node[]|nil ---@return Node[]|nil
function M.explore(node, status) function M.explore(node, status, parent)
local cwd = node.link_to or node.absolute_path local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd) local handle = vim.loop.fs_scandir(cwd)
if not handle then if not handle then
@@ -66,7 +82,7 @@ function M.explore(node, status)
local profile = log.profile_start("explore init %s", node.absolute_path) local profile = log.profile_start("explore init %s", node.absolute_path)
populate_children(handle, cwd, node, status) populate_children(handle, cwd, node, status, parent)
local is_root = not node.parent local is_root = not node.parent
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1] local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
@@ -74,15 +90,15 @@ function M.explore(node, status)
local child_cwd = child_folder_only.link_to or child_folder_only.absolute_path local child_cwd = child_folder_only.link_to or child_folder_only.absolute_path
local child_status = git.load_project_status(child_cwd) local child_status = git.load_project_status(child_cwd)
node.group_next = child_folder_only node.group_next = child_folder_only
local ns = M.explore(child_folder_only, child_status) local ns = M.explore(child_folder_only, child_status, parent)
node.nodes = ns or {} node.nodes = ns or {}
log.profile_end(profile) log.profile_end(profile)
return ns return ns
end end
sorters.sort(node.nodes) parent.sorters:sort(node.nodes)
live_filter.apply_filter(node) parent.live_filter:apply_filter(node)
log.profile_end(profile) log.profile_end(profile)
return node.nodes return node.nodes

View File

@@ -1,16 +1,53 @@
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local marks = require "nvim-tree.marks" local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
local M = { ---@class Filters to handle all opts.filters and related API
---@field config table hydrated user opts.filters
---@field private explorer Explorer
---@field private exclude_list string[] filters.exclude
---@field private ignore_list string[] filters.custom string table
---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function
local Filters = {}
---@param opts table user options
---@param explorer Explorer
---@return Filters
function Filters:new(opts, explorer)
local o = {
explorer = explorer,
ignore_list = {}, ignore_list = {},
exclude_list = {}, exclude_list = opts.filters.exclude,
custom_function = nil, custom_function = nil,
config = {
enable = opts.filters.enable,
filter_custom = true,
filter_dotfiles = opts.filters.dotfiles,
filter_git_ignored = opts.filters.git_ignored,
filter_git_clean = opts.filters.git_clean,
filter_no_buffer = opts.filters.no_buffer,
filter_no_bookmark = opts.filters.no_bookmark,
},
} }
local custom_filter = opts.filters.custom
if type(custom_filter) == "function" then
o.custom_function = custom_filter
else
if custom_filter and #custom_filter > 0 then
for _, filter_name in pairs(custom_filter) do
o.ignore_list[filter_name] = true
end
end
end
setmetatable(o, self)
self.__index = self
return o
end
---@param path string ---@param path string
---@return boolean ---@return boolean
local function is_excluded(path) local function is_excluded(self, path)
for _, node in ipairs(M.exclude_list) do for _, node in ipairs(self.exclude_list) do
if path:match(node) then if path:match(node) then
return true return true
end end
@@ -22,7 +59,7 @@ end
---@param path string Absolute path ---@param path string Absolute path
---@param git_status table from prepare ---@param git_status table from prepare
---@return boolean ---@return boolean
local function git(path, git_status) local function git(self, path, git_status)
if type(git_status) ~= "table" or type(git_status.files) ~= "table" or type(git_status.dirs) ~= "table" then if type(git_status) ~= "table" or type(git_status.files) ~= "table" or type(git_status.dirs) ~= "table" then
return false return false
end end
@@ -33,12 +70,12 @@ local function git(path, git_status)
status = status or git_status.dirs.indirect[path] and git_status.dirs.indirect[path][1] status = status or git_status.dirs.indirect[path] and git_status.dirs.indirect[path][1]
-- filter ignored; overrides clean as they are effectively dirty -- filter ignored; overrides clean as they are effectively dirty
if M.config.filter_git_ignored and status == "!!" then if self.config.filter_git_ignored and status == "!!" then
return true return true
end end
-- filter clean -- filter clean
if M.config.filter_git_clean and not status then if self.config.filter_git_clean and not status then
return true return true
end end
@@ -49,8 +86,8 @@ end
---@param path string Absolute path ---@param path string Absolute path
---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 } ---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }
---@return boolean ---@return boolean
local function buf(path, bufinfo) local function buf(self, path, bufinfo)
if not M.config.filter_no_buffer or type(bufinfo) ~= "table" then if not self.config.filter_no_buffer or type(bufinfo) ~= "table" then
return false return false
end end
@@ -66,15 +103,15 @@ end
---@param path string ---@param path string
---@return boolean ---@return boolean
local function dotfile(path) local function dotfile(self, path)
return M.config.filter_dotfiles and utils.path_basename(path):sub(1, 1) == "." return self.config.filter_dotfiles and utils.path_basename(path):sub(1, 1) == "."
end end
---@param path string ---@param path string
---@param path_type string|nil filetype of path ---@param path_type string|nil filetype of path
---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files ---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files
local function bookmark(path, path_type, bookmarks) local function bookmark(self, path, path_type, bookmarks)
if not M.config.filter_no_bookmark then if not self.config.filter_no_bookmark then
return false return false
end end
-- if bookmark is empty, we should see a empty filetree -- if bookmark is empty, we should see a empty filetree
@@ -108,21 +145,21 @@ end
---@param path string ---@param path string
---@return boolean ---@return boolean
local function custom(path) local function custom(self, path)
if not M.config.filter_custom then if not self.config.filter_custom then
return false return false
end end
local basename = utils.path_basename(path) local basename = utils.path_basename(path)
-- filter user's custom function -- filter user's custom function
if M.custom_function and M.custom_function(path) then if self.custom_function and self.custom_function(path) then
return true return true
end end
-- filter custom regexes -- filter custom regexes
local relpath = utils.path_relative(path, vim.loop.cwd()) local relpath = utils.path_relative(path, vim.loop.cwd())
for pat, _ in pairs(M.ignore_list) do for pat, _ in pairs(self.ignore_list) do
if vim.fn.match(relpath, pat) ~= -1 or vim.fn.match(basename, pat) ~= -1 then if vim.fn.match(relpath, pat) ~= -1 or vim.fn.match(basename, pat) ~= -1 then
return true return true
end end
@@ -130,7 +167,7 @@ local function custom(path)
local idx = path:match ".+()%.[^.]+$" local idx = path:match ".+()%.[^.]+$"
if idx then if idx then
if M.ignore_list["*" .. string.sub(path, idx)] == true then if self.ignore_list["*" .. string.sub(path, idx)] == true then
return true return true
end end
end end
@@ -144,20 +181,23 @@ end
--- git_status: reference --- git_status: reference
--- bufinfo: empty unless no_buffer set: vim.fn.getbufinfo { buflisted = 1 } --- bufinfo: empty unless no_buffer set: vim.fn.getbufinfo { buflisted = 1 }
--- bookmarks: absolute paths to boolean --- bookmarks: absolute paths to boolean
function M.prepare(git_status) function Filters:prepare(git_status)
local status = { local status = {
git_status = git_status or {}, git_status = git_status or {},
bufinfo = {}, bufinfo = {},
bookmarks = {}, bookmarks = {},
} }
if M.config.filter_no_buffer then if self.config.filter_no_buffer then
status.bufinfo = vim.fn.getbufinfo { buflisted = 1 } status.bufinfo = vim.fn.getbufinfo { buflisted = 1 }
end end
for _, node in pairs(marks.get_marks()) do local explorer = require("nvim-tree.core").get_explorer()
if explorer then
for _, node in pairs(explorer.marks:get_marks()) do
status.bookmarks[node.absolute_path] = node.type status.bookmarks[node.absolute_path] = node.type
end end
end
return status return status
end end
@@ -167,47 +207,50 @@ end
---@param fs_stat uv.fs_stat.result|nil fs_stat of file ---@param fs_stat uv.fs_stat.result|nil fs_stat of file
---@param status table from prepare ---@param status table from prepare
---@return boolean ---@return boolean
function M.should_filter(path, fs_stat, status) function Filters:should_filter(path, fs_stat, status)
if not M.config.enable then if not self.config.enable then
return false return false
end end
-- exclusions override all filters -- exclusions override all filters
if is_excluded(path) then if is_excluded(self, path) then
return false return false
end end
return git(path, status.git_status) return git(self, path, status.git_status)
or buf(path, status.bufinfo) or buf(self, path, status.bufinfo)
or dotfile(path) or dotfile(self, path)
or custom(path) or custom(self, path)
or bookmark(path, fs_stat and fs_stat.type, status.bookmarks) or bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks)
end end
function M.setup(opts) --- Check if the given path should be filtered, and provide the reason why it was
M.config = { ---@param path string Absolute path
enable = opts.filters.enable, ---@param fs_stat uv.fs_stat.result|nil fs_stat of file
filter_custom = true, ---@param status table from prepare
filter_dotfiles = opts.filters.dotfiles, ---@return FILTER_REASON
filter_git_ignored = opts.filters.git_ignored, function Filters:should_filter_as_reason(path, fs_stat, status)
filter_git_clean = opts.filters.git_clean, if not self.config.enable then
filter_no_buffer = opts.filters.no_buffer, return FILTER_REASON.none
filter_no_bookmark = opts.filters.no_bookmark, end
}
M.ignore_list = {} if is_excluded(self, path) then
M.exclude_list = opts.filters.exclude return FILTER_REASON.none
end
local custom_filter = opts.filters.custom if git(self, path, status.git_status) then
if type(custom_filter) == "function" then return FILTER_REASON.git
M.custom_function = custom_filter elseif buf(self, path, status.bufinfo) then
return FILTER_REASON.buf
elseif dotfile(self, path) then
return FILTER_REASON.dotfile
elseif custom(self, path) then
return FILTER_REASON.custom
elseif bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks) then
return FILTER_REASON.bookmark
else else
if custom_filter and #custom_filter > 0 then return FILTER_REASON.none
for _, filter_name in pairs(custom_filter) do
M.ignore_list[filter_name] = true
end
end
end end
end end
return M return Filters

View File

@@ -2,6 +2,10 @@ local git = require "nvim-tree.git"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
local watch = require "nvim-tree.explorer.watch" local watch = require "nvim-tree.explorer.watch"
local explorer_node = require "nvim-tree.explorer.node" local explorer_node = require "nvim-tree.explorer.node"
local Filters = require "nvim-tree.explorer.filters"
local Marks = require "nvim-tree.marks"
local LiveFilter = require "nvim-tree.explorer.live-filter"
local Sorters = require "nvim-tree.explorer.sorters"
local M = {} local M = {}
@@ -12,6 +16,10 @@ M.reload = require("nvim-tree.explorer.reload").reload
---@field absolute_path string ---@field absolute_path string
---@field nodes Node[] ---@field nodes Node[]
---@field open boolean ---@field open boolean
---@field filters Filters
---@field live_filter LiveFilter
---@field sorters Sorter
---@field marks Marks
local Explorer = {} local Explorer = {}
Explorer.__index = Explorer Explorer.__index = Explorer
@@ -36,8 +44,12 @@ function Explorer.new(path)
absolute_path = path, absolute_path = path,
nodes = {}, nodes = {},
open = true, open = true,
marks = Marks:new(),
sorters = Sorters:new(M.config),
}, Explorer) }, Explorer)
explorer.watcher = watch.create_watcher(explorer) explorer.watcher = watch.create_watcher(explorer)
explorer.filters = Filters:new(M.config, explorer)
explorer.live_filter = LiveFilter:new(M.config, explorer)
explorer:_load(explorer) explorer:_load(explorer)
return explorer return explorer
end end
@@ -47,7 +59,7 @@ end
function Explorer:_load(node) function Explorer:_load(node)
local cwd = node.link_to or node.absolute_path local cwd = node.link_to or node.absolute_path
local git_status = git.load_project_status(cwd) local git_status = git.load_project_status(cwd)
M.explore(node, git_status) M.explore(node, git_status, self)
end end
---@param node Node ---@param node Node
@@ -68,10 +80,9 @@ function Explorer:destroy()
end end
function M.setup(opts) function M.setup(opts)
M.config = opts
require("nvim-tree.explorer.node").setup(opts) require("nvim-tree.explorer.node").setup(opts)
require("nvim-tree.explorer.explore").setup(opts) require("nvim-tree.explorer.explore").setup(opts)
require("nvim-tree.explorer.filters").setup(opts)
require("nvim-tree.explorer.sorters").setup(opts)
require("nvim-tree.explorer.reload").setup(opts) require("nvim-tree.explorer.reload").setup(opts)
require("nvim-tree.explorer.watch").setup(opts) require("nvim-tree.explorer.watch").setup(opts)
end end

View File

@@ -1,28 +1,51 @@
local view = require "nvim-tree.view" local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local Iterator = require "nvim-tree.iterators.node-iterator" local Iterator = require "nvim-tree.iterators.node-iterator"
local filters = require "nvim-tree.explorer.filters"
local M = { ---@class LiveFilter
---@field explorer Explorer
---@field prefix string
---@field always_show_folders boolean
---@field filter string
local LiveFilter = {}
---@param opts table
---@param explorer Explorer
function LiveFilter:new(opts, explorer)
local o = {
explorer = explorer,
prefix = opts.live_filter.prefix,
always_show_folders = opts.live_filter.always_show_folders,
filter = nil, filter = nil,
} }
setmetatable(o, self)
self.__index = self
return o
end
local function redraw() local function redraw()
require("nvim-tree.renderer").draw() require("nvim-tree.renderer").draw()
end end
---@param node_ Node|nil ---@param node_ Node|nil
local function reset_filter(node_) local function reset_filter(self, node_)
node_ = node_ or require("nvim-tree.core").get_explorer() node_ = node_ or self.explorer
if node_ == nil then if node_ == nil then
return return
end end
node_.hidden_stats = vim.tbl_deep_extend("force", node_.hidden_stats or {}, {
live_filter = 0,
})
Iterator.builder(node_.nodes) Iterator.builder(node_.nodes)
:hidden() :hidden()
:applier(function(node) :applier(function(node)
node.hidden = false node.hidden = false
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
live_filter = 0,
})
end) end)
:iterate() :iterate()
end end
@@ -30,7 +53,7 @@ end
local overlay_bufnr = 0 local overlay_bufnr = 0
local overlay_winnr = 0 local overlay_winnr = 0
local function remove_overlay() local function remove_overlay(self)
if view.View.float.enable and view.View.float.quit_on_focus_loss then if view.View.float.enable and view.View.float.quit_on_focus_loss then
-- return to normal nvim-tree float behaviour when filter window is closed -- return to normal nvim-tree float behaviour when filter window is closed
vim.api.nvim_create_autocmd("WinLeave", { vim.api.nvim_create_autocmd("WinLeave", {
@@ -49,27 +72,27 @@ local function remove_overlay()
overlay_bufnr = 0 overlay_bufnr = 0
overlay_winnr = 0 overlay_winnr = 0
if M.filter == "" then if self.filter == "" then
M.clear_filter() self:clear_filter()
end end
end end
---@param node Node ---@param node Node
---@return boolean ---@return boolean
local function matches(node) local function matches(self, node)
if not filters.config.enable then if not self.explorer.filters.config.enable then
return true return true
end end
local path = node.absolute_path local path = node.absolute_path
local name = vim.fn.fnamemodify(path, ":t") local name = vim.fn.fnamemodify(path, ":t")
return vim.regex(M.filter):match_str(name) ~= nil return vim.regex(self.filter):match_str(name) ~= nil
end end
---@param node_ Node|nil ---@param node_ Node|nil
function M.apply_filter(node_) function LiveFilter:apply_filter(node_)
if not M.filter or M.filter == "" then if not self.filter or self.filter == "" then
reset_filter(node_) reset_filter(self, node_)
return return
end end
@@ -79,6 +102,10 @@ function M.apply_filter(node_)
local filtered_nodes = 0 local filtered_nodes = 0
local nodes = node.group_next and { node.group_next } or node.nodes local nodes = node.group_next and { node.group_next } or node.nodes
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
live_filter = 0,
})
if nodes then if nodes then
for _, n in pairs(nodes) do for _, n in pairs(nodes) do
iterate(n) iterate(n)
@@ -88,31 +115,37 @@ function M.apply_filter(node_)
end end
end end
local has_nodes = nodes and (M.always_show_folders or #nodes > filtered_nodes) node.hidden_stats.live_filter = filtered_nodes
local ok, is_match = pcall(matches, node)
local has_nodes = nodes and (self.always_show_folders or #nodes > filtered_nodes)
local ok, is_match = pcall(matches, self, node)
node.hidden = not (has_nodes or (ok and is_match)) node.hidden = not (has_nodes or (ok and is_match))
end end
iterate(node_ or require("nvim-tree.core").get_explorer()) iterate(node_ or self.explorer)
end end
local function record_char() local function record_char(self)
vim.schedule(function() vim.schedule(function()
M.filter = vim.api.nvim_buf_get_lines(overlay_bufnr, 0, -1, false)[1] self.filter = vim.api.nvim_buf_get_lines(overlay_bufnr, 0, -1, false)[1]
M.apply_filter() self:apply_filter()
redraw() redraw()
end) end)
end end
local function configure_buffer_overlay() local function configure_buffer_overlay(self)
overlay_bufnr = vim.api.nvim_create_buf(false, true) overlay_bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_attach(overlay_bufnr, true, { vim.api.nvim_buf_attach(overlay_bufnr, true, {
on_lines = record_char, on_lines = function()
return record_char(self)
end,
}) })
vim.api.nvim_create_autocmd("InsertLeave", { vim.api.nvim_create_autocmd("InsertLeave", {
callback = remove_overlay, callback = function()
return remove_overlay(self)
end,
once = true, once = true,
}) })
@@ -120,17 +153,17 @@ local function configure_buffer_overlay()
end end
---@return integer ---@return integer
local function calculate_overlay_win_width() local function calculate_overlay_win_width(self)
local wininfo = vim.fn.getwininfo(view.get_winnr())[1] local wininfo = vim.fn.getwininfo(view.get_winnr())[1]
if wininfo then if wininfo then
return wininfo.width - wininfo.textoff - #M.prefix return wininfo.width - wininfo.textoff - #self.prefix
end end
return 20 return 20
end end
local function create_overlay() local function create_overlay(self)
if view.View.float.enable then if view.View.float.enable then
-- don't close nvim-tree float when focus is changed to filter window -- don't close nvim-tree float when focus is changed to filter window
vim.api.nvim_clear_autocmds { vim.api.nvim_clear_autocmds {
@@ -140,12 +173,12 @@ local function create_overlay()
} }
end end
configure_buffer_overlay() configure_buffer_overlay(self)
overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, { overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, {
col = 1, col = 1,
row = 0, row = 0,
relative = "cursor", relative = "cursor",
width = calculate_overlay_win_width(), width = calculate_overlay_win_width(self),
height = 1, height = 1,
border = "none", border = "none",
style = "minimal", style = "minimal",
@@ -157,29 +190,31 @@ local function create_overlay()
vim.api.nvim_buf_set_option(overlay_bufnr, "modifiable", true) ---@diagnostic disable-line: deprecated vim.api.nvim_buf_set_option(overlay_bufnr, "modifiable", true) ---@diagnostic disable-line: deprecated
end end
vim.api.nvim_buf_set_lines(overlay_bufnr, 0, -1, false, { M.filter }) vim.api.nvim_buf_set_lines(overlay_bufnr, 0, -1, false, { self.filter })
vim.cmd "startinsert" vim.cmd "startinsert"
vim.api.nvim_win_set_cursor(overlay_winnr, { 1, #M.filter + 1 }) vim.api.nvim_win_set_cursor(overlay_winnr, { 1, #self.filter + 1 })
end end
function M.start_filtering() function LiveFilter:start_filtering()
view.View.live_filter.prev_focused_node = require("nvim-tree.lib").get_node_at_cursor() view.View.live_filter.prev_focused_node = require("nvim-tree.lib").get_node_at_cursor()
M.filter = M.filter or "" self.filter = self.filter or ""
redraw() redraw()
local row = require("nvim-tree.core").get_nodes_starting_line() - 1 local row = require("nvim-tree.core").get_nodes_starting_line() - 1
local col = #M.prefix > 0 and #M.prefix - 1 or 1 local col = #self.prefix > 0 and #self.prefix - 1 or 1
view.set_cursor { row, col } view.set_cursor { row, col }
-- needs scheduling to let the cursor move before initializing the window -- needs scheduling to let the cursor move before initializing the window
vim.schedule(create_overlay) vim.schedule(function()
return create_overlay(self)
end)
end end
function M.clear_filter() function LiveFilter:clear_filter()
local node = require("nvim-tree.lib").get_node_at_cursor() local node = require("nvim-tree.lib").get_node_at_cursor()
local last_node = view.View.live_filter.prev_focused_node local last_node = view.View.live_filter.prev_focused_node
M.filter = nil self.filter = nil
reset_filter() reset_filter(self)
redraw() redraw()
if node then if node then
@@ -189,9 +224,4 @@ function M.clear_filter()
end end
end end
function M.setup(opts) return LiveFilter
M.prefix = opts.live_filter.prefix
M.always_show_folders = opts.live_filter.always_show_folders
end
return M

View File

@@ -128,6 +128,19 @@ function M.is_git_ignored(node)
return node and node.git_status ~= nil and node.git_status.file == "!!" return node and node.git_status ~= nil and node.git_status.file == "!!"
end end
---@param node Node
---@return boolean
function M.is_dotfile(node)
if node == nil then
return false
end
if node.is_dot or (node.name and (node.name:sub(1, 1) == ".")) or M.is_dotfile(node.parent) then
node.is_dot = true
return true
end
return false
end
---@param node Node ---@param node Node
function M.node_destroy(node) function M.node_destroy(node)
if not node then if not node then

View File

@@ -1,12 +1,10 @@
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local builders = require "nvim-tree.explorer.node-builders" local builders = require "nvim-tree.explorer.node-builders"
local explorer_node = require "nvim-tree.explorer.node" local explorer_node = require "nvim-tree.explorer.node"
local filters = require "nvim-tree.explorer.filters"
local sorters = require "nvim-tree.explorer.sorters"
local live_filter = require "nvim-tree.live-filter"
local git = require "nvim-tree.git" local git = require "nvim-tree.git"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
local NodeIterator = require "nvim-tree.iterators.node-iterator" local NodeIterator = require "nvim-tree.iterators.node-iterator"
local Watcher = require "nvim-tree.watcher" local Watcher = require "nvim-tree.watcher"
@@ -70,6 +68,10 @@ end
---@param node Node ---@param node Node
---@param git_status table ---@param git_status table
function M.reload(node, git_status) function M.reload(node, git_status)
local explorer = require("nvim-tree.core").get_explorer()
if not explorer then
return
end
local cwd = node.link_to or node.absolute_path local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd) local handle = vim.loop.fs_scandir(cwd)
if not handle then if not handle then
@@ -78,7 +80,7 @@ function M.reload(node, git_status)
local profile = log.profile_start("reload %s", node.absolute_path) local profile = log.profile_start("reload %s", node.absolute_path)
local filter_status = filters.prepare(git_status) local filter_status = explorer.filters:prepare(git_status)
if node.group_next then if node.group_next then
node.nodes = { node.group_next } node.nodes = { node.group_next }
@@ -90,6 +92,16 @@ function M.reload(node, git_status)
local node_ignored = explorer_node.is_git_ignored(node) local node_ignored = explorer_node.is_git_ignored(node)
---@type table<string, Node> ---@type table<string, Node>
local nodes_by_path = utils.key_by(node.nodes, "absolute_path") local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
-- To reset we must 'zero' everything that we use
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
bookmark = 0,
})
while true do while true do
local name, t = vim.loop.fs_scandir_next(handle) local name, t = vim.loop.fs_scandir_next(handle)
if not name then if not name then
@@ -100,7 +112,8 @@ function M.reload(node, git_status)
---@type uv.fs_stat.result|nil ---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs) local stat = vim.loop.fs_stat(abs)
if not filters.should_filter(abs, stat, filter_status) then local filter_reason = explorer.filters:should_filter_as_reason(abs, stat, filter_status)
if filter_reason == FILTER_REASON.none then
remain_childs[abs] = true remain_childs[abs] = true
-- Recreate node if type changes. -- Recreate node if type changes.
@@ -137,6 +150,12 @@ function M.reload(node, git_status)
n.fs_stat = stat n.fs_stat = stat
end end
end end
else
for reason, value in pairs(FILTER_REASON) do
if filter_reason == value then
node.hidden_stats[reason] = node.hidden_stats[reason] + 1
end
end
end end
end end
@@ -162,8 +181,8 @@ function M.reload(node, git_status)
return ns return ns
end end
sorters.sort(node.nodes) explorer.sorters:sort(node.nodes)
live_filter.apply_filter(node) explorer.live_filter:apply_filter(node)
log.profile_end(profile) log.profile_end(profile)
return node.nodes return node.nodes
end end

View File

@@ -1,12 +1,27 @@
local M = {}
local C = {} local C = {}
---@class Sorter
local Sorter = {}
function Sorter:new(opts)
local o = {}
setmetatable(o, self)
self.__index = self
o.config = vim.deepcopy(opts.sort)
if type(o.config.sorter) == "function" then
o.user = o.config.sorter
end
return o
end
--- Predefined comparator, defaulting to name --- Predefined comparator, defaulting to name
---@param sorter string as per options ---@param sorter string as per options
---@return function ---@return function
local function get_comparator(sorter) function Sorter:get_comparator(sorter)
return C[sorter] or C.name return function(a, b)
return (C[sorter] or C.name)(a, b, self.config)
end
end end
---Create a shallow copy of a portion of a list. ---Create a shallow copy of a portion of a list.
@@ -27,17 +42,17 @@ end
---@param a Node ---@param a Node
---@param b Node ---@param b Node
---@return boolean|nil ---@return boolean|nil
local function folders_or_files_first(a, b) local function folders_or_files_first(a, b, cfg)
if not (M.config.sort.folders_first or M.config.sort.files_first) then if not (cfg.folders_first or cfg.files_first) then
return return
end end
if not a.nodes and b.nodes then if not a.nodes and b.nodes then
-- file <> folder -- file <> folder
return M.config.sort.files_first return cfg.files_first
elseif a.nodes and not b.nodes then elseif a.nodes and not b.nodes then
-- folder <> file -- folder <> file
return not M.config.sort.files_first return not cfg.files_first
end end
end end
@@ -97,8 +112,8 @@ end
---Perform a merge sort using sorter option. ---Perform a merge sort using sorter option.
---@param t table nodes ---@param t table nodes
function M.sort(t) function Sorter:sort(t)
if C.user then if self.user then
local t_user = {} local t_user = {}
local origin_index = {} local origin_index = {}
@@ -115,9 +130,9 @@ function M.sort(t)
table.insert(origin_index, n) table.insert(origin_index, n)
end end
local predefined = C.user(t_user) local predefined = self.user(t_user)
if predefined then if predefined then
split_merge(t, 1, #t, get_comparator(predefined)) split_merge(t, 1, #t, self:get_comparator(predefined))
return return
end end
@@ -142,7 +157,7 @@ function M.sort(t)
split_merge(t, 1, #t, mini_comparator) -- sort by user order split_merge(t, 1, #t, mini_comparator) -- sort by user order
else else
split_merge(t, 1, #t, get_comparator(M.config.sort.sorter)) split_merge(t, 1, #t, self:get_comparator(self.config.sorter))
end end
end end
@@ -150,12 +165,12 @@ end
---@param b Node ---@param b Node
---@param ignorecase boolean|nil ---@param ignorecase boolean|nil
---@return boolean ---@return boolean
local function node_comparator_name_ignorecase_or_not(a, b, ignorecase) local function node_comparator_name_ignorecase_or_not(a, b, ignorecase, cfg)
if not (a and b) then if not (a and b) then
return true return true
end end
local early_return = folders_or_files_first(a, b) local early_return = folders_or_files_first(a, b, cfg)
if early_return ~= nil then if early_return ~= nil then
return early_return return early_return
end end
@@ -167,20 +182,20 @@ local function node_comparator_name_ignorecase_or_not(a, b, ignorecase)
end end
end end
function C.case_sensitive(a, b) function C.case_sensitive(a, b, cfg)
return node_comparator_name_ignorecase_or_not(a, b, false) return node_comparator_name_ignorecase_or_not(a, b, false, cfg)
end end
function C.name(a, b) function C.name(a, b, cfg)
return node_comparator_name_ignorecase_or_not(a, b, true) return node_comparator_name_ignorecase_or_not(a, b, true, cfg)
end end
function C.modification_time(a, b) function C.modification_time(a, b, cfg)
if not (a and b) then if not (a and b) then
return true return true
end end
local early_return = folders_or_files_first(a, b) local early_return = folders_or_files_first(a, b, cfg)
if early_return ~= nil then if early_return ~= nil then
return early_return return early_return
end end
@@ -199,17 +214,17 @@ function C.modification_time(a, b)
return last_modified_b <= last_modified_a return last_modified_b <= last_modified_a
end end
function C.suffix(a, b) function C.suffix(a, b, cfg)
if not (a and b) then if not (a and b) then
return true return true
end end
-- directories go first -- directories go first
local early_return = folders_or_files_first(a, b) local early_return = folders_or_files_first(a, b, cfg)
if early_return ~= nil then if early_return ~= nil then
return early_return return early_return
elseif a.nodes and b.nodes then elseif a.nodes and b.nodes then
return C.name(a, b) return C.name(a, b, cfg)
end end
-- dotfiles go second -- dotfiles go second
@@ -218,7 +233,7 @@ function C.suffix(a, b)
elseif a.name:sub(1, 1) ~= "." and b.name:sub(1, 1) == "." then elseif a.name:sub(1, 1) ~= "." and b.name:sub(1, 1) == "." then
return false return false
elseif a.name:sub(1, 1) == "." and b.name:sub(1, 1) == "." then elseif a.name:sub(1, 1) == "." and b.name:sub(1, 1) == "." then
return C.name(a, b) return C.name(a, b, cfg)
end end
-- unsuffixed go third -- unsuffixed go third
@@ -230,7 +245,7 @@ function C.suffix(a, b)
elseif a_suffix_ndx and not b_suffix_ndx then elseif a_suffix_ndx and not b_suffix_ndx then
return false return false
elseif not (a_suffix_ndx and b_suffix_ndx) then elseif not (a_suffix_ndx and b_suffix_ndx) then
return C.name(a, b) return C.name(a, b, cfg)
end end
-- finally, compare by suffixes -- finally, compare by suffixes
@@ -242,18 +257,18 @@ function C.suffix(a, b)
elseif not a_suffix and b_suffix then elseif not a_suffix and b_suffix then
return false return false
elseif a_suffix:lower() == b_suffix:lower() then elseif a_suffix:lower() == b_suffix:lower() then
return C.name(a, b) return C.name(a, b, cfg)
end end
return a_suffix:lower() < b_suffix:lower() return a_suffix:lower() < b_suffix:lower()
end end
function C.extension(a, b) function C.extension(a, b, cfg)
if not (a and b) then if not (a and b) then
return true return true
end end
local early_return = folders_or_files_first(a, b) local early_return = folders_or_files_first(a, b, cfg)
if early_return ~= nil then if early_return ~= nil then
return early_return return early_return
end end
@@ -267,18 +282,18 @@ function C.extension(a, b)
local a_ext = (a.extension or ""):lower() local a_ext = (a.extension or ""):lower()
local b_ext = (b.extension or ""):lower() local b_ext = (b.extension or ""):lower()
if a_ext == b_ext then if a_ext == b_ext then
return C.name(a, b) return C.name(a, b, cfg)
end end
return a_ext < b_ext return a_ext < b_ext
end end
function C.filetype(a, b) function C.filetype(a, b, cfg)
local a_ft = vim.filetype.match { filename = a.name } local a_ft = vim.filetype.match { filename = a.name }
local b_ft = vim.filetype.match { filename = b.name } local b_ft = vim.filetype.match { filename = b.name }
-- directories first -- directories first
local early_return = folders_or_files_first(a, b) local early_return = folders_or_files_first(a, b, cfg)
if early_return ~= nil then if early_return ~= nil then
return early_return return early_return
end end
@@ -292,19 +307,10 @@ function C.filetype(a, b)
-- same filetype or both nil, sort by name -- same filetype or both nil, sort by name
if a_ft == b_ft then if a_ft == b_ft then
return C.name(a, b) return C.name(a, b, cfg)
end end
return a_ft < b_ft return a_ft < b_ft
end end
function M.setup(opts) return Sorter
M.config = {}
M.config.sort = opts.sort
if type(M.config.sort.sorter) == "function" then
C.user = M.config.sort.sorter
end
end
return M

View File

@@ -40,11 +40,15 @@ local function is_folder_ignored(path)
end end
end end
if type(M.config.filesystem_watchers.ignore_dirs) == "table" then
for _, ignore_dir in ipairs(M.config.filesystem_watchers.ignore_dirs) do for _, ignore_dir in ipairs(M.config.filesystem_watchers.ignore_dirs) do
if vim.fn.match(path, ignore_dir) ~= -1 then if vim.fn.match(path, ignore_dir) ~= -1 then
return true return true
end end
end end
elseif type(M.config.filesystem_watchers.ignore_dirs) == "function" then
return M.config.filesystem_watchers.ignore_dirs(path)
end
return false return false
end end

View File

@@ -170,15 +170,21 @@ function M.get_toplevel(path)
if not toplevel or not git_dir then if not toplevel or not git_dir then
return nil return nil
end end
local toplevel_norm = vim.fn.fnamemodify(toplevel, ":p")
-- ignore disabled paths -- ignore disabled paths
if type(M.config.git.disable_for_dirs) == "table" then
for _, disabled_for_dir in ipairs(M.config.git.disable_for_dirs) do for _, disabled_for_dir in ipairs(M.config.git.disable_for_dirs) do
local toplevel_norm = vim.fn.fnamemodify(toplevel, ":p")
local disabled_norm = vim.fn.fnamemodify(disabled_for_dir, ":p") local disabled_norm = vim.fn.fnamemodify(disabled_for_dir, ":p")
if toplevel_norm == disabled_norm then if toplevel_norm == disabled_norm then
return nil return nil
end end
end end
elseif type(M.config.git.disable_for_dirs) == "function" then
if M.config.git.disable_for_dirs(toplevel_norm) then
return nil
end
end
M._toplevels_by_path[path] = toplevel M._toplevels_by_path[path] = toplevel
M._git_dirs_by_toplevel[toplevel] = git_dir M._git_dirs_by_toplevel[toplevel] = git_dir

View File

@@ -1,4 +1,3 @@
local marks = require "nvim-tree.marks"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local remove_file = require "nvim-tree.actions.fs.remove-file" local remove_file = require "nvim-tree.actions.fs.remove-file"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
@@ -10,12 +9,13 @@ local M = {
--- Delete nodes; each removal will be optionally notified --- Delete nodes; each removal will be optionally notified
---@param nodes Node[] ---@param nodes Node[]
local function do_delete(nodes) ---@param marks Marks
local function do_delete(marks, nodes)
for _, node in pairs(nodes) do for _, node in pairs(nodes) do
remove_file.remove(node) remove_file.remove(node)
end end
marks.clear_marks() marks:clear_marks()
if not M.config.filesystem_watchers.enable then if not M.config.filesystem_watchers.enable then
require("nvim-tree.actions.reloaders").reload_explorer() require("nvim-tree.actions.reloaders").reload_explorer()
@@ -23,8 +23,15 @@ local function do_delete(nodes)
end end
--- Delete marked nodes, optionally prompting --- Delete marked nodes, optionally prompting
function M.bulk_delete() ---@param explorer Explorer
local nodes = marks.get_marks() function M.bulk_delete(explorer)
if not explorer then
return
end
local marks = explorer.marks
local nodes = marks:get_marks()
if not nodes or #nodes == 0 then if not nodes or #nodes == 0 then
notify.warn "No bookmarksed to delete." notify.warn "No bookmarksed to delete."
return return
@@ -36,11 +43,11 @@ function M.bulk_delete()
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short) lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short)
utils.clear_prompt() utils.clear_prompt()
if item_short == "y" then if item_short == "y" then
do_delete(nodes) do_delete(marks, nodes)
end end
end) end)
else else
do_delete(nodes) do_delete(marks, nodes)
end end
end end

View File

@@ -1,4 +1,3 @@
local marks = require "nvim-tree.marks"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local rename_file = require "nvim-tree.actions.fs.rename-file" local rename_file = require "nvim-tree.actions.fs.rename-file"
@@ -9,8 +8,14 @@ local M = {
config = {}, config = {},
} }
function M.bulk_move() ---@param explorer Explorer
if #marks.get_marks() == 0 then function M.bulk_move(explorer)
if not explorer then
return
end
local marks = explorer.marks
if #marks:get_marks() == 0 then
notify.warn "No bookmarks to move." notify.warn "No bookmarks to move."
return return
end end
@@ -40,14 +45,14 @@ function M.bulk_move()
return return
end end
local nodes = marks.get_marks() local nodes = marks:get_marks()
for _, node in pairs(nodes) do for _, node in pairs(nodes) do
local head = vim.fn.fnamemodify(node.absolute_path, ":t") local head = vim.fn.fnamemodify(node.absolute_path, ":t")
local to = utils.path_join { location, head } local to = utils.path_join { location, head }
rename_file.rename(node, to) rename_file.rename(node, to)
end end
marks.clear_marks() marks:clear_marks()
if not M.config.filesystem_watchers.enable then if not M.config.filesystem_watchers.enable then
require("nvim-tree.actions.reloaders").reload_explorer() require("nvim-tree.actions.reloaders").reload_explorer()

View File

@@ -1,4 +1,3 @@
local marks = require "nvim-tree.marks"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local remove_file = require "nvim-tree.actions.fs.trash" local remove_file = require "nvim-tree.actions.fs.trash"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
@@ -14,12 +13,17 @@ local function do_trash(nodes)
for _, node in pairs(nodes) do for _, node in pairs(nodes) do
remove_file.remove(node) remove_file.remove(node)
end end
marks.clear_marks()
end end
function M.bulk_trash() ---@param explorer Explorer
local nodes = marks.get_marks() function M.bulk_trash(explorer)
if not explorer then
return
end
local marks = explorer.marks
local nodes = marks:get_marks()
if not nodes or #nodes == 0 then if not nodes or #nodes == 0 then
notify.warn "No bookmarks to trash." notify.warn "No bookmarks to trash."
return return
@@ -32,10 +36,12 @@ function M.bulk_trash()
utils.clear_prompt() utils.clear_prompt()
if item_short == "y" then if item_short == "y" then
do_trash(nodes) do_trash(nodes)
marks:clear_marks()
end end
end) end)
else else
do_trash(nodes) do_trash(nodes)
marks:clear_marks()
end end
end end

View File

@@ -1,63 +1,73 @@
local renderer = {} -- circular dependency local renderer = {} -- circular dependency
local NvimTreeMarks = {} ---@class Marks
---@field private marks Node[]
local Marks = {}
local M = {} ---@return Marks
function Marks:new()
local o = {}
setmetatable(o, self)
self.__index = self
---@class MinimalNode o.marks = {}
---@field absolute_path string
---@param node Node|MinimalNode return o
local function add_mark(node) end
NvimTreeMarks[node.absolute_path] = node
---@private
---@param node Node
function Marks:add_mark(node)
self.marks[node.absolute_path] = node
renderer.draw() renderer.draw()
end end
---@param node Node|MinimalNode ---@private
local function remove_mark(node) ---@param node Node
NvimTreeMarks[node.absolute_path] = nil function Marks:remove_mark(node)
self.marks[node.absolute_path] = nil
renderer.draw() renderer.draw()
end end
---@param node Node|MinimalNode ---@param node Node
function M.toggle_mark(node) function Marks:toggle_mark(node)
if node.absolute_path == nil then if node.absolute_path == nil then
return return
end end
if M.get_mark(node) then if self:get_mark(node) then
remove_mark(node) self:remove_mark(node)
else else
add_mark(node) self:add_mark(node)
end end
renderer.draw() renderer.draw()
end end
function M.clear_marks() function Marks:clear_marks()
NvimTreeMarks = {} self.marks = {}
renderer.draw() renderer.draw()
end end
---@param node Node|MinimalNode ---@param node Node
---@return table|nil ---@return Node|nil
function M.get_mark(node) function Marks:get_mark(node)
return node and NvimTreeMarks[node.absolute_path] return node and self.marks[node.absolute_path]
end end
---@return table ---@return Node[]
function M.get_marks() function Marks:get_marks()
local list = {} local list = {}
for _, node in pairs(NvimTreeMarks) do for _, node in pairs(self.marks) do
table.insert(list, node) table.insert(list, node)
end end
return list return list
end end
function M.setup(opts) function Marks.setup(opts)
renderer = require "nvim-tree.renderer" renderer = require "nvim-tree.renderer"
require("nvim-tree.marks.bulk-delete").setup(opts) require("nvim-tree.marks.bulk-delete").setup(opts)
@@ -65,4 +75,4 @@ function M.setup(opts)
require("nvim-tree.marks.bulk-move").setup(opts) require("nvim-tree.marks.bulk-move").setup(opts)
end end
return M return Marks

View File

@@ -1,6 +1,5 @@
local Iterator = require "nvim-tree.iterators.node-iterator" local Iterator = require "nvim-tree.iterators.node-iterator"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local Marks = require "nvim-tree.marks"
local open_file = require "nvim-tree.actions.node.open-file" local open_file = require "nvim-tree.actions.node.open-file"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
@@ -9,10 +8,15 @@ local lib = require "nvim-tree.lib"
---@param where string ---@param where string
---@return Node|nil ---@return Node|nil
local function get_nearest(node, where) local function get_nearest(node, where)
local explorer = core.get_explorer()
if not explorer then
return
end
local first, prev, next, last = nil, nil, nil, nil local first, prev, next, last = nil, nil, nil, nil
local found = false local found = false
Iterator.builder(core.get_explorer().nodes) Iterator.builder(explorer.nodes)
:recursor(function(n) :recursor(function(n)
return n.open and n.nodes return n.open and n.nodes
end) end)
@@ -22,7 +26,7 @@ local function get_nearest(node, where)
return return
end end
if not Marks.get_mark(n) then if not explorer.marks:get_mark(n) then
return return
end end
@@ -84,9 +88,14 @@ M.next = navigate_to "next"
M.prev = navigate_to "prev" M.prev = navigate_to "prev"
function M.select() function M.select()
local explorer = core.get_explorer()
if not explorer then
return
end
local list = vim.tbl_map(function(n) local list = vim.tbl_map(function(n)
return n.absolute_path return n.absolute_path
end, Marks.get_marks()) end, explorer.marks:get_marks())
vim.ui.select(list, { vim.ui.select(list, {
prompt = "Select a file to open or a folder to focus", prompt = "Select a file to open or a folder to focus",
@@ -94,7 +103,7 @@ function M.select()
if not choice or choice == "" then if not choice or choice == "" then
return return
end end
local node = Marks.get_mark { absolute_path = choice } local node = explorer.marks:get_mark { absolute_path = choice }
open_or_focus(node) open_or_focus(node)
end) end)
end end

View File

@@ -9,6 +9,7 @@
---@field fs_stat uv.fs_stat.result|nil ---@field fs_stat uv.fs_stat.result|nil
---@field git_status GitStatus|nil ---@field git_status GitStatus|nil
---@field hidden boolean ---@field hidden boolean
---@field is_dot boolean
---@field name string ---@field name string
---@field parent DirNode ---@field parent DirNode
---@field type string ---@field type string
@@ -20,6 +21,7 @@
---@field group_next Node|nil ---@field group_next Node|nil
---@field nodes Node[] ---@field nodes Node[]
---@field open boolean ---@field open boolean
---@field hidden_stats table -- Each field of this table is a key for source and value for count
---@class FileNode: BaseNode ---@class FileNode: BaseNode
---@field extension string ---@field extension string

View File

@@ -1,5 +1,4 @@
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local live_filter = require "nvim-tree.live-filter"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view" local view = require "nvim-tree.view"
@@ -10,6 +9,7 @@ local DecoratorCut = require "nvim-tree.renderer.decorator.cut"
local DecoratorDiagnostics = require "nvim-tree.renderer.decorator.diagnostics" local DecoratorDiagnostics = require "nvim-tree.renderer.decorator.diagnostics"
local DecoratorGit = require "nvim-tree.renderer.decorator.git" local DecoratorGit = require "nvim-tree.renderer.decorator.git"
local DecoratorModified = require "nvim-tree.renderer.decorator.modified" local DecoratorModified = require "nvim-tree.renderer.decorator.modified"
local DecoratorHidden = require "nvim-tree.renderer.decorator.hidden"
local DecoratorOpened = require "nvim-tree.renderer.decorator.opened" local DecoratorOpened = require "nvim-tree.renderer.decorator.opened"
local pad = require "nvim-tree.renderer.components.padding" local pad = require "nvim-tree.renderer.components.padding"
@@ -42,6 +42,8 @@ local M = {
---@field lines string[] includes icons etc. ---@field lines string[] includes icons etc.
---@field hl_args AddHighlightArgs[] line highlights ---@field hl_args AddHighlightArgs[] line highlights
---@field signs string[] line signs ---@field signs string[] line signs
---@field extmarks table[] extra marks for right icon placement
---@field virtual_lines table[] virtual lines for hidden count display
---@field private root_cwd string absolute path ---@field private root_cwd string absolute path
---@field private index number ---@field private index number
---@field private depth number ---@field private depth number
@@ -60,6 +62,8 @@ function Builder:new()
lines = {}, lines = {},
markers = {}, markers = {},
signs = {}, signs = {},
extmarks = {},
virtual_lines = {},
} }
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
@@ -228,6 +232,14 @@ function Builder:format_line(indent_markers, arrows, icon, name, node)
add_to_end(line, M.decorators[i]:icons_after(node)) add_to_end(line, M.decorators[i]:icons_after(node))
end end
local rights = {}
for i = #M.decorators, 1, -1 do
add_to_end(rights, M.decorators[i]:icons_right_align(node))
end
if #rights > 0 then
self.extmarks[self.index] = rights
end
return line return line
end end
@@ -341,7 +353,6 @@ function Builder:build_line(node, idx, num_children)
self.index = self.index + 1 self.index = self.index + 1
node = require("nvim-tree.lib").get_last_group_node(node) node = require("nvim-tree.lib").get_last_group_node(node)
if node.open then if node.open then
self.depth = self.depth + 1 self.depth = self.depth + 1
self:build_lines(node) self:build_lines(node)
@@ -349,9 +360,36 @@ function Builder:build_line(node, idx, num_children)
end end
end end
---Add virtual lines for rendering hidden count information per node
---@private
function Builder:add_hidden_count_string(node, idx, num_children)
if not node.open then
return
end
local hidden_count_string = M.opts.renderer.hidden_display(node.hidden_stats)
if hidden_count_string and hidden_count_string ~= "" then
local indent_markers = pad.get_indent_markers(self.depth, idx or 0, num_children or 0, node, self.markers, 1)
local indent_width = M.opts.renderer.indent_width
local indent_padding = string.rep(" ", indent_width)
local indent_string = indent_padding .. indent_markers.str
local line_nr = #self.lines - 1
self.virtual_lines[line_nr] = self.virtual_lines[line_nr] or {}
-- NOTE: We are inserting in depth order because of current traversal
-- if we change the traversal, we might need to sort by depth before rendering `self.virtual_lines`
-- to maintain proper ordering of parent and child folder hidden count info.
table.insert(self.virtual_lines[line_nr], {
{ indent_string, indent_markers.hl },
{ string.rep(indent_padding, (node.parent == nil and 0 or 1)) .. hidden_count_string, "NvimTreeHiddenDisplay" },
})
end
end
---@private ---@private
function Builder:get_nodes_number(nodes) function Builder:get_nodes_number(nodes)
if not live_filter.filter then local explorer = core.get_explorer()
if not explorer or not explorer.live_filter.filter then
return #nodes return #nodes
end end
@@ -378,6 +416,7 @@ function Builder:build_lines(node)
idx = idx + 1 idx = idx + 1
end end
end end
self:add_hidden_count_string(node)
end end
---@private ---@private
@@ -397,6 +436,7 @@ end
---@private ---@private
function Builder:build_header() function Builder:build_header()
local explorer = core.get_explorer()
if view.is_root_folder_visible(core.get_cwd()) then if view.is_root_folder_visible(core.get_cwd()) then
local root_name = self:format_root_name(M.opts.renderer.root_folder_label) local root_name = self:format_root_name(M.opts.renderer.root_folder_label)
table.insert(self.lines, root_name) table.insert(self.lines, root_name)
@@ -404,8 +444,8 @@ function Builder:build_header()
self.index = 1 self.index = 1
end end
if live_filter.filter then if explorer and explorer.live_filter.filter then
local filter_line = string.format("%s/%s/", M.opts.live_filter.prefix, live_filter.filter) local filter_line = string.format("%s/%s/", M.opts.live_filter.prefix, explorer.live_filter.filter)
table.insert(self.lines, filter_line) table.insert(self.lines, filter_line)
local prefix_length = string.len(M.opts.live_filter.prefix) local prefix_length = string.len(M.opts.live_filter.prefix)
self:insert_highlight({ "NvimTreeLiveFilterPrefix" }, 0, prefix_length) self:insert_highlight({ "NvimTreeLiveFilterPrefix" }, 0, prefix_length)
@@ -432,7 +472,50 @@ function Builder:build()
return self return self
end end
---@param opts table
local setup_hidden_display_function = function(opts)
local hidden_display = opts.renderer.hidden_display
-- options are already validated, so ´hidden_display´ can ONLY be `string` or `function` if type(hidden_display) == "string" then
if type(hidden_display) == "string" then
if hidden_display == "none" then
opts.renderer.hidden_display = function()
return nil
end
elseif hidden_display == "simple" then
opts.renderer.hidden_display = function(hidden_stats)
return utils.default_format_hidden_count(hidden_stats, true)
end
elseif hidden_display == "all" then
opts.renderer.hidden_display = function(hidden_stats)
return utils.default_format_hidden_count(hidden_stats, false)
end
end
elseif type(hidden_display) == "function" then
local safe_render = function(hidden_stats)
-- In case of missing field such as live_filter we zero it, otherwise keep field as is
hidden_stats = vim.tbl_deep_extend("force", {
live_filter = 0,
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
bookmark = 0,
}, hidden_stats or {})
local ok, result = pcall(hidden_display, hidden_stats)
if not ok then
notify.warn "Problem occurred in the function ``opts.renderer.hidden_display`` see nvim-tree.renderer.hidden_display on :h nvim-tree"
return nil
end
return result
end
opts.renderer.hidden_display = safe_render
end
end
function Builder.setup(opts) function Builder.setup(opts)
setup_hidden_display_function(opts)
M.opts = opts M.opts = opts
-- priority order -- priority order
@@ -442,6 +525,7 @@ function Builder.setup(opts)
DecoratorDiagnostics:new(opts), DecoratorDiagnostics:new(opts),
DecoratorBookmarks:new(opts), DecoratorBookmarks:new(opts),
DecoratorModified:new(opts), DecoratorModified:new(opts),
DecoratorHidden:new(opts),
DecoratorOpened:new(opts), DecoratorOpened:new(opts),
DecoratorGit:new(opts), DecoratorGit:new(opts),
} }

View File

@@ -19,7 +19,7 @@ local function check_siblings_for_folder(node, with_arrows)
return false return false
end end
local function get_padding_indent_markers(depth, idx, nodes_number, markers, with_arrows, inline_arrows, node) local function get_padding_indent_markers(depth, idx, nodes_number, markers, with_arrows, inline_arrows, node, early_stop)
local base_padding = with_arrows and (not node.nodes or depth > 0) and " " or "" local base_padding = with_arrows and (not node.nodes or depth > 0) and " " or ""
local padding = (inline_arrows or depth == 0) and base_padding or "" local padding = (inline_arrows or depth == 0) and base_padding or ""
@@ -27,7 +27,7 @@ local function get_padding_indent_markers(depth, idx, nodes_number, markers, wit
local has_folder_sibling = check_siblings_for_folder(node, with_arrows) local has_folder_sibling = check_siblings_for_folder(node, with_arrows)
local indent = string.rep(" ", M.config.indent_width - 1) local indent = string.rep(" ", M.config.indent_width - 1)
markers[depth] = idx ~= nodes_number markers[depth] = idx ~= nodes_number
for i = 1, depth do for i = 1, depth - early_stop do
local glyph local glyph
if idx == nodes_number and i == depth then if idx == nodes_number and i == depth then
local bottom_width = M.config.indent_width - 2 + (with_arrows and not inline_arrows and has_folder_sibling and 2 or 0) local bottom_width = M.config.indent_width - 2 + (with_arrows and not inline_arrows and has_folder_sibling and 2 or 0)
@@ -62,7 +62,7 @@ end
---@param node table ---@param node table
---@param markers table ---@param markers table
---@return HighlightedString[] ---@return HighlightedString[]
function M.get_indent_markers(depth, idx, nodes_number, node, markers) function M.get_indent_markers(depth, idx, nodes_number, node, markers, early_stop)
local str = "" local str = ""
local show_arrows = M.config.icons.show.folder_arrow local show_arrows = M.config.icons.show.folder_arrow
@@ -71,7 +71,7 @@ function M.get_indent_markers(depth, idx, nodes_number, node, markers)
local indent_width = M.config.indent_width local indent_width = M.config.indent_width
if show_markers then if show_markers then
str = str .. get_padding_indent_markers(depth, idx, nodes_number, markers, show_arrows, inline_arrows, node) str = str .. get_padding_indent_markers(depth, idx, nodes_number, markers, show_arrows, inline_arrows, node, early_stop or 0)
else else
str = str .. string.rep(" ", depth * indent_width) str = str .. string.rep(" ", depth * indent_width)
end end

View File

@@ -1,4 +1,4 @@
local marks = require "nvim-tree.marks" local core = require "nvim-tree.core"
local HL_POSITION = require("nvim-tree.enum").HL_POSITION local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
@@ -34,7 +34,7 @@ end
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]|nil icons
function DecoratorBookmarks:calculate_icons(node) function DecoratorBookmarks:calculate_icons(node)
if marks.get_mark(node) then if core.get_explorer() and core.get_explorer().marks:get_mark(node) then
return { self.icon } return { self.icon }
end end
end end
@@ -43,7 +43,7 @@ end
---@param node Node ---@param node Node
---@return string|nil group ---@return string|nil group
function DecoratorBookmarks:calculate_highlight(node) function DecoratorBookmarks:calculate_highlight(node)
if self.hl_pos ~= HL_POSITION.none and marks.get_mark(node) then if self.hl_pos ~= HL_POSITION.none and core.get_explorer() and core.get_explorer().marks:get_mark(node) then
return "NvimTreeBookmarkHL" return "NvimTreeBookmarkHL"
end end
end end

View File

@@ -0,0 +1,55 @@
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
local explorer_node = require "nvim-tree.explorer.node"
local Decorator = require "nvim-tree.renderer.decorator"
---@class DecoratorHidden: Decorator
---@field icon HighlightedString|nil
local DecoratorHidden = Decorator:new()
---@param opts table
---@return DecoratorHidden
function DecoratorHidden:new(opts)
local o = Decorator.new(self, {
enabled = true,
hl_pos = HL_POSITION[opts.renderer.highlight_hidden] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT[opts.renderer.icons.hidden_placement] or ICON_PLACEMENT.none,
})
---@cast o DecoratorHidden
if opts.renderer.icons.show.hidden then
o.icon = {
str = opts.renderer.icons.glyphs.hidden,
hl = { "NvimTreeHiddenIcon" },
}
o:define_sign(o.icon)
end
return o
end
---Hidden icon: hidden.enable, renderer.icons.show.hidden and node starts with `.` (dotfile).
---@param node Node
---@return HighlightedString[]|nil icons
function DecoratorHidden:calculate_icons(node)
if self.enabled and explorer_node.is_dotfile(node) then
return { self.icon }
end
end
---Hidden highlight: hidden.enable, renderer.highlight_hidden and node starts with `.` (dotfile).
---@param node Node
---@return string|nil group
function DecoratorHidden:calculate_highlight(node)
if not self.enabled or self.hl_pos == HL_POSITION.none or (not explorer_node.is_dotfile(node)) then
return nil
end
if node.nodes then
return "NvimTreeHiddenFolderHL"
else
return "NvimTreeHiddenFileHL"
end
end
return DecoratorHidden

View File

@@ -74,6 +74,17 @@ function Decorator:icons_after(node)
return self:calculate_icons(node) return self:calculate_icons(node)
end end
---Icons when ICON_PLACEMENT.right_align
---@param node Node
---@return HighlightedString[]|nil icons
function Decorator:icons_right_align(node)
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.right_align then
return
end
return self:calculate_icons(node)
end
---Maybe icons, optionally implemented ---Maybe icons, optionally implemented
---@protected ---@protected
---@param _ Node ---@param _ Node

View File

@@ -12,13 +12,15 @@ local M = {}
local SIGN_GROUP = "NvimTreeRendererSigns" local SIGN_GROUP = "NvimTreeRendererSigns"
local namespace_id = vim.api.nvim_create_namespace "NvimTreeHighlights" local namespace_highlights_id = vim.api.nvim_create_namespace "NvimTreeHighlights"
local namespace_extmarks_id = vim.api.nvim_create_namespace "NvimTreeExtmarks"
local namespace_virtual_lines_id = vim.api.nvim_create_namespace "NvimTreeVirtualLines"
---@param bufnr number ---@param bufnr number
---@param lines string[] ---@param lines string[]
---@param hl_args AddHighlightArgs[] ---@param hl_args AddHighlightArgs[]
---@param signs string[] ---@param signs string[]
local function _draw(bufnr, lines, hl_args, signs) local function _draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines)
if vim.fn.has "nvim-0.10" == 1 then if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value("modifiable", true, { buf = bufnr }) vim.api.nvim_set_option_value("modifiable", true, { buf = bufnr })
else else
@@ -38,17 +40,37 @@ local function _draw(bufnr, lines, hl_args, signs)
for i, sign_name in pairs(signs) do for i, sign_name in pairs(signs) do
vim.fn.sign_place(0, SIGN_GROUP, sign_name, bufnr, { lnum = i + 1 }) vim.fn.sign_place(0, SIGN_GROUP, sign_name, bufnr, { lnum = i + 1 })
end end
vim.api.nvim_buf_clear_namespace(bufnr, namespace_extmarks_id, 0, -1)
for i, extname in pairs(extmarks) do
for _, mark in ipairs(extname) do
vim.api.nvim_buf_set_extmark(bufnr, namespace_extmarks_id, i, -1, {
virt_text = { { mark.str, mark.hl } },
virt_text_pos = "right_align",
hl_mode = "combine",
})
end
end
vim.api.nvim_buf_clear_namespace(bufnr, namespace_virtual_lines_id, 0, -1)
for line_nr, vlines in pairs(virtual_lines) do
vim.api.nvim_buf_set_extmark(bufnr, namespace_virtual_lines_id, line_nr, 0, {
virt_lines = vlines,
virt_lines_above = false,
virt_lines_leftcol = true,
})
end
end end
function M.render_hl(bufnr, hl) function M.render_hl(bufnr, hl)
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then
return return
end end
vim.api.nvim_buf_clear_namespace(bufnr, namespace_id, 0, -1) vim.api.nvim_buf_clear_namespace(bufnr, namespace_highlights_id, 0, -1)
for _, data in ipairs(hl) do for _, data in ipairs(hl) do
if type(data[1]) == "table" then if type(data[1]) == "table" then
for _, group in ipairs(data[1]) do for _, group in ipairs(data[1]) do
vim.api.nvim_buf_add_highlight(bufnr, namespace_id, group, data[2], data[3], data[4]) vim.api.nvim_buf_add_highlight(bufnr, namespace_highlights_id, group, data[2], data[3], data[4])
end end
end end
end end
@@ -67,7 +89,7 @@ function M.draw()
local builder = Builder:new():build() local builder = Builder:new():build()
_draw(bufnr, builder.lines, builder.hl_args, builder.signs) _draw(bufnr, builder.lines, builder.hl_args, builder.signs, builder.extmarks, builder.virtual_lines)
if cursor and #builder.lines >= cursor[1] then if cursor and #builder.lines >= cursor[1] then
vim.api.nvim_win_set_cursor(view.get_winnr() or 0, cursor) vim.api.nvim_win_set_cursor(view.get_winnr() or 0, cursor)

View File

@@ -112,7 +112,10 @@ function M.find_node(nodes, fn)
end) end)
:iterate() :iterate()
i = require("nvim-tree.view").is_root_folder_visible() and i or i - 1 i = require("nvim-tree.view").is_root_folder_visible() and i or i - 1
i = require("nvim-tree.live-filter").filter and i + 1 or i local explorer = require("nvim-tree.core").get_explorer()
if explorer and explorer.live_filter.filter then
i = i + 1
end
return node, i return node, i
end end
@@ -181,6 +184,26 @@ function M.get_parent_of_group(node)
return node return node
end end
M.default_format_hidden_count = function(hidden_count, simple)
local parts = {}
local total_count = 0
for reason, count in pairs(hidden_count) do
total_count = total_count + count
if count > 0 then
table.insert(parts, reason .. ": " .. tostring(count))
end
end
local hidden_count_string = table.concat(parts, ", ") -- if empty then is "" (empty string)
if simple then
hidden_count_string = ""
end
if total_count > 0 then
return "(" .. tostring(total_count) .. (simple and " hidden" or " total ") .. hidden_count_string .. ")"
end
return nil
end
--- Return visible nodes indexed by line --- Return visible nodes indexed by line
---@param nodes_all Node[] ---@param nodes_all Node[]
---@param line_start number ---@param line_start number

View File

@@ -50,6 +50,7 @@ M.View = {
"Normal:NvimTreeNormal", "Normal:NvimTreeNormal",
"NormalNC:NvimTreeNormalNC", "NormalNC:NvimTreeNormalNC",
"NormalFloat:NvimTreeNormalFloat", "NormalFloat:NvimTreeNormalFloat",
"FloatBorder:NvimTreeNormalFloatBorder",
}, ","), }, ","),
}, },
} }
@@ -548,6 +549,34 @@ function M.reset_winhl()
end end
end end
---Check if width determined or calculated on-fly
---@return boolean
function M.is_width_determined()
return type(M.View.width) ~= "function"
end
---Configure width-related config
---@param width string|function|number|table|nil
function M.configure_width(width)
if type(width) == "table" then
M.View.adaptive_size = true
M.View.width = width.min or DEFAULT_MIN_WIDTH
M.View.max_width = width.max or DEFAULT_MAX_WIDTH
M.View.padding = width.padding or DEFAULT_PADDING
elseif width == nil then
if M.config.width ~= nil then
-- if we had input config - fallback to it
M.configure_width(M.config.width)
else
-- otherwise - restore initial width
M.View.width = M.View.initial_width
end
else
M.View.adaptive_size = false
M.View.width = width
end
end
function M.setup(opts) function M.setup(opts)
local options = opts.view or {} local options = opts.view or {}
M.View.centralize_selection = options.centralize_selection M.View.centralize_selection = options.centralize_selection
@@ -563,15 +592,8 @@ function M.setup(opts)
M.View.float = options.float M.View.float = options.float
M.on_attach = opts.on_attach M.on_attach = opts.on_attach
if type(options.width) == "table" then M.config = options
M.View.adaptive_size = true M.configure_width(options.width)
M.View.width = options.width.min or DEFAULT_MIN_WIDTH
M.View.max_width = options.width.max or DEFAULT_MAX_WIDTH
M.View.padding = options.width.padding or DEFAULT_PADDING
else
M.View.adaptive_size = false
M.View.width = options.width
end
M.View.initial_width = get_width() M.View.initial_width = get_width()
end end