Compare commits

..

11 Commits

Author SHA1 Message Date
Mateusz Russak
fa051cf990 refactor(#2826): multi instance nvim-tree.view 2024-08-04 13:31:11 +02:00
Mateusz Russak
0f2cda6ce0 docs: add missing live filter luadocs 2024-08-04 11:26:14 +02:00
Mateusz Russak
e6374abc7d Merge branch 'live-filter-multiinstace' of github.com:nvim-tree/nvim-tree.lua into live-filter-multiinstace 2024-08-04 11:20:04 +02:00
Mateusz Russak
32314fd3ee Update lua/nvim-tree/api.lua
Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-08-04 11:19:31 +02:00
Mateusz Russak
e2853ee4fb Merge branch 'master' into live-filter-multiinstace 2024-08-04 11:19:06 +02:00
Mateusz Russak
85fba095cd Merge branch 'master' into live-filter-multiinstace 2024-07-28 11:28:26 +02:00
Mateusz Russak
d7504b3963 fix: style 2024-07-28 11:26:59 +02:00
Mateusz Russak
a634a1bb4d fix: api and filtration 2024-07-28 11:19:37 +02:00
Mateusz Russak
9deac32a40 refactor: all usages going through the explorer 2024-07-28 10:16:02 +02:00
Mateusz Russak
c6ae2431bc Merge branch 'master' into live-filter-multiinstace 2024-07-27 13:26:26 +02:00
Mateusz Russak
cc2d8c7475 feat(#2827): Multi Instance: Refactor: nvim-tree.live-filter 2024-07-23 18:53:16 +02:00
45 changed files with 1205 additions and 1325 deletions

View File

@@ -67,7 +67,7 @@ jobs:
strategy: strategy:
matrix: matrix:
nvim_version: [ stable, nightly ] nvim_version: [ stable, nightly ]
luals_version: [ 3.10.5 ] luals_version: [ 3.9.1 ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@@ -1,3 +1,3 @@
{ {
".": "1.6.1" ".": "1.5.0"
} }

View File

@@ -1,30 +1,5 @@
# Changelog # Changelog
## [1.6.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.6.0...nvim-tree-v1.6.1) (2024-09-09)
### Bug Fixes
* **#2794:** sshfs compatibility ([#2893](https://github.com/nvim-tree/nvim-tree.lua/issues/2893)) ([2d6e64d](https://github.com/nvim-tree/nvim-tree.lua/commit/2d6e64dd8c45a86f312552b7a47eef2c8623a25c))
* **#2868:** windows: do not visit unenumerable directories such as Application Data ([#2874](https://github.com/nvim-tree/nvim-tree.lua/issues/2874)) ([2104786](https://github.com/nvim-tree/nvim-tree.lua/commit/210478677cb9d672c4265deb0e9b59d58b675bd4))
* **#2878:** nowrapscan prevents move from root ([#2880](https://github.com/nvim-tree/nvim-tree.lua/issues/2880)) ([4234095](https://github.com/nvim-tree/nvim-tree.lua/commit/42340952af598a08ab80579d067b6da72a9e6d29))
* **#2879:** remove unnecessary tree window width setting to prevent unnecessary :wincmd = ([#2881](https://github.com/nvim-tree/nvim-tree.lua/issues/2881)) ([d43ab67](https://github.com/nvim-tree/nvim-tree.lua/commit/d43ab67d0eb4317961c5e9d15fffe908519debe0))
## [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) ## [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)

View File

@@ -423,7 +423,6 @@ 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",
@@ -879,49 +878,6 @@ 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`
@@ -955,6 +911,7 @@ Value can be `"none"`, `"icon"`, `"name"` or `"all"`
*nvim-tree.renderer.highlight_hidden* *nvim-tree.renderer.highlight_hidden*
Highlight icons and/or names for hidden files (dotfiles) using the Highlight icons and/or names for hidden files (dotfiles) using the
`NvimTreeHiddenFileHL` highlight group. `NvimTreeHiddenFileHL` highlight group.
Requires |nvim-tree.hidden.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"`
@@ -1088,7 +1045,8 @@ Icon order and sign column precedence:
*nvim-tree.renderer.icons.show.hidden* *nvim-tree.renderer.icons.show.hidden*
Show a hidden icon, see |renderer.icons.hidden_placement| Show a hidden icon, see |renderer.icons.hidden_placement|
Type: `boolean`, Default: `false` 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|
@@ -1679,8 +1637,10 @@ to avoid breaking configurations due to internal breaking changes.
The api is separated in multiple modules, which can be accessed with The api is separated in multiple modules, which can be accessed with
`api.<module>.<function>` `api.<module>.<function>`
Functions accepting {node} as their first argument will use the node under the Functions that needs a tree node parameter are exposed with an abstraction
cursor when that argument is not present or nil. that injects the node from the cursor position in the tree when calling
the function. It will use the node you pass as an argument in priority if it
exists.
============================================================================== ==============================================================================
6.1 API TREE *nvim-tree-api.tree* 6.1 API TREE *nvim-tree-api.tree*
@@ -1806,11 +1766,9 @@ tree.collapse_all({keep_buffers}) *nvim-tree-api.tree.collapse_all()*
Parameters: ~ Parameters: ~
• {keep_buffers} (boolean) do not collapse nodes with open buffers. • {keep_buffers} (boolean) do not collapse nodes with open buffers.
tree.expand_all({node}) *nvim-tree-api.tree.expand_all()* tree.expand_all() *nvim-tree-api.tree.expand_all()*
Recursively expand all nodes under the tree root or specified folder. Recursively expand all nodes in the tree.
Folder: only the nodes underneath that folder.
Parameters: ~
• {node} (Node|nil) folder
*nvim-tree-api.tree.toggle_enable_filters()* *nvim-tree-api.tree.toggle_enable_filters()*
tree.toggle_enable_filters() tree.toggle_enable_filters()
@@ -1885,86 +1843,86 @@ fs.create({node}) *nvim-tree-api.fs.create()*
Multiple directories/files may be created e.g. `foo/bar/baz` Multiple directories/files may be created e.g. `foo/bar/baz`
Parameters: ~ Parameters: ~
• {node} (Node|nil) parent, uses the parent of a file. • {node} (Node) parent, uses the parent of a file.
fs.remove({node}) *nvim-tree-api.fs.remove()* fs.remove({node}) *nvim-tree-api.fs.remove()*
Delete a file or folder from the file system. Delete a file or folder from the file system.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.trash({node}) *nvim-tree-api.fs.trash()* fs.trash({node}) *nvim-tree-api.fs.trash()*
Trash a file or folder as per |nvim-tree.trash| Trash a file or folder as per |nvim-tree.trash|
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.rename_node({node}) *nvim-tree-api.fs.rename_node()* fs.rename_node({node}) *nvim-tree-api.fs.rename_node()*
Prompt to rename a file or folder. Prompt to rename a file or folder.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.rename({node}) *nvim-tree-api.fs.rename()* fs.rename({node}) *nvim-tree-api.fs.rename()*
Prompt to rename a file or folder by name. Prompt to rename a file or folder by name.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.rename_basename({node}) *nvim-tree-api.fs.rename_basename()* fs.rename_basename({node}) *nvim-tree-api.fs.rename_basename()*
Prompt to rename a file or folder by name with extension omitted. Prompt to rename a file or folder by name with extension omitted.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.rename_sub({node}) *nvim-tree-api.fs.rename_sub()* fs.rename_sub({node}) *nvim-tree-api.fs.rename_sub()*
Prompt to rename a file or folder by absolute path with name omitted. Prompt to rename a file or folder by absolute path with name omitted.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.rename_full({node}) *nvim-tree-api.fs.rename_full()* fs.rename_full({node}) *nvim-tree-api.fs.rename_full()*
Prompt to rename a file or folder by absolute path. Prompt to rename a file or folder by absolute path.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.cut({node}) *nvim-tree-api.fs.cut()* fs.cut({node}) *nvim-tree-api.fs.cut()*
Cut a file or folder to the nvim-tree clipboard. Cut a file or folder to the nvim-tree clipboard.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.paste({node}) *nvim-tree-api.fs.paste()* fs.paste({node}) *nvim-tree-api.fs.paste()*
Paste a file or folder from the nvim-tree clipboard. Paste a file or folder from the nvim-tree clipboard.
Parameters: ~ Parameters: ~
• {node} (Node|nil) destination folder, uses the parent of a file. • {node} (Node) destination folder, uses the parent of a file.
fs.copy.node({node}) *nvim-tree-api.fs.copy.node()* fs.copy.node({node}) *nvim-tree-api.fs.copy.node()*
Copy a file or folder from the nvim-tree clipboard. Copy a file or folder from the nvim-tree clipboard.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.copy.absolute_path({node}) *nvim-tree-api.fs.copy.absolute_path()* fs.copy.absolute_path({node}) *nvim-tree-api.fs.copy.absolute_path()*
Copy the absolute path of a file or folder to the system clipboard. Copy the absolute path of a file or folder to the system clipboard.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.copy.filename({node}) *nvim-tree-api.fs.copy.filename()* fs.copy.filename({node}) *nvim-tree-api.fs.copy.filename()*
Copy the name of a file or folder to the system clipboard. Copy the name of a file or folder to the system clipboard.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.copy.relative_path({node}) *nvim-tree-api.fs.copy.relative_path()* fs.copy.relative_path({node}) *nvim-tree-api.fs.copy.relative_path()*
Copy the path of a file or folder relative to the tree root to the system Copy the path of a file or folder relative to the tree root to the system
clipboard. clipboard.
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node) file or folder
fs.clear_clipboard() *nvim-tree-api.fs.clear_clipboard()* fs.clear_clipboard() *nvim-tree-api.fs.clear_clipboard()*
Clear the nvim-tree clipboard. Clear the nvim-tree clipboard.
@@ -1975,37 +1933,34 @@ fs.print_clipboard() *nvim-tree-api.fs.print_clipboard()*
============================================================================== ==============================================================================
6.3 API NODE *nvim-tree-api.node* 6.3 API NODE *nvim-tree-api.node*
Parameters: ~ node.open.edit() *nvim-tree-api.node.open.edit()*
• {node} (Node|nil) file or folder
node.open.edit({node}) *nvim-tree-api.node.open.edit()*
File: open as per |nvim-tree.actions.open_file| File: open as per |nvim-tree.actions.open_file|
Folder: expand or collapse Folder: expand or collapse
Root: change directory up Root: change directory up
*nvim-tree-api.node.open.replace_tree_buffer()* *nvim-tree-api.node.open.replace_tree_buffer()*
node.open.replace_tree_buffer({node}) node.open.replace_tree_buffer()
|nvim-tree-api.node.edit()|, file will be opened in place: in the |nvim-tree-api.node.edit()|, file will be opened in place: in the
nvim-tree window. nvim-tree window.
*nvim-tree-api.node.open.no_window_picker()* *nvim-tree-api.node.open.no_window_picker()*
node.open.no_window_picker({node}) node.open.no_window_picker()
|nvim-tree-api.node.edit()|, window picker will never be used as per |nvim-tree-api.node.edit()|, window picker will never be used as per
|nvim-tree.actions.open_file.window_picker.enable| `false` |nvim-tree.actions.open_file.window_picker.enable| `false`
node.open.vertical({node}) *nvim-tree-api.node.open.vertical()* node.open.vertical() *nvim-tree-api.node.open.vertical()*
|nvim-tree-api.node.edit()|, file will be opened in a new vertical split. |nvim-tree-api.node.edit()|, file will be opened in a new vertical split.
node.open.horizontal({node}) *nvim-tree-api.node.open.horizontal()* node.open.horizontal() *nvim-tree-api.node.open.horizontal()*
|nvim-tree-api.node.edit()|, file will be opened in a new horizontal split. |nvim-tree-api.node.edit()|, file will be opened in a new horizontal split.
*nvim-tree-api.node.open.toggle_group_empty()* *nvim-tree-api.node.open.toggle_group_empty()*
node.open.toggle_group_empty({node}) node.open.toggle_group_empty()
Toggle |nvim-tree.renderer.group_empty| for a specific folder. Toggle |nvim-tree.renderer.group_empty| for a specific folder.
Does nothing on files. Does nothing on files.
Needs |nvim-tree.renderer.group_empty| set. Needs |nvim-tree.renderer.group_empty| set.
node.open.drop({node}) *nvim-tree-api.node.open.drop()* node.open.drop() *nvim-tree-api.node.open.drop()*
Switch to window with selected file if it exists. Switch to window with selected file if it exists.
Open file otherwise. Open file otherwise.
See: `:h :drop`. See: `:h :drop`.
@@ -2014,11 +1969,11 @@ node.open.drop({node}) *nvim-tree-api.node.open.drop()*
Folder: expand or collapse Folder: expand or collapse
Root: change directory up Root: change directory up
node.open.tab({node}) *nvim-tree-api.node.open.tab()* node.open.tab() *nvim-tree-api.node.open.tab()*
|nvim-tree-api.node.edit()|, file will be opened in a new tab. |nvim-tree-api.node.edit()|, file will be opened in a new tab.
*nvim-tree-api.node.open.tab_drop()* *nvim-tree-api.node.open.tab_drop()*
node.open.tab_drop({node}) node.open.tab_drop()
Switch to tab containing window with selected file if it exists. Switch to tab containing window with selected file if it exists.
Open file in new tab otherwise. Open file in new tab otherwise.
@@ -2026,103 +1981,102 @@ node.open.tab_drop({node})
Folder: expand or collapse Folder: expand or collapse
Root: change directory up Root: change directory up
node.open.preview({node}) *nvim-tree-api.node.open.preview()* node.open.preview() *nvim-tree-api.node.open.preview()*
|nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`. |nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`.
*nvim-tree-api.node.open.preview_no_picker()* node.open.preview_no_picker() *nvim-tree-api.node.open.preview_no_picker()*
node.open.preview_no_picker({node})
|nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`. |nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`.
window picker will never be used as per window picker will never be used as per
|nvim-tree.actions.open_file.window_picker.enable| `false` |nvim-tree.actions.open_file.window_picker.enable| `false`
node.navigate.git.next({node}) *nvim-tree-api.node.navigate.git.next()* node.navigate.git.next() *nvim-tree-api.node.navigate.git.next()*
Navigate to the next item showing git status. Navigate to the next item showing git status.
*nvim-tree-api.node.navigate.git.next_recursive()* *nvim-tree-api.node.navigate.git.next_recursive()*
node.navigate.git.next_recursive({node}) node.navigate.git.next_recursive()
Alternative to |nvim-tree-api.node.navigate.git.next()| that navigates to Alternative to |nvim-tree-api.node.navigate.git.next()| that navigates to
the next file showing git status, recursively. the next file showing git status, recursively.
Needs |nvim-tree.git.show_on_dirs| set. Needs |nvim-tree.git.show_on_dirs| set.
*nvim-tree-api.node.navigate.git.next_skip_gitignored()* *nvim-tree-api.node.navigate.git.next_skip_gitignored()*
node.navigate.git.next_skip_gitignored({node}) node.navigate.git.next_skip_gitignored()
Same as |node.navigate.git.next()|, but skips gitignored files. Same as |node.navigate.git.next()|, but skips gitignored files.
node.navigate.git.prev({node}) *nvim-tree-api.node.navigate.git.prev()* node.navigate.git.prev() *nvim-tree-api.node.navigate.git.prev()*
Navigate to the previous item showing git status. Navigate to the previous item showing git status.
*nvim-tree-api.node.navigate.git.prev_recursive()* *nvim-tree-api.node.navigate.git.prev_recursive()*
node.navigate.git.prev_recursive({node}) node.navigate.git.prev_recursive()
Alternative to |nvim-tree-api.node.navigate.git.prev()| that navigates to Alternative to |nvim-tree-api.node.navigate.git.prev()| that navigates to
the previous file showing git status, recursively. the previous file showing git status, recursively.
Needs |nvim-tree.git.show_on_dirs| set. Needs |nvim-tree.git.show_on_dirs| set.
*nvim-tree-api.node.navigate.git.prev_skip_gitignored()* *nvim-tree-api.node.navigate.git.prev_skip_gitignored()*
node.navigate.git.prev_skip_gitignored({node}) node.navigate.git.prev_skip_gitignored()
Same as |node.navigate.git.prev()|, but skips gitignored files. Same as |node.navigate.git.prev()|, but skips gitignored files.
*nvim-tree-api.node.navigate.diagnostics.next()* *nvim-tree-api.node.navigate.diagnostics.next()*
node.navigate.diagnostics.next({node}) node.navigate.diagnostics.next()
Navigate to the next item showing diagnostic status. Navigate to the next item showing diagnostic status.
*nvim-tree-api.node.navigate.diagnostics.next_recursive()* *nvim-tree-api.node.navigate.diagnostics.next_recursive()*
node.navigate.diagnostics.next_recursive({node}) node.navigate.diagnostics.next_recursive()
Alternative to |nvim-tree-api.node.navigate.diagnostics.next()| that Alternative to |nvim-tree-api.node.navigate.diagnostics.next()| that
navigates to the next file showing diagnostic status, recursively. navigates to the next file showing diagnostic status, recursively.
Needs |nvim-tree.diagnostics.show_on_dirs| set. Needs |nvim-tree.diagnostics.show_on_dirs| set.
*nvim-tree-api.node.navigate.diagnostics.prev()* *nvim-tree-api.node.navigate.diagnostics.prev()*
node.navigate.diagnostics.prev({node}) node.navigate.diagnostics.prev()
Navigate to the next item showing diagnostic status. Navigate to the next item showing diagnostic status.
*nvim-tree-api.node.navigate.diagnostics.prev_recursive()* *nvim-tree-api.node.navigate.diagnostics.prev_recursive()*
node.navigate.diagnostics.prev_recursive({node}) node.navigate.diagnostics.prev_recursive()
Alternative to |nvim-tree-api.node.navigate.diagnostics.prev()| that Alternative to |nvim-tree-api.node.navigate.diagnostics.prev()| that
navigates to the previous file showing diagnostic status, recursively. navigates to the previous file showing diagnostic status, recursively.
Needs |nvim-tree.diagnostics.show_on_dirs| set. Needs |nvim-tree.diagnostics.show_on_dirs| set.
*nvim-tree-api.node.navigate.opened.next()* *nvim-tree-api.node.navigate.opened.next()*
node.navigate.opened.next({node}) node.navigate.opened.next()
Navigate to the next |bufloaded()| item. Navigate to the next |bufloaded()| item.
See |nvim-tree.renderer.highlight_opened_files| See |nvim-tree.renderer.highlight_opened_files|
*nvim-tree-api.node.navigate.opened.prev()* *nvim-tree-api.node.navigate.opened.prev()*
node.navigate.opened.prev({node}) node.navigate.opened.prev()
Navigate to the previous |bufloaded()| item. Navigate to the previous |bufloaded()| item.
See |nvim-tree.renderer.highlight_opened_files| See |nvim-tree.renderer.highlight_opened_files|
*nvim-tree-api.node.navigate.sibling.next()* *nvim-tree-api.node.navigate.sibling.next()*
node.navigate.sibling.next({node}) node.navigate.sibling.next()
Navigate to the next node in the current node's folder, wraps. Navigate to the next node in the current node's folder, wraps.
*nvim-tree-api.node.navigate.sibling.prev()* *nvim-tree-api.node.navigate.sibling.prev()*
node.navigate.sibling.prev({node}) node.navigate.sibling.prev()
Navigate to the previous node in the current node's folder, wraps. Navigate to the previous node in the current node's folder, wraps.
*nvim-tree-api.node.navigate.sibling.first()* *nvim-tree-api.node.navigate.sibling.first()*
node.navigate.sibling.first({node}) node.navigate.sibling.first()
Navigate to the first node in the current node's folder. Navigate to the first node in the current node's folder.
*nvim-tree-api.node.navigate.sibling.last()* *nvim-tree-api.node.navigate.sibling.last()*
node.navigate.sibling.last({node}) node.navigate.sibling.last()
Navigate to the last node in the current node's folder. Navigate to the last node in the current node's folder.
*nvim-tree-api.node.navigate.parent()* *nvim-tree-api.node.navigate.parent()*
node.navigate.parent({node}) node.navigate.parent()
Navigate to the parent folder of the current node. Navigate to the parent folder of the current node.
*nvim-tree-api.node.navigate.parent_close()* *nvim-tree-api.node.navigate.parent_close()*
node.navigate.parent_close({node}) node.navigate.parent_close()
|api.node.navigate.parent()|, closing that folder. |api.node.navigate.parent()|, closing that folder.
node.show_info_popup({node}) *nvim-tree-api.node.show_info_popup()* node.show_info_popup() *nvim-tree-api.node.show_info_popup()*
Open a popup window showing: fullpath, size, accessed, modified, created. Open a popup window showing: fullpath, size, accessed, modified, created.
node.run.cmd({node}) *nvim-tree-api.node.run.cmd()* node.run.cmd() *nvim-tree-api.node.run.cmd()*
Enter |cmdline| with the full path of the node and the cursor at the start Enter |cmdline| with the full path of the node and the cursor at the start
of the line. of the line.
node.run.system({node}) *nvim-tree-api.node.run.system()* node.run.system() *nvim-tree-api.node.run.system()*
Execute |nvim-tree.system_open| Execute |nvim-tree.system_open|
============================================================================== ==============================================================================
@@ -2197,8 +2151,8 @@ marks.navigate.prev() *nvim-tree-api.marks.navigate.prev()*
As per |nvim-tree-api.marks.navigate.next()| As per |nvim-tree-api.marks.navigate.next()|
marks.navigate.select() *nvim-tree-api.marks.navigate.select()* marks.navigate.select() *nvim-tree-api.marks.navigate.select()*
Prompts for selection of a marked node, sorted by absolute paths. Prompts for selection of a marked node as per
A folder will be focused, a file will be opened. |nvim-tree-api.marks.navigate.next()|
============================================================================== ==============================================================================
6.8 API CONFIG *nvim-tree-api.config* 6.8 API CONFIG *nvim-tree-api.config*
@@ -2300,8 +2254,7 @@ Single left mouse mappings can be achieved via `<LeftRelease>`.
Single right / middle mouse mappings will require changes to |mousemodel| or |mouse|. Single right / middle mouse mappings will require changes to |mousemodel| or |mouse|.
|vim.keymap.set()| {rhs} is a `(function|string)` thus it may be necessary to You may execute your own functions as well as |nvim-tree-api| functions e.g. >
define your own function to map complex functionality e.g. >
local function print_node_path() local function print_node_path()
local api = require('nvim-tree.api') local api = require('nvim-tree.api')
@@ -2508,9 +2461,6 @@ Hidden: >
NvimTreeModifiedFileHL NvimTreeHiddenIcon NvimTreeModifiedFileHL NvimTreeHiddenIcon
NvimTreeModifiedFolderHL NvimTreeHiddenFileHL NvimTreeModifiedFolderHL NvimTreeHiddenFileHL
< <
Hidden Display: >
NvimTreeHiddenDisplay Conceal
<
Opened: > Opened: >
NvimTreeOpenedHL Special NvimTreeOpenedHL Special
< <
@@ -2922,7 +2872,6 @@ 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|

View File

@@ -2,7 +2,6 @@ local lib = require "nvim-tree.lib"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local appearance = require "nvim-tree.appearance" local appearance = require "nvim-tree.appearance"
local renderer = require "nvim-tree.renderer" local renderer = require "nvim-tree.renderer"
local view = require "nvim-tree.view"
local commands = require "nvim-tree.commands" local commands = require "nvim-tree.commands"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local actions = require "nvim-tree.actions" local actions = require "nvim-tree.actions"
@@ -81,7 +80,11 @@ function M.change_root(path, bufnr)
end end
function M.tab_enter() function M.tab_enter()
if view.is_visible { any_tabpage = true } then local explorer = core.get_explorer();
if not explorer then
return
end
if explorer.view:is_visible { any_tabpage = true } then
local bufname = vim.api.nvim_buf_get_name(0) local bufname = vim.api.nvim_buf_get_name(0)
local ft local ft
@@ -96,13 +99,17 @@ function M.tab_enter()
return return
end end
end end
view.open { focus_tree = false } explorer.view:open { focus_tree = false }
renderer.draw() renderer.draw()
end end
end end
function M.open_on_directory() function M.open_on_directory()
local should_proceed = _config.hijack_directories.auto_open or view.is_visible() local explorer = core.get_explorer();
if not explorer then
return
end
local should_proceed = _config.hijack_directories.auto_open or explorer.view:is_visible()
if not should_proceed then if not should_proceed then
return return
end end
@@ -177,8 +184,12 @@ local function setup_autocommands(opts)
-- reset and draw (highlights) when colorscheme is changed -- reset and draw (highlights) when colorscheme is changed
create_nvim_tree_autocmd("ColorScheme", { create_nvim_tree_autocmd("ColorScheme", {
callback = function() callback = function()
local explorer = core.get_explorer();
if not explorer then
return
end
appearance.setup() appearance.setup()
view.reset_winhl() explorer.view:reset_winhl()
renderer.draw() renderer.draw()
end, end,
}) })
@@ -190,10 +201,14 @@ local function setup_autocommands(opts)
if not utils.is_nvim_tree_buf(0) then if not utils.is_nvim_tree_buf(0) then
return return
end end
local explorer = core.get_explorer();
if not explorer then
return
end
if opts.actions.open_file.eject then if opts.actions.open_file.eject then
view._prevent_buffer_override() explorer.view:_prevent_buffer_override()
else else
view.abandon_current_window() explorer.view:abandon_current_window()
end end
end, end,
}) })
@@ -331,8 +346,12 @@ local function setup_autocommands(opts)
create_nvim_tree_autocmd("WinLeave", { create_nvim_tree_autocmd("WinLeave", {
pattern = "NvimTree_*", pattern = "NvimTree_*",
callback = function() callback = function()
local explorer = core.get_explorer()
if not explorer then
return
end
if utils.is_nvim_tree_buf(0) then if utils.is_nvim_tree_buf(0) then
view.close() explorer.view:close()
end end
end, end,
}) })
@@ -398,7 +417,6 @@ 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",
@@ -648,7 +666,6 @@ 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" },
}, },
@@ -682,7 +699,6 @@ 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" },
@@ -786,8 +802,12 @@ end
function M.purge_all_state() function M.purge_all_state()
require("nvim-tree.watcher").purge_watchers() require("nvim-tree.watcher").purge_watchers()
view.close_all_tabs() local explorer = core.get_explorer()
view.abandon_all_windows() if not explorer then
return
end
explorer.view:close_all_tabs()
explorer.view:abandon_all_windows()
if core.get_explorer() ~= nil then if core.get_explorer() ~= nil then
git.purge_state() git.purge_state()
core.reset_explorer() core.reset_explorer()
@@ -837,9 +857,9 @@ function M.setup(conf)
require("nvim-tree.explorer").setup(opts) require("nvim-tree.explorer").setup(opts)
require("nvim-tree.git").setup(opts) require("nvim-tree.git").setup(opts)
require("nvim-tree.git.utils").setup(opts) require("nvim-tree.git.utils").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.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)
require("nvim-tree.watcher").setup(opts) require("nvim-tree.watcher").setup(opts)

View File

@@ -1,7 +1,7 @@
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local renderer = require "nvim-tree.renderer" local renderer = require "nvim-tree.renderer"
local reload = require "nvim-tree.explorer.reload"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local Iterator = require "nvim-tree.iterators.node-iterator" local Iterator = require "nvim-tree.iterators.node-iterator"
@@ -13,7 +13,7 @@ local running = {}
---@param path string relative or absolute ---@param path string relative or absolute
function M.fn(path) function M.fn(path)
local explorer = core.get_explorer() local explorer = core.get_explorer()
if not explorer or not view.is_visible() then if not explorer or not explorer.view:is_visible() then
return return
end end
@@ -32,7 +32,7 @@ function M.fn(path)
-- refresh the contents of all parents, expanding groups as needed -- refresh the contents of all parents, expanding groups as needed
if utils.get_node_from_path(path_real) == nil then if utils.get_node_from_path(path_real) == nil then
explorer:refresh_parent_nodes_for_path(vim.fn.fnamemodify(path_real, ":h")) reload.refresh_parent_nodes_for_path(vim.fn.fnamemodify(path_real, ":h"))
end end
local line = core.get_nodes_starting_line() local line = core.get_nodes_starting_line()
@@ -75,9 +75,9 @@ function M.fn(path)
end) end)
:iterate() :iterate()
if found and view.is_visible() then if found and explorer.view:is_visible() then
renderer.draw() renderer.draw()
view.set_cursor { line, 0 } explorer.view:set_cursor { line, 0 }
end end
running[path_real] = false running[path_real] = false

View File

@@ -9,39 +9,15 @@ local reloaders = require "nvim-tree.actions.reloaders"
local find_file = require("nvim-tree.actions.finders.find-file").fn local find_file = require("nvim-tree.actions.finders.find-file").fn
---@enum ACTION local M = {
local ACTION = { config = {},
copy = "copy",
cut = "cut",
} }
---@class Clipboard to handle all actions.fs clipboard API local clipboard = {
---@field config table hydrated user opts.filters cut = {},
---@field private explorer Explorer copy = {},
---@field private data table<ACTION, Node[]>
local Clipboard = {}
---@param opts table user options
---@param explorer Explorer
---@return Clipboard
function Clipboard:new(opts, explorer)
local o = {
explorer = explorer,
data = {
[ACTION.copy] = {},
[ACTION.cut] = {},
},
config = {
filesystem_watchers = opts.filesystem_watchers,
actions = opts.actions,
},
} }
setmetatable(o, self)
self.__index = self
return o
end
---@param source string ---@param source string
---@param destination string ---@param destination string
---@return boolean ---@return boolean
@@ -109,11 +85,11 @@ end
---@param source string ---@param source string
---@param dest string ---@param dest string
---@param action ACTION ---@param action_type string
---@param action_fn fun(source: string, dest: string) ---@param action_fn fun(source: string, dest: string)
---@return boolean|nil -- success ---@return boolean|nil -- success
---@return string|nil -- error message ---@return string|nil -- error message
local function do_single_paste(source, dest, action, action_fn) local function do_single_paste(source, dest, action_type, action_fn)
local dest_stats local dest_stats
local success, errmsg, errcode local success, errmsg, errcode
local notify_source = notify.render_path(source) local notify_source = notify.render_path(source)
@@ -122,14 +98,14 @@ local function do_single_paste(source, dest, action, action_fn)
dest_stats, errmsg, errcode = vim.loop.fs_stat(dest) dest_stats, errmsg, errcode = vim.loop.fs_stat(dest)
if not dest_stats and errcode ~= "ENOENT" then if not dest_stats and errcode ~= "ENOENT" then
notify.error("Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or "???")) notify.error("Could not " .. action_type .. " " .. notify_source .. " - " .. (errmsg or "???"))
return false, errmsg return false, errmsg
end end
local function on_process() local function on_process()
success, errmsg = action_fn(source, dest) success, errmsg = action_fn(source, dest)
if not success then if not success then
notify.error("Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or "???")) notify.error("Could not " .. action_type .. " " .. notify_source .. " - " .. (errmsg or "???"))
return false, errmsg return false, errmsg
end end
@@ -147,7 +123,7 @@ local function do_single_paste(source, dest, action, action_fn)
vim.ui.input(input_opts, function(new_dest) vim.ui.input(input_opts, function(new_dest)
utils.clear_prompt() utils.clear_prompt()
if new_dest then if new_dest then
do_single_paste(source, new_dest, action, action_fn) do_single_paste(source, new_dest, action_type, action_fn)
end end
end) end)
else else
@@ -161,7 +137,7 @@ local function do_single_paste(source, dest, action, action_fn)
vim.ui.input(input_opts, function(new_dest) vim.ui.input(input_opts, function(new_dest)
utils.clear_prompt() utils.clear_prompt()
if new_dest then if new_dest then
do_single_paste(source, new_dest, action, action_fn) do_single_paste(source, new_dest, action_type, action_fn)
end end
end) end)
end end
@@ -189,42 +165,37 @@ local function toggle(node, clip)
notify.info(notify_node .. " added to clipboard.") notify.info(notify_node .. " added to clipboard.")
end end
---Clear copied and cut function M.clear_clipboard()
function Clipboard:clear_clipboard() clipboard.cut = {}
self.data[ACTION.copy] = {} clipboard.copy = {}
self.data[ACTION.cut] = {}
notify.info "Clipboard has been emptied." notify.info "Clipboard has been emptied."
renderer.draw() renderer.draw()
end end
---Copy one node
---@param node Node ---@param node Node
function Clipboard:copy(node) function M.copy(node)
utils.array_remove(self.data[ACTION.cut], node) utils.array_remove(clipboard.cut, node)
toggle(node, self.data[ACTION.copy]) toggle(node, clipboard.copy)
renderer.draw() renderer.draw()
end end
---Cut one node
---@param node Node ---@param node Node
function Clipboard:cut(node) function M.cut(node)
utils.array_remove(self.data[ACTION.copy], node) utils.array_remove(clipboard.copy, node)
toggle(node, self.data[ACTION.cut]) toggle(node, clipboard.cut)
renderer.draw() renderer.draw()
end end
---Paste cut or cop
---@private
---@param node Node ---@param node Node
---@param action ACTION ---@param action_type string
---@param action_fn fun(source: string, dest: string) ---@param action_fn fun(source: string, dest: string)
function Clipboard:do_paste(node, action, action_fn) local function do_paste(node, action_type, action_fn)
node = lib.get_last_group_node(node) node = lib.get_last_group_node(node)
local explorer = core.get_explorer() local explorer = core.get_explorer()
if node.name == ".." and explorer then if node.name == ".." and explorer then
node = explorer node = explorer
end end
local clip = self.data[action] local clip = clipboard[action_type]
if #clip == 0 then if #clip == 0 then
return return
end end
@@ -233,7 +204,7 @@ function Clipboard:do_paste(node, action, action_fn)
local stats, errmsg, errcode = vim.loop.fs_stat(destination) local stats, errmsg, errcode = vim.loop.fs_stat(destination)
if not stats and errcode ~= "ENOENT" then if not stats and errcode ~= "ENOENT" then
log.line("copy_paste", "do_paste fs_stat '%s' failed '%s'", destination, errmsg) log.line("copy_paste", "do_paste fs_stat '%s' failed '%s'", destination, errmsg)
notify.error("Could not " .. action .. " " .. notify.render_path(destination) .. " - " .. (errmsg or "???")) notify.error("Could not " .. action_type .. " " .. notify.render_path(destination) .. " - " .. (errmsg or "???"))
return return
end end
local is_dir = stats and stats.type == "directory" local is_dir = stats and stats.type == "directory"
@@ -243,11 +214,11 @@ function Clipboard:do_paste(node, action, action_fn)
for _, _node in ipairs(clip) do for _, _node in ipairs(clip) do
local dest = utils.path_join { destination, _node.name } local dest = utils.path_join { destination, _node.name }
do_single_paste(_node.absolute_path, dest, action, action_fn) do_single_paste(_node.absolute_path, dest, action_type, action_fn)
end end
self.data[action] = {} clipboard[action_type] = {}
if not self.config.filesystem_watchers.enable then if not M.config.filesystem_watchers.enable then
reloaders.reload_explorer() reloaders.reload_explorer()
end end
end end
@@ -275,27 +246,26 @@ local function do_cut(source, destination)
return true return true
end end
---Paste cut (if present) or copy (if present)
---@param node Node ---@param node Node
function Clipboard:paste(node) function M.paste(node)
if self.data[ACTION.cut][1] ~= nil then if clipboard.cut[1] ~= nil then
self:do_paste(node, ACTION.cut, do_cut) do_paste(node, "cut", do_cut)
elseif self.data[ACTION.copy][1] ~= nil then else
self:do_paste(node, ACTION.copy, do_copy) do_paste(node, "copy", do_copy)
end end
end end
function Clipboard:print_clipboard() function M.print_clipboard()
local content = {} local content = {}
if #self.data[ACTION.cut] > 0 then if #clipboard.cut > 0 then
table.insert(content, "Cut") table.insert(content, "Cut")
for _, node in pairs(self.data[ACTION.cut]) do for _, node in pairs(clipboard.cut) do
table.insert(content, " * " .. (notify.render_path(node.absolute_path))) table.insert(content, " * " .. (notify.render_path(node.absolute_path)))
end end
end end
if #self.data[ACTION.copy] > 0 then if #clipboard.copy > 0 then
table.insert(content, "Copy") table.insert(content, "Copy")
for _, node in pairs(self.data[ACTION.copy]) do for _, node in pairs(clipboard.copy) do
table.insert(content, " * " .. (notify.render_path(node.absolute_path))) table.insert(content, " * " .. (notify.render_path(node.absolute_path)))
end end
end end
@@ -304,10 +274,10 @@ function Clipboard:print_clipboard()
end end
---@param content string ---@param content string
function Clipboard:copy_to_reg(content) local function copy_to_clipboard(content)
local clipboard_name local clipboard_name
local reg local reg
if self.config.actions.use_system_clipboard == true then if M.config.actions.use_system_clipboard == true then
clipboard_name = "system" clipboard_name = "system"
reg = "+" reg = "+"
else else
@@ -328,18 +298,18 @@ function Clipboard:copy_to_reg(content)
end end
---@param node Node ---@param node Node
function Clipboard:copy_filename(node) function M.copy_filename(node)
self:copy_to_reg(node.name) copy_to_clipboard(node.name)
end end
---@param node Node ---@param node Node
function Clipboard:copy_basename(node) function M.copy_basename(node)
local basename = vim.fn.fnamemodify(node.name, ":r") local basename = vim.fn.fnamemodify(node.name, ":r")
self:copy_to_reg(basename) copy_to_clipboard(basename)
end end
---@param node Node ---@param node Node
function Clipboard:copy_path(node) function M.copy_path(node)
local absolute_path = node.absolute_path local absolute_path = node.absolute_path
local cwd = core.get_cwd() local cwd = core.get_cwd()
if cwd == nil then if cwd == nil then
@@ -348,28 +318,33 @@ function Clipboard:copy_path(node)
local relative_path = utils.path_relative(absolute_path, cwd) local relative_path = utils.path_relative(absolute_path, cwd)
local content = node.nodes ~= nil and utils.path_add_trailing(relative_path) or relative_path local content = node.nodes ~= nil and utils.path_add_trailing(relative_path) or relative_path
self:copy_to_reg(content) copy_to_clipboard(content)
end end
---@param node Node ---@param node Node
function Clipboard:copy_absolute_path(node) function M.copy_absolute_path(node)
local absolute_path = node.absolute_path local absolute_path = node.absolute_path
local content = node.nodes ~= nil and utils.path_add_trailing(absolute_path) or absolute_path local content = node.nodes ~= nil and utils.path_add_trailing(absolute_path) or absolute_path
self:copy_to_reg(content) copy_to_clipboard(content)
end end
---Node is cut. Will not be copied. ---Node is cut. Will not be copied.
---@param node Node ---@param node Node
---@return boolean ---@return boolean
function Clipboard:is_cut(node) function M.is_cut(node)
return vim.tbl_contains(self.data[ACTION.cut], node) return vim.tbl_contains(clipboard.cut, node)
end end
---Node is copied. Will not be cut. ---Node is copied. Will not be cut.
---@param node Node ---@param node Node
---@return boolean ---@return boolean
function Clipboard:is_copied(node) function M.is_copied(node)
return vim.tbl_contains(self.data[ACTION.copy], node) return vim.tbl_contains(clipboard.copy, node)
end end
return Clipboard function M.setup(opts)
M.config.filesystem_watchers = opts.filesystem_watchers
M.config.actions = opts.actions
end
return M

View File

@@ -1,11 +1,13 @@
local M = {} local M = {}
M.copy_paste = require "nvim-tree.actions.fs.copy-paste"
M.create_file = require "nvim-tree.actions.fs.create-file" M.create_file = require "nvim-tree.actions.fs.create-file"
M.remove_file = require "nvim-tree.actions.fs.remove-file" M.remove_file = require "nvim-tree.actions.fs.remove-file"
M.rename_file = require "nvim-tree.actions.fs.rename-file" M.rename_file = require "nvim-tree.actions.fs.rename-file"
M.trash = require "nvim-tree.actions.fs.trash" M.trash = require "nvim-tree.actions.fs.trash"
function M.setup(opts) function M.setup(opts)
M.copy_paste.setup(opts)
M.remove_file.setup(opts) M.remove_file.setup(opts)
M.rename_file.setup(opts) M.rename_file.setup(opts)
M.trash.setup(opts) M.trash.setup(opts)

View File

@@ -1,6 +1,5 @@
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
local view = require "nvim-tree.view"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
@@ -10,10 +9,14 @@ local M = {
---@param windows integer[] ---@param windows integer[]
local function close_windows(windows) local function close_windows(windows)
local explorer = require "nvim-tree.core".get_explorer()
if not explorer then
return
end
-- Prevent from closing when the win count equals 1 or 2, -- Prevent from closing when the win count equals 1 or 2,
-- where the win to remove could be the last opened. -- where the win to remove could be the last opened.
-- For details see #2503. -- For details see #2503.
if view.View.float.enable and #vim.api.nvim_list_wins() < 3 then if explorer.view.View.float.enable and #vim.api.nvim_list_wins() < 3 then
return return
end end
@@ -26,16 +29,20 @@ end
---@param absolute_path string ---@param absolute_path string
local function clear_buffer(absolute_path) local function clear_buffer(absolute_path)
local explorer = require "nvim-tree.core".get_explorer()
if not explorer then
return
end
local bufs = vim.fn.getbufinfo { bufloaded = 1, buflisted = 1 } local bufs = vim.fn.getbufinfo { bufloaded = 1, buflisted = 1 }
for _, buf in pairs(bufs) do for _, buf in pairs(bufs) do
if buf.name == absolute_path then if buf.name == absolute_path then
local tree_winnr = vim.api.nvim_get_current_win() local tree_winnr = vim.api.nvim_get_current_win()
if buf.hidden == 0 and (#bufs > 1 or view.View.float.enable) then if buf.hidden == 0 and (#bufs > 1 or explorer.view.View.float.enable) then
vim.api.nvim_set_current_win(buf.windows[1]) vim.api.nvim_set_current_win(buf.windows[1])
vim.cmd ":bn" vim.cmd ":bn"
end end
vim.api.nvim_buf_delete(buf.bufnr, { force = true }) vim.api.nvim_buf_delete(buf.bufnr, { force = true })
if not view.View.float.quit_on_focus_loss then if not explorer.view.View.float.quit_on_focus_loss then
vim.api.nvim_set_current_win(tree_winnr) vim.api.nvim_set_current_win(tree_winnr)
end end
if M.config.actions.remove_file.close_window then if M.config.actions.remove_file.close_window then
@@ -56,18 +63,13 @@ local function remove_dir(cwd)
end end
while true do while true do
local name, _ = vim.loop.fs_scandir_next(handle) local name, t = vim.loop.fs_scandir_next(handle)
if not name then if not name then
break break
end end
local new_cwd = utils.path_join { cwd, name } local new_cwd = utils.path_join { cwd, name }
if t == "directory" then
-- Type must come from fs_stat and not fs_scandir_next to maintain sshfs compatibility
local stat = vim.loop.fs_stat(new_cwd)
local type = stat and stat.type or nil
if type == "directory" then
local success = remove_dir(new_cwd) local success = remove_dir(new_cwd)
if not success then if not success then
return false return false

View File

@@ -1,5 +1,4 @@
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local explorer_node = require "nvim-tree.explorer.node" local explorer_node = require "nvim-tree.explorer.node"
@@ -33,15 +32,11 @@ end
---@param what string type of status ---@param what string type of status
---@param skip_gitignored boolean default false ---@param skip_gitignored boolean default false
local function move(where, what, skip_gitignored) local function move(where, what, skip_gitignored)
local node_cur = lib.get_node_at_cursor()
local first_node_line = core.get_nodes_starting_line() local first_node_line = core.get_nodes_starting_line()
local nodes_by_line = utils.get_nodes_by_line(core.get_explorer().nodes, first_node_line) local nodes_by_line = utils.get_nodes_by_line(core.get_explorer().nodes, first_node_line)
local iter_start, iter_end, iter_step, cur, first, nex local iter_start, iter_end, iter_step, cur, first, nex
local cursor = lib.get_cursor_position()
if cursor and cursor[1] < first_node_line then
cur = cursor[1]
end
if where == "next" then if where == "next" then
iter_start, iter_end, iter_step = first_node_line, #nodes_by_line, 1 iter_start, iter_end, iter_step = first_node_line, #nodes_by_line, 1
elseif where == "prev" then elseif where == "prev" then
@@ -56,7 +51,7 @@ local function move(where, what, skip_gitignored)
first = line first = line
end end
if cursor and line == cursor[1] then if node == node_cur then
cur = line cur = line
elseif valid and cur then elseif valid and cur then
nex = line nex = line
@@ -64,10 +59,14 @@ local function move(where, what, skip_gitignored)
end end
end end
local explorer = core.get_explorer()
if not explorer then
return
end
if nex then if nex then
view.set_cursor { nex, 0 } explorer.view:set_cursor { nex, 0 }
elseif vim.o.wrapscan and first then elseif vim.o.wrapscan and first then
view.set_cursor { first, 0 } explorer.view:set_cursor { first, 0 }
end end
end end
@@ -186,13 +185,19 @@ local function move_prev_recursive(what, skip_gitignored)
-- 4.3) -- 4.3)
if node_init.name == ".." then -- root node if node_init.name == ".." then -- root node
view.set_cursor { 1, 0 } -- move to root node (position 1) local explorer = core.get_explorer()
if explorer then
explorer.view:set_cursor { 1, 0 } -- move to root node (position 1)
end
else else
local node_init_line = utils.find_node_line(node_init) local node_init_line = utils.find_node_line(node_init)
if node_init_line < 0 then if node_init_line < 0 then
return return
end end
view.set_cursor { node_init_line, 0 } local explorer = core.get_explorer()
if explorer then
explorer.view:set_cursor { node_init_line, 0 } -- move to root node (position 1)
end
end end
-- 4.4) -- 4.4)

View File

@@ -1,5 +1,4 @@
local renderer = require "nvim-tree.renderer" local renderer = require "nvim-tree.renderer"
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
@@ -21,14 +20,20 @@ function M.fn(should_close)
local parent = utils.get_parent_of_group(node).parent local parent = utils.get_parent_of_group(node).parent
if not parent or not parent.parent then if not parent or not parent.parent then
return view.set_cursor { 1, 0 } local explorer = core.get_explorer()
if explorer then
return explorer.view:set_cursor { 1, 0 }
end
end end
local _, line = utils.find_node(core.get_explorer().nodes, function(n) local _, line = utils.find_node(core.get_explorer().nodes, function(n)
return n.absolute_path == parent.absolute_path return n.absolute_path == parent.absolute_path
end) end)
view.set_cursor { line + 1, 0 } local explorer = core.get_explorer()
if explorer then
explorer.view:set_cursor { line + 1, 0 }
end
if should_close then if should_close then
parent.open = false parent.open = false
renderer.draw() renderer.draw()

View File

@@ -2,7 +2,6 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
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 M = {} local M = {}
@@ -19,9 +18,11 @@ end
---Get all windows in the current tabpage that aren't NvimTree. ---Get all windows in the current tabpage that aren't NvimTree.
---@return table with valid win_ids ---@return table with valid win_ids
local function usable_win_ids() local function usable_win_ids()
local explorer = require "nvim-tree.core".get_explorer()
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = vim.api.nvim_get_current_tabpage()
local win_ids = vim.api.nvim_tabpage_list_wins(tabpage) local win_ids = vim.api.nvim_tabpage_list_wins(tabpage)
local tree_winid = view.get_winnr(tabpage) local tree_winid = explorer and explorer.view:get_winnr(tabpage)
return vim.tbl_filter(function(id) return vim.tbl_filter(function(id)
local bufid = vim.api.nvim_win_get_buf(id) local bufid = vim.api.nvim_win_get_buf(id)
@@ -167,8 +168,6 @@ 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 })
@@ -178,7 +177,6 @@ local function pick_win_id()
end end
end end
end end
end
vim.o.laststatus = laststatus vim.o.laststatus = laststatus
@@ -191,7 +189,10 @@ end
local function open_file_in_tab(filename) local function open_file_in_tab(filename)
if M.quit_on_open then if M.quit_on_open then
view.close() local explorer = require "nvim-tree.core".get_explorer()
if explorer then
explorer.view:close()
end
end end
if M.relative_path then if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd()) filename = utils.path_relative(filename, vim.fn.getcwd())
@@ -201,7 +202,10 @@ end
local function drop(filename) local function drop(filename)
if M.quit_on_open then if M.quit_on_open then
view.close() local explorer = require"nvim-tree.core".get_explorer()
if explorer then
explorer.view:close()
end
end end
if M.relative_path then if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd()) filename = utils.path_relative(filename, vim.fn.getcwd())
@@ -211,7 +215,10 @@ end
local function tab_drop(filename) local function tab_drop(filename)
if M.quit_on_open then if M.quit_on_open then
view.close() local explorer = require"nvim-tree.core".get_explorer()
if explorer then
explorer.view:close()
end
end end
if M.relative_path then if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd()) filename = utils.path_relative(filename, vim.fn.getcwd())
@@ -232,7 +239,10 @@ local function on_preview(buf_loaded)
once = true, once = true,
}) })
end end
view.focus() local explorer = require"nvim-tree.core".get_explorer()
if explorer then
explorer.view:focus()
end
end end
local function get_target_winid(mode) local function get_target_winid(mode)
@@ -290,7 +300,8 @@ local function open_in_new_window(filename, mode)
end, vim.api.nvim_list_wins()) end, vim.api.nvim_list_wins())
local create_new_window = #win_ids == 1 -- This implies that the nvim-tree window is the only one local create_new_window = #win_ids == 1 -- This implies that the nvim-tree window is the only one
local new_window_side = (view.View.side == "right") and "aboveleft" or "belowright" local explorer = require"nvim-tree.core".get_explorer()
local new_window_side = (explorer and view.View.side == "right") and "aboveleft" or "belowright"
-- Target is invalid: create new window -- Target is invalid: create new window
if not vim.tbl_contains(win_ids, target_winid) then if not vim.tbl_contains(win_ids, target_winid) then
@@ -322,7 +333,7 @@ local function open_in_new_window(filename, mode)
end end
end end
if (mode == "preview" or mode == "preview_no_picker") and view.View.float.enable then if (mode == "preview" or mode == "preview_no_picker") and explorer and explorer.view.View.float.enable then
-- ignore "WinLeave" autocmd on preview -- ignore "WinLeave" autocmd on preview
-- because the registered "WinLeave" -- because the registered "WinLeave"
-- will kill the floating window immediately -- will kill the floating window immediately
@@ -362,7 +373,10 @@ local function is_already_loaded(filename)
end end
local function edit_in_current_buf(filename) local function edit_in_current_buf(filename)
require("nvim-tree.view").abandon_current_window() local explorer = require"nvim-tree.core".get_explorer()
if explorer then
explorer.view:abandon_current_window()
end
if M.relative_path then if M.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd()) filename = utils.path_relative(filename, vim.fn.getcwd())
end end
@@ -407,7 +421,10 @@ function M.fn(mode, filename)
end end
if M.resize_window then if M.resize_window then
view.resize() local explorer = require"nvim-tree.core".get_explorer()
if explorer then
explorer.view:resize()
end
end end
if mode == "preview" or mode == "preview_no_picker" then if mode == "preview" or mode == "preview_no_picker" then
@@ -415,7 +432,10 @@ function M.fn(mode, filename)
end end
if M.quit_on_open then if M.quit_on_open then
view.close() local explorer = require"nvim-tree.core".get_explorer()
if explorer then
explorer.view:close()
end
end end
end end

View File

@@ -1,22 +1,20 @@
local git = require "nvim-tree.git" local git = require "nvim-tree.git"
local view = require "nvim-tree.view"
local renderer = require "nvim-tree.renderer" local renderer = require "nvim-tree.renderer"
local explorer_module = require "nvim-tree.explorer"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local explorer_node = require "nvim-tree.explorer.node" local explorer_node = require "nvim-tree.explorer.node"
local Iterator = require "nvim-tree.iterators.node-iterator" local Iterator = require "nvim-tree.iterators.node-iterator"
local M = {} local M = {}
---@param explorer Explorer|nil ---@param node Explorer|nil
---@param projects table ---@param projects table
local function refresh_nodes(explorer, projects) local function refresh_nodes(node, projects)
Iterator.builder({ explorer }) Iterator.builder({ node })
:applier(function(n) :applier(function(n)
if n.nodes then if n.nodes then
local toplevel = git.get_toplevel(n.cwd or n.link_to or n.absolute_path) local toplevel = git.get_toplevel(n.cwd or n.link_to or n.absolute_path)
if explorer then explorer_module.reload(n, projects[toplevel] or {})
explorer:reload(n, projects[toplevel] or {})
end
end end
end) end)
:recursor(function(n) :recursor(function(n)
@@ -44,14 +42,15 @@ end
local event_running = false local event_running = false
function M.reload_explorer() function M.reload_explorer()
if event_running or not core.get_explorer() or vim.v.exiting ~= vim.NIL then local explorer = core.get_explorer()
if event_running or not explorer or vim.v.exiting ~= vim.NIL then
return return
end end
event_running = true event_running = true
local projects = git.reload() local projects = git.reload()
refresh_nodes(core.get_explorer(), projects) refresh_nodes(core.get_explorer(), projects)
if view.is_visible() then if explorer.view:is_visible() then
renderer.draw() renderer.draw()
end end
event_running = false event_running = false

View File

@@ -1,6 +1,5 @@
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local view = require "nvim-tree.view"
local finders_find_file = require "nvim-tree.actions.finders.find-file" local finders_find_file = require "nvim-tree.actions.finders.find-file"
local M = {} local M = {}
@@ -41,11 +40,12 @@ function M.fn(opts)
return return
end end
if view.is_visible() then local explorer = core.get_explorer()
if explorer and explorer.view:is_visible() then
-- focus -- focus
if opts.focus then if opts.focus then
lib.set_target_win() lib.set_target_win()
view.focus() explorer.view:focus()
end end
elseif opts.open then elseif opts.open then
-- open -- open

View File

@@ -1,5 +1,4 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local view = require "nvim-tree.view"
local finders_find_file = require "nvim-tree.actions.finders.find-file" local finders_find_file = require "nvim-tree.actions.finders.find-file"
local M = {} local M = {}
@@ -23,10 +22,11 @@ function M.fn(opts)
opts.path = nil opts.path = nil
end end
if view.is_visible() then local explorer = require"nvim-tree.core".get_explorer()
if explorer and explorer.view:is_visible() then
-- focus -- focus
lib.set_target_win() lib.set_target_win()
view.focus() explorer.view:focus()
else else
-- open -- open
lib.open { lib.open {

View File

@@ -1,14 +1,18 @@
local view = require "nvim-tree.view"
local M = {} local M = {}
---Resize the tree, persisting the new size. ---Resize the tree, persisting the new size.
---@param opts ApiTreeResizeOpts|nil ---@param opts ApiTreeResizeOpts|nil
function M.fn(opts) function M.fn(opts)
local explorer = require"nvim-tree.core".get_explorer()
if not explorer then
return
end
if opts == nil then if opts == nil then
-- reset to config values -- reset to config values
view.configure_width() explorer.view:configure_width()
view.resize() explorer.view:resize()
return return
end end
@@ -16,19 +20,19 @@ function M.fn(opts)
local width_cfg = options.width local width_cfg = options.width
if width_cfg ~= nil then if width_cfg ~= nil then
view.configure_width(width_cfg) explorer.view:configure_width(width_cfg)
view.resize() explorer.view:resize()
return return
end end
if not view.is_width_determined() then if not explorer.view:is_width_determined() then
-- {absolute} and {relative} do nothing when {width} is a function. -- {absolute} and {relative} do nothing when {width} is a function.
return return
end end
local absolute = options.absolute local absolute = options.absolute
if type(absolute) == "number" then if type(absolute) == "number" then
view.resize(absolute) explorer.view:resize(absolute)
return return
end end
@@ -39,7 +43,7 @@ function M.fn(opts)
relative_size = "+" .. relative_size relative_size = "+" .. relative_size
end end
view.resize(relative_size) explorer.view:resize(relative_size)
return return
end end
end end

View File

@@ -1,5 +1,4 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local view = require "nvim-tree.view"
local finders_find_file = require "nvim-tree.actions.finders.find-file" local finders_find_file = require "nvim-tree.actions.finders.find-file"
local M = {} local M = {}
@@ -40,9 +39,14 @@ function M.fn(opts, no_focus, cwd, bang)
opts.path = nil opts.path = nil
end end
if view.is_visible() then local explorer = require"nvim-tree.core".get_explorer()
if not explorer then
return
end
if explorer.view:is_visible() then
-- close -- close
view.close() explorer.view:close()
else else
-- open -- open
lib.open { lib.open {

View File

@@ -1,11 +1,14 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
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 marks_navigation = require "nvim-tree.marks.navigation"
local marks_bulk_delete = require "nvim-tree.marks.bulk-delete"
local marks_bulk_trash = require "nvim-tree.marks.bulk-trash"
local marks_bulk_move = require "nvim-tree.marks.bulk-move"
local keymap = require "nvim-tree.keymap" local keymap = require "nvim-tree.keymap"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
@@ -72,6 +75,18 @@ local function wrap_node_or_nil(fn)
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. ---Invoke a member's method on the singleton explorer.
---Print error when setup not called. ---Print error when setup not called.
---@param explorer_member string explorer member name ---@param explorer_member string explorer member name
@@ -105,9 +120,9 @@ Api.tree.focus = Api.tree.open
---@field focus boolean|nil default true ---@field focus boolean|nil default true
Api.tree.toggle = wrap(actions.tree.toggle.fn) Api.tree.toggle = wrap(actions.tree.toggle.fn)
Api.tree.close = wrap(view.close) Api.tree.close = wrap_explorer_member("view", "close")
Api.tree.close_in_this_tab = wrap(view.close_this_tab_only) Api.tree.close_in_this_tab = wrap_explorer_member("view", "close_this_tab_only")
Api.tree.close_in_all_tabs = wrap(view.close_all_tabs) Api.tree.close_in_all_tabs = wrap_explorer_member("view", "close_all_tabs")
Api.tree.reload = wrap(actions.reloaders.reload_explorer) Api.tree.reload = wrap(actions.reloaders.reload_explorer)
---@class ApiTreeResizeOpts ---@class ApiTreeResizeOpts
@@ -159,12 +174,12 @@ Api.tree.is_tree_buf = wrap(utils.is_nvim_tree_buf)
---@field tabpage number|nil ---@field tabpage number|nil
---@field any_tabpage boolean|nil default false ---@field any_tabpage boolean|nil default false
Api.tree.is_visible = wrap(view.is_visible) Api.tree.is_visible = wrap_explorer_member("view", "is_visible")
---@class ApiTreeWinIdOpts ---@class ApiTreeWinIdOpts
---@field tabpage number|nil default nil ---@field tabpage number|nil default nil
Api.tree.winid = wrap(view.winid) Api.tree.winid = wrap_explorer_member("view", "winid")
Api.fs.create = wrap_node_or_nil(actions.fs.create_file.fn) Api.fs.create = wrap_node_or_nil(actions.fs.create_file.fn)
Api.fs.remove = wrap_node(actions.fs.remove_file.fn) Api.fs.remove = wrap_node(actions.fs.remove_file.fn)
@@ -174,15 +189,15 @@ Api.fs.rename = wrap_node(actions.fs.rename_file.fn ":t")
Api.fs.rename_sub = wrap_node(actions.fs.rename_file.fn ":p:h") Api.fs.rename_sub = wrap_node(actions.fs.rename_file.fn ":p:h")
Api.fs.rename_basename = wrap_node(actions.fs.rename_file.fn ":t:r") Api.fs.rename_basename = wrap_node(actions.fs.rename_file.fn ":t:r")
Api.fs.rename_full = wrap_node(actions.fs.rename_file.fn ":p") Api.fs.rename_full = wrap_node(actions.fs.rename_file.fn ":p")
Api.fs.cut = wrap_node(wrap_explorer_member("clipboard", "cut")) Api.fs.cut = wrap_node(actions.fs.copy_paste.cut)
Api.fs.paste = wrap_node(wrap_explorer_member("clipboard", "paste")) Api.fs.paste = wrap_node(actions.fs.copy_paste.paste)
Api.fs.clear_clipboard = wrap_explorer_member("clipboard", "clear_clipboard") Api.fs.clear_clipboard = wrap(actions.fs.copy_paste.clear_clipboard)
Api.fs.print_clipboard = wrap_explorer_member("clipboard", "print_clipboard") Api.fs.print_clipboard = wrap(actions.fs.copy_paste.print_clipboard)
Api.fs.copy.node = wrap_node(wrap_explorer_member("clipboard", "copy")) Api.fs.copy.node = wrap_node(actions.fs.copy_paste.copy)
Api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_absolute_path")) Api.fs.copy.absolute_path = wrap_node(actions.fs.copy_paste.copy_absolute_path)
Api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename")) Api.fs.copy.filename = wrap_node(actions.fs.copy_paste.copy_filename)
Api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename")) Api.fs.copy.basename = wrap_node(actions.fs.copy_paste.copy_basename)
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path")) Api.fs.copy.relative_path = wrap_node(actions.fs.copy_paste.copy_path)
---@param mode string ---@param mode string
---@param node table ---@param node table
@@ -251,16 +266,16 @@ Api.events.Event = events.Event
Api.live_filter.start = wrap_explorer_member("live_filter", "start_filtering") Api.live_filter.start = wrap_explorer_member("live_filter", "start_filtering")
Api.live_filter.clear = wrap_explorer_member("live_filter", "clear_filter") Api.live_filter.clear = wrap_explorer_member("live_filter", "clear_filter")
Api.marks.get = wrap_node(wrap_explorer_member("marks", "get")) Api.marks.get = wrap_node(wrap_explorer_member("marks", "get_mark"))
Api.marks.list = wrap_explorer_member("marks", "list") Api.marks.list = wrap_explorer_member("marks", "get_marks")
Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle")) Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle_mark"))
Api.marks.clear = wrap_explorer_member("marks", "clear") Api.marks.clear = wrap_explorer_member("marks", "clear_marks")
Api.marks.bulk.delete = wrap_explorer_member("marks", "bulk_delete") Api.marks.bulk.delete = wrap_explorer(marks_bulk_delete.bulk_delete)
Api.marks.bulk.trash = wrap_explorer_member("marks", "bulk_trash") Api.marks.bulk.trash = wrap_explorer(marks_bulk_trash.bulk_trash)
Api.marks.bulk.move = wrap_explorer_member("marks", "bulk_move") Api.marks.bulk.move = wrap_explorer(marks_bulk_move.bulk_move)
Api.marks.navigate.next = wrap_explorer_member("marks", "navigate_next") Api.marks.navigate.next = wrap(marks_navigation.next)
Api.marks.navigate.prev = wrap_explorer_member("marks", "navigate_prev") Api.marks.navigate.prev = wrap(marks_navigation.prev)
Api.marks.navigate.select = wrap_explorer_member("marks", "navigate_select") Api.marks.navigate.select = wrap(marks_navigation.select)
Api.config.mappings.get_keymap = wrap(keymap.get_keymap) Api.config.mappings.get_keymap = wrap(keymap.get_keymap)
Api.config.mappings.get_keymap_default = wrap(keymap.get_keymap_default) Api.config.mappings.get_keymap_default = wrap(keymap.get_keymap_default)

View File

@@ -14,7 +14,6 @@ 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" },
@@ -82,9 +81,6 @@ M.HIGHLIGHT_GROUPS = {
{ group = "NvimTreeHiddenFileHL", link = "NvimTreeHiddenIcon" }, { group = "NvimTreeHiddenFileHL", link = "NvimTreeHiddenIcon" },
{ group = "NvimTreeHiddenFolderHL", link = "NvimTreeHiddenFileHL" }, { group = "NvimTreeHiddenFolderHL", link = "NvimTreeHiddenFileHL" },
-- Hidden Display
{ group = "NvimTreeHiddenDisplay", link = "Conceal" },
-- Opened -- Opened
{ group = "NvimTreeOpenedHL", link = "Special" }, { group = "NvimTreeOpenedHL", link = "Special" },

View File

@@ -1,5 +1,4 @@
local api = require "nvim-tree.api" local api = require "nvim-tree.api"
local view = require "nvim-tree.view"
local M = {} local M = {}
@@ -111,7 +110,11 @@ local CMDS = {
bar = true, bar = true,
}, },
command = function(c) command = function(c)
view.resize(c.args) local explorer = require "nvim-tree.core".get_explorer();
if not explorer then
return
end
explorer.view:resize(c.args)
end, end,
}, },
{ {

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 view = require "nvim-tree.view"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local M = {} local M = {}
@@ -16,7 +15,7 @@ function M.init(foldername)
if TreeExplorer then if TreeExplorer then
TreeExplorer:destroy() TreeExplorer:destroy()
end end
TreeExplorer = explorer:new(foldername) TreeExplorer = explorer.Explorer.new(foldername)
if not first_init_done then if not first_init_done then
events._dispatch_ready() events._dispatch_ready()
first_init_done = true first_init_done = true
@@ -41,7 +40,7 @@ end
---@return integer ---@return integer
function M.get_nodes_starting_line() function M.get_nodes_starting_line()
local offset = 1 local offset = 1
if view.is_root_folder_visible(M.get_cwd()) then if TreeExplorer and TreeExplorer.view:is_root_folder_visible(M.get_cwd()) then
offset = offset + 1 offset = offset + 1
end end
if TreeExplorer and TreeExplorer.live_filter.filter then if TreeExplorer and TreeExplorer.live_filter.filter then

View File

@@ -1,5 +1,4 @@
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local M = {} local M = {}
@@ -164,7 +163,9 @@ function M.update()
end end
end end
log.profile_end(profile) log.profile_end(profile)
if view.is_buf_valid(view.get_bufnr()) then local explorer = require "nvim-tree.core".get_explorer()
if explorer and explorer.view:is_buf_valid(explorer.view:get_bufnr()) then
require("nvim-tree.renderer").draw() require("nvim-tree.renderer").draw()
end end
end) end)

View File

@@ -19,15 +19,4 @@ M.ICON_PLACEMENT = {
right_align = 4, 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

@@ -4,7 +4,6 @@ local explorer_node = require "nvim-tree.explorer.node"
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 Watcher = require "nvim-tree.watcher" local Watcher = require "nvim-tree.watcher"
local M = {} local M = {}
@@ -17,42 +16,26 @@ local M = {}
local function populate_children(handle, cwd, node, git_status, parent) 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 = parent.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, _ = vim.loop.fs_scandir_next(handle) local name, t = vim.loop.fs_scandir_next(handle)
if not name then if not name then
break break
end end
local abs = utils.path_join { cwd, name } local abs = utils.path_join { cwd, name }
if Watcher.is_fs_event_capable(abs) then
local profile = log.profile_start("explore populate_children %s", abs) local profile = log.profile_start("explore populate_children %s", abs)
---@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)
-- Type must come from fs_stat and not fs_scandir_next to maintain sshfs compatibility if not parent.filters:should_filter(abs, stat, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
local type = stat and stat.type or nil
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
local child = nil local child = nil
if type == "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)
elseif type == "file" then elseif t == "file" then
child = builders.file(node, abs, name, stat) child = builders.file(node, abs, name, stat)
elseif type == "link" then elseif t == "link" then
local link = builders.link(node, abs, name, stat) local link = builders.link(node, abs, name, stat)
if link.link_to ~= nil then if link.link_to ~= nil then
child = link child = link
@@ -63,18 +46,11 @@ local function populate_children(handle, cwd, node, git_status, parent)
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)
end end
end end
end
---@param node Node ---@param node Node
---@param status table ---@param status table

View File

@@ -1,5 +1,4 @@
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
---@class Filters to handle all opts.filters and related API ---@class Filters to handle all opts.filters and related API
---@field config table hydrated user opts.filters ---@field config table hydrated user opts.filters
@@ -194,7 +193,7 @@ function Filters:prepare(git_status)
local explorer = require("nvim-tree.core").get_explorer() local explorer = require("nvim-tree.core").get_explorer()
if explorer then if explorer then
for _, node in pairs(explorer.marks:list()) do 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 end
@@ -224,33 +223,4 @@ function Filters:should_filter(path, fs_stat, status)
or bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks) or bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks)
end end
--- Check if the given path should be filtered, and provide the reason why it was
---@param path string Absolute path
---@param fs_stat uv.fs_stat.result|nil fs_stat of file
---@param status table from prepare
---@return FILTER_REASON
function Filters:should_filter_as_reason(path, fs_stat, status)
if not self.config.enable then
return FILTER_REASON.none
end
if is_excluded(self, path) then
return FILTER_REASON.none
end
if git(self, path, status.git_status) then
return FILTER_REASON.git
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
return FILTER_REASON.none
end
end
return Filters return Filters

View File

@@ -1,23 +1,17 @@
local builders = require "nvim-tree.explorer.node-builders"
local git = require "nvim-tree.git" local git = require "nvim-tree.git"
local log = require "nvim-tree.log"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
local utils = require "nvim-tree.utils"
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 NodeIterator = require "nvim-tree.iterators.node-iterator"
local Watcher = require "nvim-tree.watcher"
local Filters = require "nvim-tree.explorer.filters" local Filters = require "nvim-tree.explorer.filters"
local Marks = {} -- circular dependencies local Marks = require "nvim-tree.marks"
local LiveFilter = require "nvim-tree.explorer.live-filter" local LiveFilter = require "nvim-tree.explorer.live-filter"
local Sorters = require "nvim-tree.explorer.sorters" local Sorters = require "nvim-tree.explorer.sorters"
local Clipboard = {} -- circular dependencies local View = require "nvim-tree.explorer.view"
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON local M = {}
local config M.explore = require("nvim-tree.explorer.explore").explore
M.reload = require("nvim-tree.explorer.reload").reload
---@class Explorer ---@class Explorer
---@field absolute_path string ---@field absolute_path string
@@ -27,14 +21,13 @@ local config
---@field live_filter LiveFilter ---@field live_filter LiveFilter
---@field sorters Sorter ---@field sorters Sorter
---@field marks Marks ---@field marks Marks
---@field clipboard Clipboard
local Explorer = {}
Explorer.explore = require("nvim-tree.explorer.explore").explore local Explorer = {}
Explorer.__index = Explorer
---@param path string|nil ---@param path string|nil
---@return Explorer|nil ---@return Explorer|nil
function Explorer:new(path) function Explorer.new(path)
local err local err
if path then if path then
@@ -48,24 +41,27 @@ function Explorer:new(path)
end end
---@class Explorer ---@class Explorer
local o = setmetatable({ local explorer = setmetatable({
absolute_path = path, absolute_path = path,
nodes = {}, nodes = {},
open = true, open = true,
sorters = Sorters:new(config), marks = Marks:new(),
sorters = Sorters:new(M.config),
view = View:new(M.config),
}, Explorer) }, Explorer)
setmetatable(o, self) explorer.watcher = watch.create_watcher(explorer)
self.__index = self explorer.filters = Filters:new(M.config, explorer)
explorer.live_filter = LiveFilter:new(M.config, explorer)
explorer:_load(explorer)
return explorer
end
o.watcher = watch.create_watcher(o) ---@private
o.filters = Filters:new(config, o) ---@param node Node
o.live_filter = LiveFilter:new(config, o) function Explorer:_load(node)
o.marks = Marks:new(config, o) local cwd = node.link_to or node.absolute_path
o.clipboard = Clipboard:new(config, o) local git_status = git.load_project_status(cwd)
M.explore(node, git_status, self)
o:_load(o)
return o
end end
---@param node Node ---@param node Node
@@ -85,256 +81,14 @@ function Explorer:destroy()
iterate(self) iterate(self)
end end
---@param node Node function M.setup(opts)
---@param git_status table|nil M.config = opts
function Explorer:reload(node, git_status)
local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd)
if not handle then
return
end
local profile = log.profile_start("reload %s", node.absolute_path)
local filter_status = self.filters:prepare(git_status)
if node.group_next then
node.nodes = { node.group_next }
node.group_next = nil
end
local remain_childs = {}
local node_ignored = explorer_node.is_git_ignored(node)
---@type table<string, Node>
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
local name, _ = vim.loop.fs_scandir_next(handle)
if not name then
break
end
local abs = utils.path_join { cwd, name }
---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs)
local filter_reason = self.filters:should_filter_as_reason(abs, stat, filter_status)
if filter_reason == FILTER_REASON.none then
remain_childs[abs] = true
-- Type must come from fs_stat and not fs_scandir_next to maintain sshfs compatibility
local type = stat and stat.type or nil
-- Recreate node if type changes.
if nodes_by_path[abs] then
local n = nodes_by_path[abs]
if n.type ~= type then
utils.array_remove(node.nodes, n)
explorer_node.node_destroy(n)
nodes_by_path[abs] = nil
end
end
if not nodes_by_path[abs] then
local new_child = nil
if type == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
new_child = builders.folder(node, abs, name, stat)
elseif type == "file" then
new_child = builders.file(node, abs, name, stat)
elseif type == "link" then
local link = builders.link(node, abs, name, stat)
if link.link_to ~= nil then
new_child = link
end
end
if new_child then
table.insert(node.nodes, new_child)
nodes_by_path[abs] = new_child
end
else
local n = nodes_by_path[abs]
if n then
n.executable = builders.is_executable(abs) or false
n.fs_stat = stat
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
node.nodes = vim.tbl_map(
self:update_status(nodes_by_path, node_ignored, git_status),
vim.tbl_filter(function(n)
if remain_childs[n.absolute_path] then
return remain_childs[n.absolute_path]
else
explorer_node.node_destroy(n)
return false
end
end, node.nodes)
)
local is_root = not node.parent
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
if config.group_empty and not is_root and child_folder_only then
node.group_next = child_folder_only
local ns = self:reload(child_folder_only, git_status)
node.nodes = ns or {}
log.profile_end(profile)
return ns
end
self.sorters:sort(node.nodes)
self.live_filter:apply_filter(node)
log.profile_end(profile)
return node.nodes
end
---TODO #2837 #2871 move this and similar to node
---Refresh contents and git status for a single node
---@param node Node
---@param callback function
function Explorer:refresh_node(node, callback)
if type(node) ~= "table" then
callback()
end
local parent_node = utils.get_parent_of_group(node)
self:reload_and_get_git_project(node.absolute_path, function(toplevel, project)
self:reload(parent_node, project)
self:update_parent_statuses(parent_node, project, toplevel)
callback()
end)
end
---Refresh contents of all nodes to a path: actual directory and links.
---Groups will be expanded if needed.
---@param path string absolute path
function Explorer:refresh_parent_nodes_for_path(path)
local profile = log.profile_start("refresh_parent_nodes_for_path %s", path)
-- collect parent nodes from the top down
local parent_nodes = {}
NodeIterator.builder({ self })
:recursor(function(node)
return node.nodes
end)
:applier(function(node)
local abs_contains = node.absolute_path and path:find(node.absolute_path, 1, true) == 1
local link_contains = node.link_to and path:find(node.link_to, 1, true) == 1
if abs_contains or link_contains then
table.insert(parent_nodes, node)
end
end)
:iterate()
-- refresh in order; this will expand groups as needed
for _, node in ipairs(parent_nodes) do
local toplevel = git.get_toplevel(node.absolute_path)
local project = git.get_project(toplevel) or {}
self:reload(node, project)
self:update_parent_statuses(node, project, toplevel)
end
log.profile_end(profile)
end
---@private
---@param node Node
function Explorer:_load(node)
local cwd = node.link_to or node.absolute_path
local git_status = git.load_project_status(cwd)
Explorer.explore(node, git_status, self)
end
function Explorer.setup(opts)
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.reload").setup(opts)
require("nvim-tree.explorer.watch").setup(opts) require("nvim-tree.explorer.watch").setup(opts)
Marks = require "nvim-tree.marks"
Clipboard = require "nvim-tree.actions.fs.clipboard"
end end
---@private M.Explorer = Explorer
---@param nodes_by_path table
---@param node_ignored boolean
---@param status table|nil
---@return fun(node: Node): table
function Explorer:update_status(nodes_by_path, node_ignored, status)
return function(node)
if nodes_by_path[node.absolute_path] then
explorer_node.update_git_status(node, node_ignored, status)
end
return node
end
end
---TODO #2837 #2871 move this and similar to node return M
---@private
---@param path string
---@param callback fun(toplevel: string|nil, project: table|nil)
function Explorer:reload_and_get_git_project(path, callback)
local toplevel = git.get_toplevel(path)
git.reload_project(toplevel, path, function()
callback(toplevel, git.get_project(toplevel) or {})
end)
end
---TODO #2837 #2871 move this and similar to node
---@private
---@param node Node
---@param project table|nil
---@param root string|nil
function Explorer:update_parent_statuses(node, project, root)
while project and node do
-- step up to the containing project
if node.absolute_path == root then
-- stop at the top of the tree
if not node.parent then
break
end
root = git.get_toplevel(node.parent.absolute_path)
-- stop when no more projects
if not root then
break
end
-- update the containing project
project = git.get_project(root)
git.reload_project(root, node.absolute_path, nil)
end
-- update status
explorer_node.update_git_status(node, explorer_node.is_git_ignored(node.parent), project)
-- maybe parent
node = node.parent
end
end
return Explorer

View File

@@ -1,4 +1,3 @@
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"
@@ -35,17 +34,10 @@ local function reset_filter(self, node_)
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
@@ -54,14 +46,14 @@ local overlay_bufnr = 0
local overlay_winnr = 0 local overlay_winnr = 0
local function remove_overlay(self) local function remove_overlay(self)
if view.View.float.enable and view.View.float.quit_on_focus_loss then if self.explorer.view.View.float.enable and self.explorer.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", {
pattern = "NvimTree_*", pattern = "NvimTree_*",
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }), group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
callback = function() callback = function()
if utils.is_nvim_tree_buf(0) then if utils.is_nvim_tree_buf(0) then
view.close() self.explorer.view:close()
end end
end, end,
}) })
@@ -102,10 +94,6 @@ function LiveFilter: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)
@@ -115,8 +103,6 @@ function LiveFilter:apply_filter(node_)
end end
end end
node.hidden_stats.live_filter = filtered_nodes
local has_nodes = nodes and (self.always_show_folders or #nodes > filtered_nodes) local has_nodes = nodes and (self.always_show_folders or #nodes > filtered_nodes)
local ok, is_match = pcall(matches, self, node) 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))
@@ -154,7 +140,7 @@ end
---@return integer ---@return integer
local function calculate_overlay_win_width(self) local function calculate_overlay_win_width(self)
local wininfo = vim.fn.getwininfo(view.get_winnr())[1] local wininfo = vim.fn.getwininfo(self.explorer.view:get_winnr())[1]
if wininfo then if wininfo then
return wininfo.width - wininfo.textoff - #self.prefix return wininfo.width - wininfo.textoff - #self.prefix
@@ -164,7 +150,7 @@ local function calculate_overlay_win_width(self)
end end
local function create_overlay(self) local function create_overlay(self)
if view.View.float.enable then if self.explorer.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 {
event = "WinLeave", event = "WinLeave",
@@ -196,13 +182,13 @@ local function create_overlay(self)
end end
function LiveFilter:start_filtering() function LiveFilter:start_filtering()
view.View.live_filter.prev_focused_node = require("nvim-tree.lib").get_node_at_cursor() self.explorer.view.View.live_filter.prev_focused_node = require("nvim-tree.lib").get_node_at_cursor()
self.filter = self.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 = #self.prefix > 0 and #self.prefix - 1 or 1 local col = #self.prefix > 0 and #self.prefix - 1 or 1
view.set_cursor { row, col } self.explorer.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(function() vim.schedule(function()
return create_overlay(self) return create_overlay(self)
@@ -211,7 +197,7 @@ end
function LiveFilter: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 = self.explorer.view.View.live_filter.prev_focused_node
self.filter = nil self.filter = nil
reset_filter(self) reset_filter(self)

View File

@@ -0,0 +1,233 @@
local utils = require "nvim-tree.utils"
local builders = require "nvim-tree.explorer.node-builders"
local explorer_node = require "nvim-tree.explorer.node"
local git = require "nvim-tree.git"
local log = require "nvim-tree.log"
local NodeIterator = require "nvim-tree.iterators.node-iterator"
local Watcher = require "nvim-tree.watcher"
local M = {}
---@param nodes_by_path table
---@param node_ignored boolean
---@param status table
---@return fun(node: Node): table
local function update_status(nodes_by_path, node_ignored, status)
return function(node)
if nodes_by_path[node.absolute_path] then
explorer_node.update_git_status(node, node_ignored, status)
end
return node
end
end
---@param path string
---@param callback fun(toplevel: string|nil, project: table|nil)
local function reload_and_get_git_project(path, callback)
local toplevel = git.get_toplevel(path)
git.reload_project(toplevel, path, function()
callback(toplevel, git.get_project(toplevel) or {})
end)
end
---@param node Node
---@param project table|nil
---@param root string|nil
local function update_parent_statuses(node, project, root)
while project and node do
-- step up to the containing project
if node.absolute_path == root then
-- stop at the top of the tree
if not node.parent then
break
end
root = git.get_toplevel(node.parent.absolute_path)
-- stop when no more projects
if not root then
break
end
-- update the containing project
project = git.get_project(root)
git.reload_project(root, node.absolute_path, nil)
end
-- update status
explorer_node.update_git_status(node, explorer_node.is_git_ignored(node.parent), project)
-- maybe parent
node = node.parent
end
end
---@param node Node
---@param git_status table
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 handle = vim.loop.fs_scandir(cwd)
if not handle then
return
end
local profile = log.profile_start("reload %s", node.absolute_path)
local filter_status = explorer.filters:prepare(git_status)
if node.group_next then
node.nodes = { node.group_next }
node.group_next = nil
end
local remain_childs = {}
local node_ignored = explorer_node.is_git_ignored(node)
---@type table<string, Node>
local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
while true do
local name, t = vim.loop.fs_scandir_next(handle)
if not name then
break
end
local abs = utils.path_join { cwd, name }
---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs)
if not explorer.filters:should_filter(abs, stat, filter_status) then
remain_childs[abs] = true
-- Recreate node if type changes.
if nodes_by_path[abs] then
local n = nodes_by_path[abs]
if n.type ~= t then
utils.array_remove(node.nodes, n)
explorer_node.node_destroy(n)
nodes_by_path[abs] = nil
end
end
if not nodes_by_path[abs] then
local new_child = nil
if t == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
new_child = builders.folder(node, abs, name, stat)
elseif t == "file" then
new_child = builders.file(node, abs, name, stat)
elseif t == "link" then
local link = builders.link(node, abs, name, stat)
if link.link_to ~= nil then
new_child = link
end
end
if new_child then
table.insert(node.nodes, new_child)
nodes_by_path[abs] = new_child
end
else
local n = nodes_by_path[abs]
if n then
n.executable = builders.is_executable(abs) or false
n.fs_stat = stat
end
end
end
end
node.nodes = vim.tbl_map(
update_status(nodes_by_path, node_ignored, git_status),
vim.tbl_filter(function(n)
if remain_childs[n.absolute_path] then
return remain_childs[n.absolute_path]
else
explorer_node.node_destroy(n)
return false
end
end, node.nodes)
)
local is_root = not node.parent
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
if M.config.group_empty and not is_root and child_folder_only then
node.group_next = child_folder_only
local ns = M.reload(child_folder_only, git_status)
node.nodes = ns or {}
log.profile_end(profile)
return ns
end
explorer.sorters:sort(node.nodes)
explorer.live_filter:apply_filter(node)
log.profile_end(profile)
return node.nodes
end
---Refresh contents and git status for a single node
---@param node Node
---@param callback function
function M.refresh_node(node, callback)
if type(node) ~= "table" then
callback()
end
local parent_node = utils.get_parent_of_group(node)
reload_and_get_git_project(node.absolute_path, function(toplevel, project)
require("nvim-tree.explorer.reload").reload(parent_node, project)
update_parent_statuses(parent_node, project, toplevel)
callback()
end)
end
---Refresh contents of all nodes to a path: actual directory and links.
---Groups will be expanded if needed.
---@param path string absolute path
function M.refresh_parent_nodes_for_path(path)
local explorer = require("nvim-tree.core").get_explorer()
if not explorer then
return
end
local profile = log.profile_start("refresh_parent_nodes_for_path %s", path)
-- collect parent nodes from the top down
local parent_nodes = {}
NodeIterator.builder({ explorer })
:recursor(function(node)
return node.nodes
end)
:applier(function(node)
local abs_contains = node.absolute_path and path:find(node.absolute_path, 1, true) == 1
local link_contains = node.link_to and path:find(node.link_to, 1, true) == 1
if abs_contains or link_contains then
table.insert(parent_nodes, node)
end
end)
:iterate()
-- refresh in order; this will expand groups as needed
for _, node in ipairs(parent_nodes) do
local toplevel = git.get_toplevel(node.absolute_path)
local project = git.get_project(toplevel) or {}
M.reload(node, project)
update_parent_statuses(node, project, toplevel)
end
log.profile_end(profile)
end
function M.setup(opts)
M.config = opts.renderer
end
return M

View File

@@ -2,18 +2,15 @@ local events = require "nvim-tree.events"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
---@class OpenInWinOpts local ExplorerView = {}
---@field hijack_current_buf boolean|nil default true
---@field resize boolean|nil default true
---@field winid number|nil 0 or nil for current
local M = {}
local DEFAULT_MIN_WIDTH = 30 local DEFAULT_MIN_WIDTH = 30
local DEFAULT_MAX_WIDTH = -1 local DEFAULT_MAX_WIDTH = -1
local DEFAULT_PADDING = 1 local DEFAULT_PADDING = 1
M.View = { function ExplorerView:new(opts)
local o = {
View = {
adaptive_size = false, adaptive_size = false,
centralize_selection = false, centralize_selection = false,
tabpages = {}, tabpages = {},
@@ -50,10 +47,32 @@ M.View = {
"Normal:NvimTreeNormal", "Normal:NvimTreeNormal",
"NormalNC:NvimTreeNormalNC", "NormalNC:NvimTreeNormalNC",
"NormalFloat:NvimTreeNormalFloat", "NormalFloat:NvimTreeNormalFloat",
"FloatBorder:NvimTreeNormalFloatBorder",
}, ","), }, ","),
}, },
} }
}
local options = opts.view or {}
o.View.centralize_selection = options.centralize_selection
o.View.side = (options.side == "right") and "right" or "left"
o.View.height = options.height
o.View.hide_root_folder = opts.renderer.root_folder_label == false
o.View.tab = opts.tab
o.View.preserve_window_proportions = options.preserve_window_proportions
o.View.winopts.cursorline = options.cursorline
o.View.winopts.number = options.number
o.View.winopts.relativenumber = options.relativenumber
o.View.winopts.signcolumn = options.signcolumn
o.View.float = options.float
o.on_attach = opts.on_attach
o.config = vim.deepcopy(options)
setmetatable(o, self)
self.__index = self
o:configure_width(options.width)
o.View.initial_width = o:get_width()
end
-- The initial state of a tab -- The initial state of a tab
local tabinitial = { local tabinitial = {
@@ -93,20 +112,20 @@ local function wipe_rogue_buffer()
end end
---@param bufnr integer|boolean|nil ---@param bufnr integer|boolean|nil
local function create_buffer(bufnr) local function create_buffer(self, bufnr)
wipe_rogue_buffer() wipe_rogue_buffer()
local tab = vim.api.nvim_get_current_tabpage() local tab = vim.api.nvim_get_current_tabpage()
BUFNR_PER_TAB[tab] = bufnr or vim.api.nvim_create_buf(false, false) BUFNR_PER_TAB[tab] = bufnr or vim.api.nvim_create_buf(false, false)
vim.api.nvim_buf_set_name(M.get_bufnr(), "NvimTree_" .. tab) vim.api.nvim_buf_set_name(self:get_bufnr(), "NvimTree_" .. tab)
for option, value in pairs(BUFFER_OPTIONS) do for option, value in pairs(BUFFER_OPTIONS) do
vim.bo[M.get_bufnr()][option] = value vim.bo[self:get_bufnr()][option] = value
end end
require("nvim-tree.keymap").on_attach(M.get_bufnr()) require("nvim-tree.keymap").on_attach(self:get_bufnr())
events._dispatch_tree_attached_post(M.get_bufnr()) events._dispatch_tree_attached_post(self:get_bufnr())
end end
---@param size (fun():integer)|integer|string ---@param size (fun():integer)|integer|string
@@ -123,11 +142,11 @@ local function get_size(size)
end end
---@param size (fun():integer)|integer|nil ---@param size (fun():integer)|integer|nil
local function get_width(size) function ExplorerView:get_width(size)
if size then if size then
return get_size(size) return get_size(size)
else else
return get_size(M.View.width) return get_size(self.View.width)
end end
end end
@@ -138,39 +157,39 @@ local move_tbl = {
-- setup_tabpage sets up the initial state of a tab -- setup_tabpage sets up the initial state of a tab
---@param tabpage integer ---@param tabpage integer
local function setup_tabpage(tabpage) local function setup_tabpage(self, tabpage)
local winnr = vim.api.nvim_get_current_win() local winnr = vim.api.nvim_get_current_win()
M.View.tabpages[tabpage] = vim.tbl_extend("force", M.View.tabpages[tabpage] or tabinitial, { winnr = winnr }) self.View.tabpages[tabpage] = vim.tbl_extend("force", self.View.tabpages[tabpage] or tabinitial, { winnr = winnr })
end end
local function set_window_options_and_buffer() local function set_window_options_and_buffer(self)
pcall(vim.api.nvim_command, "buffer " .. M.get_bufnr()) pcall(vim.api.nvim_command, "buffer " .. self:get_bufnr())
local eventignore = vim.opt.eventignore:get() local eventignore = vim.opt.eventignore:get()
vim.opt.eventignore = "all" vim.opt.eventignore = "all"
for k, v in pairs(M.View.winopts) do for k, v in pairs(self.View.winopts) do
vim.opt_local[k] = v vim.opt_local[k] = v
end end
vim.opt.eventignore = eventignore vim.opt.eventignore = eventignore
end end
---@return table ---@return table
local function open_win_config() local function open_win_config(self)
if type(M.View.float.open_win_config) == "function" then if type(self.View.float.open_win_config) == "function" then
return M.View.float.open_win_config() return self.View.float.open_win_config(self)
else else
return M.View.float.open_win_config return self.View.float.open_win_config
end end
end end
local function open_window() local function open_window(self)
if M.View.float.enable then if self.View.float.enable then
vim.api.nvim_open_win(0, true, open_win_config()) vim.api.nvim_open_win(0, true, open_win_config(self))
else else
vim.api.nvim_command "vsp" vim.api.nvim_command "vsp"
M.reposition_window() self:reposition_window()
end end
setup_tabpage(vim.api.nvim_get_current_tabpage()) setup_tabpage(self, vim.api.nvim_get_current_tabpage())
set_window_options_and_buffer() set_window_options_and_buffer(self)
end end
---@param buf integer ---@param buf integer
@@ -206,19 +225,19 @@ end
-- save_tab_state saves any state that should be preserved across redraws. -- save_tab_state saves any state that should be preserved across redraws.
---@param tabnr integer ---@param tabnr integer
local function save_tab_state(tabnr) local function save_tab_state(self, tabnr)
local tabpage = tabnr or vim.api.nvim_get_current_tabpage() local tabpage = tabnr or vim.api.nvim_get_current_tabpage()
M.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr(tabpage) or 0) self.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(self:get_winnr(tabpage) or 0)
end end
---@param tabpage integer ---@param tabpage integer
local function close(tabpage) local function close(self, tabpage)
if not M.is_visible { tabpage = tabpage } then if not self:is_visible { tabpage = tabpage } then
return return
end end
save_tab_state(tabpage) save_tab_state(self, tabpage)
switch_buf_if_last_buf() switch_buf_if_last_buf()
local tree_win = M.get_winnr(tabpage) local tree_win = self:get_winnr(tabpage)
local current_win = vim.api.nvim_get_current_win() local current_win = vim.api.nvim_get_current_win()
for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do
if vim.api.nvim_win_get_config(win).relative == "" then if vim.api.nvim_win_get_config(win).relative == "" then
@@ -235,35 +254,35 @@ local function close(tabpage)
end end
end end
function M.close_this_tab_only() function ExplorerView:close_this_tab_only()
close(vim.api.nvim_get_current_tabpage()) close(self, vim.api.nvim_get_current_tabpage())
end end
function M.close_all_tabs() function ExplorerView:close_all_tabs()
for tabpage, _ in pairs(M.View.tabpages) do for tabpage, _ in pairs(self.View.tabpages) do
close(tabpage) close(self, tabpage)
end end
end end
function M.close() function ExplorerView:close()
if M.View.tab.sync.close then if self.View.tab.sync.close then
M.close_all_tabs() self:close_all_tabs()
else else
M.close_this_tab_only() self:close_this_tab_only()
end end
end end
---@param options table|nil ---@param options table|nil
function M.open(options) function ExplorerView:open(options)
if M.is_visible() then if self:is_visible() then
return return
end end
local profile = log.profile_start "view open" local profile = log.profile_start "view open"
create_buffer() create_buffer(self)
open_window() open_window(self)
M.resize() self:resize()
local opts = options or { focus_tree = true } local opts = options or { focus_tree = true }
if not opts.focus_tree then if not opts.focus_tree then
@@ -274,26 +293,26 @@ function M.open(options)
log.profile_end(profile) log.profile_end(profile)
end end
local function grow() local function grow(self)
local starts_at = M.is_root_folder_visible(require("nvim-tree.core").get_cwd()) and 1 or 0 local starts_at = self:is_root_folder_visible(require("nvim-tree.core").get_cwd()) and 1 or 0
local lines = vim.api.nvim_buf_get_lines(M.get_bufnr(), starts_at, -1, false) local lines = vim.api.nvim_buf_get_lines(self:get_bufnr(), starts_at, -1, false)
-- number of columns of right-padding to indicate end of path -- number of columns of right-padding to indicate end of path
local padding = get_size(M.View.padding) local padding = get_size(self.View.padding)
-- account for sign/number columns etc. -- account for sign/number columns etc.
local wininfo = vim.fn.getwininfo(M.get_winnr()) local wininfo = vim.fn.getwininfo(self:get_winnr())
if type(wininfo) == "table" and type(wininfo[1]) == "table" then if type(wininfo) == "table" and type(wininfo[1]) == "table" then
padding = padding + wininfo[1].textoff padding = padding + wininfo[1].textoff
end end
local resizing_width = M.View.initial_width - padding local resizing_width = self.View.initial_width - padding
local max_width local max_width
-- maybe bound max -- maybe bound max
if M.View.max_width == -1 then if self.View.max_width == -1 then
max_width = -1 max_width = -1
else else
max_width = get_width(M.View.max_width) - padding max_width = self:get_width(self.View.max_width) - padding
end end
for _, l in pairs(lines) do for _, l in pairs(lines) do
@@ -301,23 +320,23 @@ local function grow()
if resizing_width < count then if resizing_width < count then
resizing_width = count resizing_width = count
end end
if M.View.adaptive_size and max_width >= 0 and resizing_width >= max_width then if self.View.adaptive_size and max_width >= 0 and resizing_width >= max_width then
resizing_width = max_width resizing_width = max_width
break break
end end
end end
M.resize(resizing_width + padding) self:resize(resizing_width + padding)
end end
function M.grow_from_content() function ExplorerView:grow_from_content()
if M.View.adaptive_size then if self.View.adaptive_size then
grow() grow(self)
end end
end end
---@param size string|number|nil ---@param size string|number|nil
function M.resize(size) function ExplorerView:resize(size)
if M.View.float.enable and not M.View.adaptive_size then if self.View.float.enable and not self.View.adaptive_size then
-- if the floating windows's adaptive size is not desired, then the -- if the floating windows's adaptive size is not desired, then the
-- float size should be defined in view.float.open_win_config -- float size should be defined in view.float.open_win_config
return return
@@ -329,7 +348,7 @@ function M.resize(size)
size = tonumber(size) size = tonumber(size)
if first_char == "+" or first_char == "-" then if first_char == "+" or first_char == "-" then
size = M.View.width + size size = self.View.width + size
end end
end end
@@ -338,85 +357,81 @@ function M.resize(size)
end end
if size then if size then
M.View.width = size self.View.width = size
M.View.height = size self.View.height = size
end end
if not M.is_visible() then if not self:is_visible() then
return return
end end
local winnr = M.get_winnr() or 0 local new_size = self:get_width()
vim.api.nvim_win_set_width(self:get_winnr() or 0, new_size)
local new_size = get_width() events._dispatch_on_tree_resize(new_size)
if new_size ~= vim.api.nvim_win_get_width(winnr) then if not self.View.preserve_window_proportions then
vim.api.nvim_win_set_width(winnr, new_size)
if not M.View.preserve_window_proportions then
vim.cmd ":wincmd =" vim.cmd ":wincmd ="
end end
end end
events._dispatch_on_tree_resize(new_size) function ExplorerView:reposition_window()
end local move_to = move_tbl[self.View.side]
function M.reposition_window()
local move_to = move_tbl[M.View.side]
vim.api.nvim_command("wincmd " .. move_to) vim.api.nvim_command("wincmd " .. move_to)
M.resize() self:resize()
end end
local function set_current_win() local function set_current_win(self)
local current_tab = vim.api.nvim_get_current_tabpage() local current_tab = vim.api.nvim_get_current_tabpage()
M.View.tabpages[current_tab].winnr = vim.api.nvim_get_current_win() self.View.tabpages[current_tab].winnr = vim.api.nvim_get_current_win()
end end
---Open the tree in the a window ---Open the tree in the a window
---@param opts OpenInWinOpts|nil ---@param opts OpenInWinOpts|nil
function M.open_in_win(opts) function ExplorerView:open_in_win(opts)
opts = opts or { hijack_current_buf = true, resize = true } opts = opts or { hijack_current_buf = true, resize = true }
if opts.winid and vim.api.nvim_win_is_valid(opts.winid) then if opts.winid and vim.api.nvim_win_is_valid(opts.winid) then
vim.api.nvim_set_current_win(opts.winid) vim.api.nvim_set_current_win(opts.winid)
end end
create_buffer(opts.hijack_current_buf and vim.api.nvim_get_current_buf()) create_buffer(self, opts.hijack_current_buf and vim.api.nvim_get_current_buf())
setup_tabpage(vim.api.nvim_get_current_tabpage()) setup_tabpage(self, vim.api.nvim_get_current_tabpage())
set_current_win() set_current_win(self)
set_window_options_and_buffer() set_window_options_and_buffer(self)
if opts.resize then if opts.resize then
M.reposition_window() self:reposition_window()
M.resize() self:resize()
end end
end end
function M.abandon_current_window() function ExplorerView:abandon_current_window()
local tab = vim.api.nvim_get_current_tabpage() local tab = vim.api.nvim_get_current_tabpage()
BUFNR_PER_TAB[tab] = nil BUFNR_PER_TAB[tab] = nil
if M.View.tabpages[tab] then if self.View.tabpages[tab] then
M.View.tabpages[tab].winnr = nil self.View.tabpages[tab].winnr = nil
end end
end end
function M.abandon_all_windows() function ExplorerView:abandon_all_windows()
for tab, _ in pairs(vim.api.nvim_list_tabpages()) do for tab, _ in pairs(vim.api.nvim_list_tabpages()) do
BUFNR_PER_TAB[tab] = nil BUFNR_PER_TAB[tab] = nil
if M.View.tabpages[tab] then if self.View.tabpages[tab] then
M.View.tabpages[tab].winnr = nil self.View.tabpages[tab].winnr = nil
end end
end end
end end
---@param opts table|nil ---@param opts table|nil
function M.is_visible(opts) function ExplorerView:is_visible(opts)
if opts and opts.tabpage then if opts and opts.tabpage then
if M.View.tabpages[opts.tabpage] == nil then if self.View.tabpages[opts.tabpage] == nil then
return false return false
end end
local winnr = M.View.tabpages[opts.tabpage].winnr local winnr = self.View.tabpages[opts.tabpage].winnr
return winnr and vim.api.nvim_win_is_valid(winnr) return winnr and vim.api.nvim_win_is_valid(winnr)
end end
if opts and opts.any_tabpage then if opts and opts.any_tabpage then
for _, v in pairs(M.View.tabpages) do for _, v in pairs(self.View.tabpages) do
if v.winnr and vim.api.nvim_win_is_valid(v.winnr) then if v.winnr and vim.api.nvim_win_is_valid(v.winnr) then
return true return true
end end
@@ -424,27 +439,27 @@ function M.is_visible(opts)
return false return false
end end
return M.get_winnr() ~= nil and vim.api.nvim_win_is_valid(M.get_winnr() or 0) return self:get_winnr() ~= nil and vim.api.nvim_win_is_valid(self:get_winnr() or 0)
end end
---@param opts table|nil ---@param opts table|nil
function M.set_cursor(opts) function ExplorerView:set_cursor(opts)
if M.is_visible() then if self:is_visible() then
pcall(vim.api.nvim_win_set_cursor, M.get_winnr(), opts) pcall(vim.api.nvim_win_set_cursor, self:get_winnr(), opts)
end end
end end
---@param winnr number|nil ---@param winnr number|nil
---@param open_if_closed boolean|nil ---@param open_if_closed boolean|nil
function M.focus(winnr, open_if_closed) function ExplorerView:focus(winnr, open_if_closed)
local wnr = winnr or M.get_winnr() local wnr = winnr or self:get_winnr()
if vim.api.nvim_win_get_tabpage(wnr or 0) ~= vim.api.nvim_win_get_tabpage(0) then if vim.api.nvim_win_get_tabpage(wnr or 0) ~= vim.api.nvim_win_get_tabpage(0) then
M.close() self:close()
M.open() self:open()
wnr = M.get_winnr() wnr = self:get_winnr()
elseif open_if_closed and not M.is_visible() then elseif open_if_closed and not self:is_visible() then
M.open() self:open()
end end
if wnr then if wnr then
@@ -455,30 +470,30 @@ end
--- Retrieve the winid of the open tree. --- Retrieve the winid of the open tree.
---@param opts ApiTreeWinIdOpts|nil ---@param opts ApiTreeWinIdOpts|nil
---@return number|nil winid unlike get_winnr(), this returns nil if the nvim-tree window is not visible ---@return number|nil winid unlike get_winnr(), this returns nil if the nvim-tree window is not visible
function M.winid(opts) function ExplorerView:winid(opts)
local tabpage = opts and opts.tabpage local tabpage = opts and opts.tabpage
if tabpage == 0 then if tabpage == 0 then
tabpage = vim.api.nvim_get_current_tabpage() tabpage = vim.api.nvim_get_current_tabpage()
end end
if M.is_visible { tabpage = tabpage } then if self:is_visible { tabpage = tabpage } then
return M.get_winnr(tabpage) return self:get_winnr(tabpage)
else else
return nil return nil
end end
end end
--- Restores the state of a NvimTree window if it was initialized before. --- Restores the state of a NvimTree window if it was initialized before.
function M.restore_tab_state() function ExplorerView:restore_tab_state()
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = vim.api.nvim_get_current_tabpage()
M.set_cursor(M.View.cursors[tabpage]) self:set_cursor(self.View.cursors[tabpage])
end end
--- Returns the window number for nvim-tree within the tabpage specified --- Returns the window number for nvim-tree within the tabpage specified
---@param tabpage number|nil (optional) the number of the chosen tabpage. Defaults to current tabpage. ---@param tabpage number|nil (optional) the number of the chosen tabpage. Defaults to current tabpage.
---@return number|nil ---@return number|nil
function M.get_winnr(tabpage) function ExplorerView:get_winnr(tabpage)
tabpage = tabpage or vim.api.nvim_get_current_tabpage() tabpage = tabpage or vim.api.nvim_get_current_tabpage()
local tabinfo = M.View.tabpages[tabpage] local tabinfo = self.View.tabpages[tabpage]
if tabinfo and tabinfo.winnr and vim.api.nvim_win_is_valid(tabinfo.winnr) then if tabinfo and tabinfo.winnr and vim.api.nvim_win_is_valid(tabinfo.winnr) then
return tabinfo.winnr return tabinfo.winnr
end end
@@ -486,19 +501,19 @@ end
--- Returns the current nvim tree bufnr --- Returns the current nvim tree bufnr
---@return number ---@return number
function M.get_bufnr() function ExplorerView:get_bufnr()
return BUFNR_PER_TAB[vim.api.nvim_get_current_tabpage()] return BUFNR_PER_TAB[vim.api.nvim_get_current_tabpage()]
end end
---@param bufnr number ---@param bufnr number
---@return boolean ---@return boolean
function M.is_buf_valid(bufnr) function ExplorerView:is_buf_valid(bufnr)
return bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr) return bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr)
end end
function M._prevent_buffer_override() function ExplorerView:_prevent_buffer_override()
local view_winnr = M.get_winnr() local view_winnr = self:get_winnr()
local view_bufnr = M.get_bufnr() local view_bufnr = self:get_bufnr()
-- need to schedule to let the new buffer populate the window -- need to schedule to let the new buffer populate the window
-- because this event needs to be run on bufWipeout. -- because this event needs to be run on bufWipeout.
@@ -510,7 +525,7 @@ function M._prevent_buffer_override()
local bufname = vim.api.nvim_buf_get_name(curbuf) local bufname = vim.api.nvim_buf_get_name(curbuf)
if not bufname:match "NvimTree" then if not bufname:match "NvimTree" then
for i, tabpage in ipairs(M.View.tabpages) do for i, tabpage in ipairs(self.View.tabpages) do
if tabpage.winnr == view_winnr then if tabpage.winnr == view_winnr then
M.View.tabpages[i] = nil M.View.tabpages[i] = nil
break break
@@ -541,65 +556,44 @@ end
---@param cwd string|nil ---@param cwd string|nil
---@return boolean ---@return boolean
function M.is_root_folder_visible(cwd) function ExplorerView:is_root_folder_visible(cwd)
return cwd ~= "/" and not M.View.hide_root_folder return cwd ~= "/" and not self.View.hide_root_folder
end end
-- used on ColorScheme event -- used on ColorScheme event
function M.reset_winhl() function ExplorerView:reset_winhl()
local winnr = M.get_winnr() local winnr = self:get_winnr()
if winnr and vim.api.nvim_win_is_valid(winnr) then if winnr and vim.api.nvim_win_is_valid(winnr) then
vim.wo[M.get_winnr()].winhl = M.View.winopts.winhl vim.wo[self:get_winnr()].winhl = self.View.winopts.winhl
end end
end end
---Check if width determined or calculated on-fly ---Check if width determined or calculated on-fly
---@return boolean ---@return boolean
function M.is_width_determined() function ExplorerView:is_width_determined()
return type(M.View.width) ~= "function" return type(self.View.width) ~= "function"
end end
---Configure width-related config ---Configure width-related config
---@param width string|function|number|table|nil ---@param width string|function|number|table|nil
function M.configure_width(width) function ExplorerView:configure_width(width)
if type(width) == "table" then if type(width) == "table" then
M.View.adaptive_size = true self.View.adaptive_size = true
M.View.width = width.min or DEFAULT_MIN_WIDTH self.View.width = width.min or DEFAULT_MIN_WIDTH
M.View.max_width = width.max or DEFAULT_MAX_WIDTH self.View.max_width = width.max or DEFAULT_MAX_WIDTH
M.View.padding = width.padding or DEFAULT_PADDING self.View.padding = width.padding or DEFAULT_PADDING
elseif width == nil then elseif width == nil then
if M.config.width ~= nil then if self.config.width ~= nil then
-- if we had input config - fallback to it -- if we had input config - fallback to it
M.configure_width(M.config.width) self.configure_width(self.config.width)
else else
-- otherwise - restore initial width -- otherwise - restore initial width
M.View.width = M.View.initial_width self.View.width = self.View.initial_width
end end
else else
M.View.adaptive_size = false self.View.adaptive_size = false
M.View.width = width self.View.width = width
end end
end end
function M.setup(opts) return ExplorerView
local options = opts.view or {}
M.View.centralize_selection = options.centralize_selection
M.View.side = (options.side == "right") and "right" or "left"
M.View.height = options.height
M.View.hide_root_folder = opts.renderer.root_folder_label == false
M.View.tab = opts.tab
M.View.preserve_window_proportions = options.preserve_window_proportions
M.View.winopts.cursorline = options.cursorline
M.View.winopts.number = options.number
M.View.winopts.relativenumber = options.relativenumber
M.View.winopts.signcolumn = options.signcolumn
M.View.float = options.float
M.on_attach = opts.on_attach
M.config = options
M.configure_width(options.width)
M.View.initial_width = get_width()
end
return M

View File

@@ -76,12 +76,9 @@ function M.create_watcher(node)
else else
log.line("watcher", "node event executing refresh '%s'", node.absolute_path) log.line("watcher", "node event executing refresh '%s'", node.absolute_path)
end end
local explorer = require("nvim-tree.core").get_explorer() require("nvim-tree.explorer.reload").refresh_node(node, function()
if explorer then
explorer:refresh_node(node, function()
require("nvim-tree.renderer").draw() require("nvim-tree.renderer").draw()
end) end)
end
end) end)
end end

View File

@@ -1,5 +1,4 @@
local renderer = require "nvim-tree.renderer" local renderer = require "nvim-tree.renderer"
local view = require "nvim-tree.view"
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 events = require "nvim-tree.events" local events = require "nvim-tree.events"
@@ -15,33 +14,26 @@ local M = {
target_winid = nil, target_winid = nil,
} }
---Cursor position as per vim.api.nvim_win_get_cursor
---@return integer[]|nil
function M.get_cursor_position()
if not core.get_explorer() then
return
end
local winnr = view.get_winnr()
if not winnr or not vim.api.nvim_win_is_valid(winnr) then
return
end
return vim.api.nvim_win_get_cursor(winnr)
end
---@return Node|nil ---@return Node|nil
function M.get_node_at_cursor() function M.get_node_at_cursor()
local cursor = M.get_cursor_position() local explorer = core.get_explorer()
if not cursor then if not explorer then
return return
end end
if cursor[1] == 1 and view.is_root_folder_visible(core.get_cwd()) then local winnr = explorer.view:get_winnr()
if not winnr then
return
end
local cursor = vim.api.nvim_win_get_cursor(winnr)
local line = cursor[1]
if line == 1 and explorer.view:is_root_folder_visible(core.get_cwd()) then
return { name = ".." } return { name = ".." }
end end
return utils.get_nodes_by_line(core.get_explorer().nodes, core.get_nodes_starting_line())[cursor[1]] return utils.get_nodes_by_line(core.get_explorer().nodes, core.get_nodes_starting_line())[line]
end end
---Create a sanitized partial copy of a node, populating children recursively. ---Create a sanitized partial copy of a node, populating children recursively.
@@ -197,8 +189,12 @@ local function handle_buf_cwd(cwd)
end end
local function open_view_and_draw() local function open_view_and_draw()
local explorer = core.get_explorer()
if not explorer then
return
end
local cwd = vim.fn.getcwd() local cwd = vim.fn.getcwd()
view.open() explorer.view:open()
handle_buf_cwd(cwd) handle_buf_cwd(cwd)
renderer.draw() renderer.draw()
end end
@@ -269,20 +265,24 @@ function M.open(opts)
core.init(cwd) core.init(cwd)
end end
end end
local explorer = core.get_explorer()
if not explorer then
return
end
if should_hijack_current_buf() then if should_hijack_current_buf() then
view.close_this_tab_only() explorer.view:close_this_tab_only()
view.open_in_win() explorer.view:open_in_win()
renderer.draw() renderer.draw()
elseif opts.winid then elseif opts.winid then
view.open_in_win { hijack_current_buf = false, resize = false, winid = opts.winid } explorer.view:open_in_win { hijack_current_buf = false, resize = false, winid = opts.winid }
renderer.draw() renderer.draw()
elseif opts.current_window then elseif opts.current_window then
view.open_in_win { hijack_current_buf = false, resize = false } explorer.view:open_in_win { hijack_current_buf = false, resize = false }
renderer.draw() renderer.draw()
else else
open_view_and_draw() open_view_and_draw()
end end
view.restore_tab_state() explorer.view:restore_tab_state()
events._dispatch_on_tree_open() events._dispatch_on_tree_open()
end end

View File

@@ -0,0 +1,59 @@
local utils = require "nvim-tree.utils"
local remove_file = require "nvim-tree.actions.fs.remove-file"
local notify = require "nvim-tree.notify"
local lib = require "nvim-tree.lib"
local M = {
config = {},
}
--- Delete nodes; each removal will be optionally notified
---@param nodes Node[]
---@param marks Marks
local function do_delete(marks, nodes)
for _, node in pairs(nodes) do
remove_file.remove(node)
end
marks:clear_marks()
if not M.config.filesystem_watchers.enable then
require("nvim-tree.actions.reloaders").reload_explorer()
end
end
--- Delete marked nodes, optionally prompting
---@param explorer Explorer
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
notify.warn "No bookmarksed to delete."
return
end
if M.config.ui.confirm.remove then
local prompt_select = "Remove bookmarked ?"
local prompt_input = prompt_select .. " y/N: "
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short)
utils.clear_prompt()
if item_short == "y" then
do_delete(marks, nodes)
end
end)
else
do_delete(marks, nodes)
end
end
function M.setup(opts)
M.config.ui = opts.ui
M.config.filesystem_watchers = opts.filesystem_watchers
end
return M

View File

@@ -0,0 +1,67 @@
local core = require "nvim-tree.core"
local utils = require "nvim-tree.utils"
local rename_file = require "nvim-tree.actions.fs.rename-file"
local notify = require "nvim-tree.notify"
local lib = require "nvim-tree.lib"
local M = {
config = {},
}
---@param explorer Explorer
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."
return
end
local node_at_cursor = lib.get_node_at_cursor()
local default_path = core.get_cwd()
if node_at_cursor and node_at_cursor.type == "directory" then
default_path = node_at_cursor.absolute_path
elseif node_at_cursor and node_at_cursor.parent then
default_path = node_at_cursor.parent.absolute_path
end
local input_opts = {
prompt = "Move to: ",
default = default_path,
completion = "dir",
}
vim.ui.input(input_opts, function(location)
utils.clear_prompt()
if not location or location == "" then
return
end
if vim.fn.filewritable(location) ~= 2 then
notify.warn(location .. " is not writable, cannot move.")
return
end
local nodes = marks:get_marks()
for _, node in pairs(nodes) do
local head = vim.fn.fnamemodify(node.absolute_path, ":t")
local to = utils.path_join { location, head }
rename_file.rename(node, to)
end
marks:clear_marks()
if not M.config.filesystem_watchers.enable then
require("nvim-tree.actions.reloaders").reload_explorer()
end
end)
end
function M.setup(opts)
M.config.filesystem_watchers = opts.filesystem_watchers
end
return M

View File

@@ -0,0 +1,53 @@
local utils = require "nvim-tree.utils"
local remove_file = require "nvim-tree.actions.fs.trash"
local notify = require "nvim-tree.notify"
local lib = require "nvim-tree.lib"
local M = {
config = {},
}
--- Delete nodes; each removal will be optionally notified
---@param nodes Node[]
local function do_trash(nodes)
for _, node in pairs(nodes) do
remove_file.remove(node)
end
end
---@param explorer Explorer
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
notify.warn "No bookmarks to trash."
return
end
if M.config.ui.confirm.trash then
local prompt_select = "Trash bookmarked ?"
local prompt_input = prompt_select .. " y/N: "
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_trash", function(item_short)
utils.clear_prompt()
if item_short == "y" then
do_trash(nodes)
marks:clear_marks()
end
end)
else
do_trash(nodes)
marks:clear_marks()
end
end
function M.setup(opts)
M.config.ui = opts.ui
M.config.filesystem_watchers = opts.filesystem_watchers
end
return M

View File

@@ -1,82 +1,65 @@
local Iterator = require "nvim-tree.iterators.node-iterator" local renderer = {} -- circular dependency
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local notify = require "nvim-tree.notify"
local open_file = require "nvim-tree.actions.node.open-file"
local remove_file = require "nvim-tree.actions.fs.remove-file"
local rename_file = require "nvim-tree.actions.fs.rename-file"
local renderer = require "nvim-tree.renderer"
local trash = require "nvim-tree.actions.fs.trash"
local utils = require "nvim-tree.utils"
---@class Marks ---@class Marks
---@field config table hydrated user opts.filters ---@field private marks Node[]
---@field private explorer Explorer
---@field private marks table<string, Node> by absolute path
local Marks = {} local Marks = {}
---@return Marks ---@return Marks
---@param opts table user options function Marks:new()
---@param explorer Explorer local o = {}
function Marks:new(opts, explorer)
local o = {
explorer = explorer,
config = {
ui = opts.ui,
filesystem_watchers = opts.filesystem_watchers,
},
marks = {},
}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
o.marks = {}
return o return o
end end
---Clear all marks and reload if watchers disabled
---@private ---@private
function Marks:clear_reload() ---@param node Node
self:clear() function Marks:add_mark(node)
if not self.config.filesystem_watchers.enable then self.marks[node.absolute_path] = node
require("nvim-tree.actions.reloaders").reload_explorer()
end
end
---Clear all marks and redraw
---@public
function Marks:clear()
self.marks = {}
renderer.draw() renderer.draw()
end end
---@public ---@private
---@param node Node ---@param node Node
function Marks:toggle(node) function Marks:remove_mark(node)
self.marks[node.absolute_path] = nil
renderer.draw()
end
---@param node Node
function Marks:toggle_mark(node)
if node.absolute_path == nil then if node.absolute_path == nil then
return return
end end
if self:get(node) then if self:get_mark(node) then
self.marks[node.absolute_path] = nil self:remove_mark(node)
else else
self.marks[node.absolute_path] = node self:add_mark(node)
end end
renderer.draw() renderer.draw()
end end
---Return node if marked function Marks:clear_marks()
---@public self.marks = {}
renderer.draw()
end
---@param node Node ---@param node Node
---@return Node|nil ---@return Node|nil
function Marks:get(node) function Marks:get_mark(node)
return node and self.marks[node.absolute_path] return node and self.marks[node.absolute_path]
end end
---List marked nodes
---@public
---@return Node[] ---@return Node[]
function Marks:list() function Marks:get_marks()
local list = {} local list = {}
for _, node in pairs(self.marks) do for _, node in pairs(self.marks) do
table.insert(list, node) table.insert(list, node)
@@ -84,190 +67,12 @@ function Marks:list()
return list return list
end end
---Delete marked; each removal will be optionally notified function Marks.setup(opts)
---@public renderer = require "nvim-tree.renderer"
function Marks:bulk_delete()
if not next(self.marks) then
notify.warn "No bookmarks to delete."
return
end
local function execute() require("nvim-tree.marks.bulk-delete").setup(opts)
for _, node in pairs(self.marks) do require("nvim-tree.marks.bulk-trash").setup(opts)
remove_file.remove(node) require("nvim-tree.marks.bulk-move").setup(opts)
end
self:clear_reload()
end
if self.config.ui.confirm.remove then
local prompt_select = "Remove bookmarked ?"
local prompt_input = prompt_select .. " y/N: "
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short)
utils.clear_prompt()
if item_short == "y" then
execute()
end
end)
else
execute()
end
end
---Trash marked; each removal will be optionally notified
---@public
function Marks:bulk_trash()
if not next(self.marks) then
notify.warn "No bookmarks to trash."
return
end
local function execute()
for _, node in pairs(self.marks) do
trash.remove(node)
end
self:clear_reload()
end
if self.config.ui.confirm.trash then
local prompt_select = "Trash bookmarked ?"
local prompt_input = prompt_select .. " y/N: "
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_trash", function(item_short)
utils.clear_prompt()
if item_short == "y" then
execute()
end
end)
else
execute()
end
end
---Move marked
---@public
function Marks:bulk_move()
if not next(self.marks) then
notify.warn "No bookmarks to move."
return
end
local node_at_cursor = lib.get_node_at_cursor()
local default_path = core.get_cwd()
if node_at_cursor and node_at_cursor.type == "directory" then
default_path = node_at_cursor.absolute_path
elseif node_at_cursor and node_at_cursor.parent then
default_path = node_at_cursor.parent.absolute_path
end
local input_opts = {
prompt = "Move to: ",
default = default_path,
completion = "dir",
}
vim.ui.input(input_opts, function(location)
utils.clear_prompt()
if not location or location == "" then
return
end
if vim.fn.filewritable(location) ~= 2 then
notify.warn(location .. " is not writable, cannot move.")
return
end
for _, node in pairs(self.marks) do
local head = vim.fn.fnamemodify(node.absolute_path, ":t")
local to = utils.path_join { location, head }
rename_file.rename(node, to)
end
self:clear_reload()
end)
end
---Focus nearest marked node in direction.
---@private
---@param up boolean
function Marks:navigate(up)
local node = lib.get_node_at_cursor()
if not node then
return
end
local first, prev, next, last = nil, nil, nil, nil
local found = false
Iterator.builder(self.explorer.nodes)
:recursor(function(n)
return n.open and n.nodes
end)
:applier(function(n)
if n.absolute_path == node.absolute_path then
found = true
return
end
if not self:get(n) then
return
end
last = n
first = first or n
if found and not next then
next = n
end
if not found then
prev = n
end
end)
:iterate()
if not found then
return
end
if up then
utils.focus_node_or_parent(prev or last)
else
utils.focus_node_or_parent(next or first)
end
end
---@public
function Marks:navigate_prev()
self:navigate(true)
end
---@public
function Marks:navigate_next()
self:navigate(false)
end
---Prompts for selection of a marked node, sorted by absolute paths.
---A folder will be focused, a file will be opened.
---@public
function Marks:navigate_select()
local list = vim.tbl_map(function(n)
return n.absolute_path
end, self:list())
table.sort(list)
vim.ui.select(list, {
prompt = "Select a file to open or a folder to focus",
}, function(choice)
if not choice or choice == "" then
return
end
local node = self.marks[choice]
if node and not node.nodes and not utils.get_win_buf_from_path(node.absolute_path) then
open_file.fn("edit", node.absolute_path)
elseif node then
utils.focus_file(node.absolute_path)
end
end)
end end
return Marks return Marks

View File

@@ -0,0 +1,111 @@
local Iterator = require "nvim-tree.iterators.node-iterator"
local core = require "nvim-tree.core"
local open_file = require "nvim-tree.actions.node.open-file"
local utils = require "nvim-tree.utils"
local lib = require "nvim-tree.lib"
---@param node table
---@param where string
---@return Node|nil
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 found = false
Iterator.builder(explorer.nodes)
:recursor(function(n)
return n.open and n.nodes
end)
:applier(function(n)
if n.absolute_path == node.absolute_path then
found = true
return
end
if not explorer.marks:get_mark(n) then
return
end
last = n
first = first or n
if found and not next then
next = n
end
if not found then
prev = n
end
end)
:iterate()
if not found then
return
end
if where == "next" then
return next or first
else
return prev or last
end
end
---@param where string
---@param node table|nil
---@return Node|nil
local function get(where, node)
if node then
return get_nearest(node, where)
end
end
---@param node table|nil
local function open_or_focus(node)
if node and not node.nodes and not utils.get_win_buf_from_path(node.absolute_path) then
open_file.fn("edit", node.absolute_path)
elseif node then
utils.focus_file(node.absolute_path)
end
end
---@param where string
---@return function
local function navigate_to(where)
return function()
local node = lib.get_node_at_cursor()
local next = get(where, node)
open_or_focus(next)
end
end
local M = {}
M.next = navigate_to "next"
M.prev = navigate_to "prev"
function M.select()
local explorer = core.get_explorer()
if not explorer then
return
end
local list = vim.tbl_map(function(n)
return n.absolute_path
end, explorer.marks:get_marks())
vim.ui.select(list, {
prompt = "Select a file to open or a folder to focus",
}, function(choice)
if not choice or choice == "" then
return
end
local node = explorer.marks:get_mark { absolute_path = choice }
open_or_focus(node)
end)
end
return M

View File

@@ -21,7 +21,6 @@
---@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,7 +1,6 @@
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
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 DecoratorBookmarks = require "nvim-tree.renderer.decorator.bookmarks" local DecoratorBookmarks = require "nvim-tree.renderer.decorator.bookmarks"
local DecoratorCopied = require "nvim-tree.renderer.decorator.copied" local DecoratorCopied = require "nvim-tree.renderer.decorator.copied"
@@ -42,8 +41,6 @@ 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
@@ -63,7 +60,6 @@ function Builder:new()
markers = {}, markers = {},
signs = {}, signs = {},
extmarks = {}, extmarks = {},
virtual_lines = {},
} }
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
@@ -353,6 +349,7 @@ 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)
@@ -360,32 +357,6 @@ 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)
local explorer = core.get_explorer() local explorer = core.get_explorer()
@@ -416,7 +387,6 @@ 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
@@ -437,14 +407,17 @@ end
---@private ---@private
function Builder:build_header() function Builder:build_header()
local explorer = core.get_explorer() local explorer = core.get_explorer()
if view.is_root_folder_visible(core.get_cwd()) then if not explorer then
return
end
if explorer.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)
self:insert_highlight({ "NvimTreeRootFolder" }, 0, string.len(root_name)) self:insert_highlight({ "NvimTreeRootFolder" }, 0, string.len(root_name))
self.index = 1 self.index = 1
end end
if explorer and explorer.live_filter.filter then if explorer.live_filter.filter then
local filter_line = string.format("%s/%s/", M.opts.live_filter.prefix, explorer.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)
@@ -472,50 +445,7 @@ 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

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, early_stop) local function get_padding_indent_markers(depth, idx, nodes_number, markers, with_arrows, inline_arrows, node)
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 - early_stop do for i = 1, depth 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, early_stop) function M.get_indent_markers(depth, idx, nodes_number, node, markers)
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, early_sto
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, early_stop or 0) str = str .. get_padding_indent_markers(depth, idx, nodes_number, markers, show_arrows, inline_arrows, node)
else else
str = str .. string.rep(" ", depth * indent_width) str = str .. string.rep(" ", depth * indent_width)
end end

View File

@@ -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 core.get_explorer() and core.get_explorer().marks:get(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 core.get_explorer() and core.get_explorer().marks:get(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

@@ -1,4 +1,4 @@
local core = require "nvim-tree.core" local copy_paste
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
@@ -20,6 +20,9 @@ function DecoratorCopied:new(opts)
}) })
---@cast o DecoratorCopied ---@cast o DecoratorCopied
-- cyclic
copy_paste = copy_paste or require "nvim-tree.actions.fs.copy-paste"
return o return o
end end
@@ -27,7 +30,7 @@ end
---@param node Node ---@param node Node
---@return string|nil group ---@return string|nil group
function DecoratorCopied:calculate_highlight(node) function DecoratorCopied:calculate_highlight(node)
if self.hl_pos ~= HL_POSITION.none and core.get_explorer() and core.get_explorer().clipboard:is_copied(node) then if self.hl_pos ~= HL_POSITION.none and copy_paste.is_copied(node) then
return "NvimTreeCopiedHL" return "NvimTreeCopiedHL"
end end
end end

View File

@@ -1,4 +1,4 @@
local core = require "nvim-tree.core" local copy_paste
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
@@ -20,6 +20,9 @@ function DecoratorCut:new(opts)
}) })
---@cast o DecoratorCut ---@cast o DecoratorCut
-- cyclic
copy_paste = copy_paste or require "nvim-tree.actions.fs.copy-paste"
return o return o
end end
@@ -27,7 +30,7 @@ end
---@param node Node ---@param node Node
---@return string|nil group ---@return string|nil group
function DecoratorCut:calculate_highlight(node) function DecoratorCut:calculate_highlight(node)
if self.hl_pos ~= HL_POSITION.none and core.get_explorer() and core.get_explorer().clipboard:is_cut(node) then if self.hl_pos ~= HL_POSITION.none and copy_paste.is_cut(node) then
return "NvimTreeCutHL" return "NvimTreeCutHL"
end end
end end

View File

@@ -28,7 +28,7 @@ function DecoratorHidden:new(opts)
return o return o
end end
---Hidden icon: renderer.icons.show.hidden and node starts with `.` (dotfile). ---Hidden icon: hidden.enable, renderer.icons.show.hidden and node starts with `.` (dotfile).
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]|nil icons
function DecoratorHidden:calculate_icons(node) function DecoratorHidden:calculate_icons(node)
@@ -37,7 +37,7 @@ function DecoratorHidden:calculate_icons(node)
end end
end end
---Hidden highlight: renderer.highlight_hidden and node starts with `.` (dotfile). ---Hidden highlight: hidden.enable, renderer.highlight_hidden and node starts with `.` (dotfile).
---@param node Node ---@param node Node
---@return string|nil group ---@return string|nil group
function DecoratorHidden:calculate_highlight(node) function DecoratorHidden:calculate_highlight(node)

View File

@@ -1,6 +1,5 @@
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local view = require "nvim-tree.view"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
local _padding = require "nvim-tree.renderer.components.padding" local _padding = require "nvim-tree.renderer.components.padding"
@@ -14,13 +13,12 @@ local SIGN_GROUP = "NvimTreeRendererSigns"
local namespace_highlights_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_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, extmarks, virtual_lines) local function _draw(bufnr, lines, hl_args, signs, extmarks)
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
@@ -51,15 +49,6 @@ local function _draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines)
}) })
end end
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)
@@ -77,29 +66,33 @@ function M.render_hl(bufnr, hl)
end end
function M.draw() function M.draw()
local bufnr = view.get_bufnr() local explorer = core.get_explorer()
if not core.get_explorer() or not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then if not explorer then
return
end
local bufnr = explorer.view:get_bufnr()
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then
return return
end end
local profile = log.profile_start "draw" local profile = log.profile_start "draw"
local cursor = vim.api.nvim_win_get_cursor(view.get_winnr() or 0) local cursor = vim.api.nvim_win_get_cursor(explorer.view:get_winnr() or 0)
icon_component.reset_config() icon_component.reset_config()
local builder = Builder:new():build() local builder = Builder:new():build()
_draw(bufnr, builder.lines, builder.hl_args, builder.signs, builder.extmarks, builder.virtual_lines) _draw(bufnr, builder.lines, builder.hl_args, builder.signs, builder.extmarks)
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(explorer.view:get_winnr() or 0, cursor)
end end
view.grow_from_content() explorer.view:grow_from_content()
log.profile_end(profile) log.profile_end(profile)
events._dispatch_on_tree_rendered(bufnr, view.get_winnr()) events._dispatch_on_tree_rendered(bufnr, explorer.view:get_winnr())
end end
function M.setup(opts) function M.setup(opts)

View File

@@ -111,8 +111,8 @@ function M.find_node(nodes, fn)
return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes) return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes)
end) end)
:iterate() :iterate()
i = require("nvim-tree.view").is_root_folder_visible() and i or i - 1
local explorer = require("nvim-tree.core").get_explorer() local explorer = require("nvim-tree.core").get_explorer()
i = explorer and explorer.view:is_root_folder_visible() and i or i - 1
if explorer and explorer.live_filter.filter then if explorer and explorer.live_filter.filter then
i = i + 1 i = i + 1
end end
@@ -184,26 +184,6 @@ 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
@@ -464,10 +444,14 @@ function M.debounce(context, timeout, callback)
end end
function M.focus_file(path) function M.focus_file(path)
local _, i = M.find_node(require("nvim-tree.core").get_explorer().nodes, function(node) local explorer = require("nvim-tree.core").get_explorer()
if not explorer then
return
end
local _, i = M.find_node(explorer.nodes, function(node)
return node.absolute_path == path return node.absolute_path == path
end) end)
require("nvim-tree.view").set_cursor { i + 1, 1 } explorer.view:set_cursor { i + 1, 1 }
end end
---Focus node passed as parameter if visible, otherwise focus first visible parent. ---Focus node passed as parameter if visible, otherwise focus first visible parent.
@@ -487,7 +471,7 @@ function M.focus_node_or_parent(node)
end) end)
if found_node or node.parent == nil then if found_node or node.parent == nil then
require("nvim-tree.view").set_cursor { i + 1, 1 } explorer.view:set_cursor { i + 1, 1 }
break break
end end