Compare commits

..

191 Commits

Author SHA1 Message Date
Uanela Como
27aeba9b03 Merge branch 'master' into feat/rename-with-substitute 2026-07-14 12:15:43 +02:00
Igor Lacerda
cf18a662cd feat: experiment: restore nvim-tree on session load, see https://github.com/nvim-tree/nvim-tree.lua/issues/3343 (#3335)
* feat: restore buffers after loading a session

* feat(sessions): invalid json handling

* feat(sessions): switch to "persist in a file" strategy

* docs: no longer needs to toggle sessionoptions flag

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2026-07-10 11:27:15 +10:00
Alexander Courtis
a797d5e201 fix(#3346): api.tree.resize: 'configure_width' (a nil value) (#3347) 2026-07-09 10:21:38 +10:00
Alexander Courtis
35d3511e7a refactor(#3344): temporarily allow deprecated nvim_win_set_width until a replacement is provided (#3345) 2026-07-07 11:09:35 +10:00
Uanela Como
7ff7040bf6 docs: update README with cut, copy, and paste features between instances (#3342)
Update README with cut, copy, and paste features between instances
2026-07-02 11:05:47 +10:00
github-actions[bot]
531b807b8f chore(master): release nvim-tree 1.18.0 (#3318)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-01 12:53:44 +10:00
Uanela Como
c760c92667 feat(#1549): p will paste files from another nvim-tree instance, gp will cut/paste (#3336)
* feat: add copy absolute path with protocol into public api

* feat: add helper get_basename into node

* feat: add gby keymap, and allow visual mode for copy.basename|absolute_path

* feat(clipboard): add opts for adding protocol in reg operations

* feat(api): allow visual operation in absolute_path*

* feat: add paste with protocol to keymap+api

* feat: add paste with protocol to clipboard

* feat: add configurable clipboard protocol

* fix: filter descendant nodes on copy_absolute_path_with_protocol

* refactor: change clip protocol keymaps to bgy and bgp

* wip

* fix(clipboad): correctly show the number of copied nodes on bgy

* chore: remove bgy, bgp and add gp + gx

* chore: update api/fs.lua and api impl.lua to match new keymaps

* fix: correctly pass the function to new keymaps

* chore: remove use_protocol and use use_register + cut options

* chore: update docs

* chore: move node:get_basename to cliboard_

* chore(clipboard): remove vim.cmd workaround in favor of vim.fn.setreg

* chore: use newline as separator instead of comma

* refactor(clipboard): simplify copying node attribute and allow all to work in visual mode

* refactor(keymap): remove gx, use gp to move

* feat(clipboard): always copy/cut to register and make `p` pick local data or register data

* refactor(docs): update api docs and rename paste_while_cutting to move

* fix: correctly format keymaps

* chore: regenerate docs

* fix: remove unused apis

* chore: copy clipboard.data to register on copy/cut

* chore: revert copy_node_attribute

* refactor: remove visual mode from copy.basename|filename|path

* chore: update docs

* chore: add fs.move api and update docs

* chore: destroy dummy nodes and prevent empty paths nodes parsing

* fix: _meta/api/fs.lua and docs

* fix(clipboard): destroy dummy nodes from register on paste

* fix(clipboard): correctly check for local paste operation
2026-07-01 10:11:03 +10:00
Uanela Como
0d1176b4c5 Merge branch 'master' into feat/rename-with-substitute 2026-06-22 09:23:43 +02:00
dependabot[bot]
8e8be709c9 chore(deps): bump actions/checkout from 6 to 7 (#3340)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 13:16:28 +10:00
Uanela Como
85a743b8f1 Merge branch 'master' into feat/rename-with-substitute 2026-06-18 08:18:11 +02:00
uanela
68992ee244 feat: add rename with substitue command 2026-06-16 23:49:46 +02:00
Alexander Courtis
fb343438d4 docs: generated help fields are sorted as of v0.12.3 (#3337)
* doc: generated help fields are sorted as of v0.12.3

* ci: add weekly trigger for CI job
2026-06-15 12:30:16 +10:00
Sean McCollum
82f58063d6 docs: spelling error (#3333) 2026-06-08 14:32:39 +10:00
Alexander Courtis
07f541fcaa fix(#3327): pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function (#3329)
* fix(#3327): pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function

* fix(#3327): pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function

* fix(#3327): pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function

* fix(#3327): pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function

* fix(#3327): pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function
2026-05-16 08:49:14 +10:00
Alexander Courtis
f9bfc0059e fix(#3324): deprecated BufModifiedSet event replaced by OptionSet event for Nvim 0.13+ (#3325)
* fix(#3324): deprecated BufModifiedSet event replaced by OptionSet event for Nvim 0.13+

* fix(#3324): deprecated BufModifiedSet event replaced by OptionSet event for Nvim 0.13+

* ci: remove help-check for Nvim nightly
2026-05-01 08:31:56 +10:00
dependabot[bot]
24cfcc9437 chore(deps): bump googleapis/release-please-action from 4 to 5 (#3319)
Bumps [googleapis/release-please-action](https://github.com/googleapis/release-please-action) from 4 to 5.
- [Release notes](https://github.com/googleapis/release-please-action/releases)
- [Changelog](https://github.com/googleapis/release-please-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/release-please-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: googleapis/release-please-action
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-26 09:20:37 +10:00
dependabot[bot]
5f65bd3d93 chore(deps): bump leafo/gh-actions-lua from 12 to 13 (#3320)
Bumps [leafo/gh-actions-lua](https://github.com/leafo/gh-actions-lua) from 12 to 13.
- [Release notes](https://github.com/leafo/gh-actions-lua/releases)
- [Commits](https://github.com/leafo/gh-actions-lua/compare/v12...v13)

---
updated-dependencies:
- dependency-name: leafo/gh-actions-lua
  dependency-version: '13'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2026-04-26 09:18:06 +10:00
Alexander Courtis
5a1d08bc0c docs: remove lintdoc from help-check, skip the name checker during help-update (#3322)
* docs: remove lintdoc from help-check, skip the name checker during help-update

* docs: test new help-check

* Revert "docs: test new help-check"

This reverts commit 52128372ca.
2026-04-26 09:16:14 +10:00
Tomasz N
d277467fc0 fix(#3312): correct cursor when full name shown, requires nvim 0.12.2+ (#3316)
fix(full-name): lower `zindex` to get consistent cursor shape

See https://github.com/neovim/neovim/pull/39054
2026-04-16 10:51:29 +10:00
Alexander Courtis
509962f21a docs: update to 0.12 help format, remove blank line help suppression (#3314)
doc: update to 0.12 help format, remove blank line help suppression
2026-04-07 11:09:12 +10:00
github-actions[bot]
85d1145ac7 chore(master): release nvim-tree 1.17.0 (#3296)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-07 10:46:51 +10:00
Alexander Courtis
7adc5679fe feat(#3310): minimum neovim version 0.10 (#3311)
* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10

* feat(#3310): minimum neovim version 0.10
2026-04-07 10:37:49 +10:00
Alexander Courtis
31503ad5d8 perf(#3231): decrease setup time: optimise setup and defer requires until tree open (#3308)
* perf(#3257): remove devicons setup

* perf(#3257): remove padding setup

* perf(#3257): remove appearance and log setup

* perf(#3257): remove appearance setup

* perf(#3257): remove rename-file setup

* perf(#3257): remove devicons setup

* perf(#3257): remove appearance setup

* perf(#3257): inline require legacy notify

* perf(#3257): inline require events notify

* perf(#3257): inline require log notify

* perf(#3253): extract change-root

* perf(#3253): extract open_on_directory

* perf(#3253): extract tab_enter

* perf(#3253): move requires inline

* perf(#3253): extract au find-file, view

* perf(#3253): extract manage_netrw

* perf(#3253): extract purge_all_state and document

* perf(#3253): move full-name setup_autocommands to main

* perf(#3253): unextract au find-file for laziness

* perf(#3253): short-circuit open_on_directory au

* perf(#3253): extract autocmd

* perf(#3253): move all setup requires inside the function
2026-04-02 13:33:16 +11:00
Alexander Courtis
02ccbcd4af fix: system open native requires nvim 0.10 (#3309) 2026-04-01 13:52:44 +11:00
Alexander Courtis
3603f069ff perf(#3257): remove setup for renderer, log, appearance (#3307)
* perf(#3257): remove devicons setup

* perf(#3257): remove padding setup

* perf(#3257): remove appearance and log setup

* perf(#3257): remove appearance setup

* perf(#3257): remove rename-file setup

* perf(#3257): remove devicons setup

* perf(#3257): remove appearance setup

* perf(#3257): inline require legacy notify

* perf(#3257): inline require events notify

* perf(#3257): inline require log notify
2026-03-31 12:21:08 +11:00
Alexander Courtis
4cbe79526f perf(#3257): remove setup for view, extracting view-state (#3306)
* perf(#3257): extract view-state

* fix(#3257): move prev_focused_node from View to LiveFilter
2026-03-30 14:22:19 +11:00
Alexander Courtis
b548cfef00 fix(#3257): error when calling api.tree.find_file (#3304) 2026-03-28 15:07:18 +11:00
Alexander Courtis
ec0efb06b0 fix(#3301): setup with empty config fails (#3303) 2026-03-28 13:06:40 +11:00
Alexander Courtis
43a3bb4b26 perf(#3257): remove setup for actions modules, full-name and explorer (#3299)
* perf(#3257): remove remove-file setup

* perf(#3257): remove rename-file setup

* perf(#3257): remove trash setup

* perf(#3257): remove actions.fs init

* perf(#3257): remove file-popup setup

* perf(#3257): remove open-file setup

* perf(#3257): remove system-open setup

* perf(#3257): remove open-file setup

* perf(#3257): remove actions.node init

* perf(#3257): remove change-dir setup

* perf(#3257): remove find-file setup

* perf(#3257): remove open setup

* perf(#3257): remove toggle setup

* perf(#3257): remove resize setup

* perf(#3257): remove finders init

* perf(#3257): remove moves init

* perf(#3257): remove tree init

* perf(#3257): remove explorer setup

* perf(#3257): view uses config.g

* perf(#3257): move os from utils to config, removing requires

* perf(#3257): move os from utils to config, removing requires

* perf(#3257): move os from utils to config, removing requires

* perf(#3257): remove watch setup

* perf(#3257): remove notify setup

* perf(#3257): remove full-name setup

* perf(#3257): remove padding setup

* Revert "perf(#3257): remove padding setup"

This reverts commit 862db0366b.
2026-03-28 11:21:57 +11:00
Alexander Courtis
6eaf74c991 feat(#3292): add nvim_tree.config.filesystem_watchers.whitelist_dirs - strongly recommended for windows (#3298)
* feat(#3292): add nvim_tree.config.filesystem_watchers.whitelist_dirs

* feat(#3292): add nvim_tree.config.filesystem_watchers.whitelist_dirs

* feat(#3292): add nvim_tree.config.filesystem_watchers.whitelist_dirs
2026-03-26 10:40:03 +11:00
Alexander Courtis
3d385d3346 perf(#3257): remove setup for modules: buffers, diagnostics, git, help, keymap, lib, watcher (#3295)
* perf(#3257): remove keymap setup

* perf(#3257): remove diagnostics setup

* perf(#3257): remove diagnostics setup

* perf(#3257): remove help setup

* perf(#3257): remove help setup

* perf(#3257): remove watcher setup

* perf(#3257): remove buffers setup

* perf(#3257): remove lib setup

* perf(#3257): remove git setup

* perf(#3257): remove git utils setup

* perf(#3257): remove buffers setup
2026-03-24 10:42:27 +11:00
Vlad Ceban
f37ebac811 fix(#3293): marks.bulk operations filter descendant nodes (#3294) 2026-03-23 10:49:34 +11:00
Alexander Courtis
dfeeb12aaa refactor(#3255): all API is lazily loaded (#3287)
* refactor(#3255): wire node.navigate.sibling API impl directly to functions

* refactor(#3255): wire fs.rename API impl directly to functions

* refactor(#3255): wire node.navigate.parent API impl directly to functions

* refactor(#3255): wire remaining node.navigate API impl directly to functions

* refactor(#3255): wire node.open API impl directly to functions, move edit and open_or_expand_or_dir_up to open-file

* refactor(#3255): wire tree.change_root API impl directly to a function in new action module change-dir

* refactor(#3255): lazy post requires, split into formatted groups

* refactor(#3255): all post are lazy

* refactor(#3255): all post are wrapped consistently

* refactor(#3255): all pre are lazy

* refactor(#3255): move Event to meta

* refactor(#3255): ensure all args passed to api

* refactor(#3255): ensure all args passed to api, fix incorrectly documented api interface

* refactor(#3255): ensure all args passed to api, fix incorrectly documented api interface

* refactor(#3255): ensure all args passed to api

* refactor(#3255): add visual mode wrappers

* refactor(#3255): define @enum nvim_tree.api.events.Event

* refactor(#3255): merge api pre and post

* refactor(#3255): merge api pre and post

* refactor(#3255): merge api pre and post
2026-03-20 15:13:24 +11:00
github-actions[bot]
e16cd38962 chore(master): release nvim-tree 1.16.0 (#3291)
* chore(master): release nvim-tree 1.16.0

* chore(master): tidy changelog

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2026-03-18 09:00:38 +11:00
Alexander Courtis
2eefa8303b ci: migrate to googleapis/release-please-action (#3290)
* ci: migrate to googleapis/release-please-action

* ci: migrate to googleapis/release-please-action

* ci: migrate to googleapis/release-please-action

* ci: migrate to googleapis/release-please-action

* ci: migrate to googleapis/release-please-action
2026-03-18 08:55:11 +11:00
Vlad Ceban
b3772adec8 fix(##3288): restore single-item paste conflict prompts (#3289) 2026-03-15 15:21:22 +11:00
Vlad Ceban
9197f3ee3f feat(#2994): add visual mode operations: copy, cut, delete, trash, toggle bookmark (#3268)
feat(#2994): add visual selection operations
2026-03-13 14:11:48 +11:00
Alexander Courtis
e1ad8b9683 chore: build scripts use sed -E for cross-OS compatibility (#3275)
* chore: build scripts use sed -E for cross-OS compatibility

* chore: build scripts use sed -E for cross-OS compatibility

---------

Co-authored-by: Uanela Como <104281989+Uanela@users.noreply.github.com>
2026-03-12 10:02:53 +11:00
Alexander Courtis
4b30847c91 docs: clarify root_folder_label=false (not true) to disable, tidy nvim_tree_config.renderer help (#3286)
docs: root_folder_label=false to disable, tidy nvim_tree_config.renderer help
2026-03-05 14:07:36 +11:00
Alexander Courtis
c8d8d515c2 perf(#3284): re-draw instead of refreshing the tree when a buffer is modified (#3285) 2026-03-04 11:53:00 +11:00
Šimon Mandlík
c988e28942 fix(#3281): fix a bug when a view width of -1 is returned from a function (#3283) 2026-02-28 15:27:13 +11:00
Fidel Yin
018a078c1e fix(#3187): prevent closing the last non-floating window when deleting files (#3282) 2026-02-27 14:51:18 +11:00
Alexander Courtis
ae16aab5fd refactor(#3256): move setup/config related functionality to new module config.lua (#3262)
* refactor(#3253): move setup/config related functionality to new module config.lua

* refactor(#3253): add api.config, tidy new config module

* refactor(#3253): add api.config, tidy new config module

* refactor(#3253): add api.config, tidy new config module

* refactor(#3253): format

* refactor(#3256): fix doc generator

* refactor(#3256): fix doc generator

* refactor(#3256): strongly type legacy config

* refactor(#3256): legacy config consistent parameter naming

* refactor(#3256): fix bad rebase
2026-02-25 13:18:42 +11:00
Alexander Courtis
87594aa7c8 fix: allow 0 (unlimited) filesystem_watchers.max_events which is the new default, except for windows at 1000 (#3279) 2026-02-24 17:27:31 +11:00
Alexander Courtis
e49b0d9bfa fix(#3251): pass git.timeout to all vim.system git calls (#3277) 2026-02-24 10:35:08 +11:00
Alexander Courtis
1b5d96696b docs: AI usage policy (#3276)
* docs: AI usage policy

* docs: AI usage policy
2026-02-24 09:12:57 +11:00
Uanela Como
5499299746 fix(#3178): handle Windows paths in ignore_dirs and add .zig-cache to defaults (#3261)
* feat: add .zig-cache in default ignored_dirs

* feat: correctly add .zig-cache in default ignored_dirs

* fix(#3178): handle windows paths in ignore_dirs

* fix: remove unsed print statement

* fix: tab formatting

* chore: match with documentation

* chore: double espace backslashes and revert unrelated formatting

* chore: correctly rever unrelated formatting

* chore: indenting explorer/watch

* fix: remove unneeded print

* chore: remove unneeded true check

* chore: adding .zig-cache into nvim-tree.lua

* chore: add /.zig-cache under filesystem_watchers

* chore: regenerate docs
2026-02-23 13:07:24 +11:00
Alexander Courtis
e11ce83ed9 chore(#3271): add new format-check, rename style-fix to format-fix (#3273)
* chore(#3271): add new format-check, rename style-fix to format-fix

* chore(#3271): CI install CodeFormat from luarocks

* chore(#3271): CI install CodeFormat from luarocks

* chore(#3271): CI install CodeFormat from luarocks

* chore(#3271): remove bad printf after format-check failure

* chore(#3271): install CodeFormat from binary as luarocks is too slow

* chore(#3271): no broken windows - all formatting fixed
2026-02-18 10:49:39 +11:00
Alexander Courtis
5dd95eaa23 docs: explicitly state no AI review tools on PRs (#3272) 2026-02-18 10:36:14 +11:00
Alexander Courtis
fa3c45875f fix(#3267): renderer.icons.*_placement 'right_align' at the right hand edge, not the right of the name (#3270) 2026-02-16 18:15:10 +11:00
Devansh Sharma
516f95e8ef docs: fix default mappings function name in examples: api.map.on_attach.default (#3269)
docs: fix default mappings function name
2026-02-16 09:48:09 +11:00
Alexander Courtis
1df1960d0e fix(#3265): rename module api.health to api.appearance, to avoid :checkhealth detection (#3266) 2026-02-14 11:17:44 +11:00
Alexander Courtis
2f1010ca77 docs: help links regular-expression instead of vim.regex (#3264)
doc: help: link regular-expression instead of vim.regex
2026-02-13 13:44:18 +11:00
Alexander Courtis
0f4d2d6998 fix: increase filesystem_watchers.max_events from 100 to 1000 (#3263)
* fix: increase filesystem_watchers.max_events from 100 to 1000

* fix: increase filesystem_watchers.max_events from 100 to 1000

* fix: increase filesystem_watchers.max_events from 100 to 1000
2026-02-13 13:37:19 +11:00
Alexander Courtis
118057ebd2 docs(#3241): add api.Decorator and Class meta with generated help, harden decorator rendering, api.decorator.UserDecorator changed to nvim_tree.api.Decorator in a non-breaking manner (#3259)
* doc(#2934): POC to use gen_vimdoc.lua for decorator meta

* doc(#2934): use injected gen_vimdoc.lua from nvim source, move decorators to main help

* doc(#2934): tidy and harden scripts

* doc(#2934): add nvim_tree.Config meta classes, add nvim_tree.api. meta classes

* doc(#2934): add nvim_tree.Config to help

* doc(#2934): add nvim_tree.api classes to help

* doc(#2934): tidy and correct nvim_tree.Config.UI and nvim_tree.Config.Actions

* doc(#2934): tidy and correct nvim_tree.Config.HijackDirectories, format and order nvim_tree.Config

* doc(#2934): tidy and nvim_tree.Config top level

* doc(#2934): tidy and format nvim_tree.Config.LiveFilter, Modified, Tab, Trash

* doc(#2934): tidy and format nvim_tree.Config.FilesystemWatchers, Log

* doc(#2934): split nvim_tree.Config into files, Sort and HijackDirectories populate others placeholder

* doc(#2934): more generic gen_vimdoc_config module config

* doc(#2934): tidy Config.HijackDirectories and Actions, move them into place

* doc(#2934): tidy Config.Modified, move into place

* doc(#2934): tidy Config.LiveFilter, move into place

* doc(#2934): tidy Config.FilesystemWatchers, move into place

* doc(#2934): tidy Config.Trash, move into place

* doc(#2934): tidy Config.Tab, move into place

* doc(#2934): tidy Config.UI, move into place

* doc(#2934): tidy Config.Log, move into place

* doc(#2934): tidy Config.Notify, move into place

* doc(#2934): tidy Config.SystemOpen, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Diagnostics, move into place

* doc(#2934): remove api/decorator as they are out of scope, revert api_decorator.lua changes, retain api opts classes but make them exact

* doc(#2934): tidy Config.Filters, move into place

* doc(#2934): tidy Config.FilesystemWatchers, move into place

* doc(#2934): tidy Config.FilesystemWatchers, Git, move into place

* doc(#2934): tidy Config.UpdateFocusedFile, move into place

* doc(#2934): tidy Config.Sort, move into place

* doc(#2934): tidy Config.View, move into place

* doc(#2934): tidy Config.View, move into place

* docs(#2934): type DEFAULT_OPTS, use bracketed references to keep luals happy

* docs(#2934): tidy, inline some classes

* docs(#2934): tidy Config.Renderer

* docs(#2934): remove problematic glyphs, tidy other glyphs

* docs(#2934): tidy Config.Renderer, ensure functions are parenthesised when necessary

* docs(#2934): tidy Config.Renderer

* docs(#2934): tidy Config.Renderer, move into place

* docs(#2934): tidy Config.Renderer

* docs(#2934): tidy Config.FilesystemWatchers

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): refer to nvim in help, not neovim, as this trips the lintdoc spell checker

* docs(#2934): fix broken links

* docs(#2934): add lintdoc.sh, fix some branding spelling

* docs(#2934): update old config links

* docs(#2934): remove 5. Opts, 8.2  Highlight: Overhaul, 15.1  Index: Opts, update links as needed

* docs(#2934): remove indices

* docs(#2934): add bookmarks, put long descriptions into @brief

* docs(#2934): move hidden display to verbatim new section

* docs(#2934): add config lsp examples, link setup

* docs(#2934): normalise heading formatting, remove TOC

* docs(#2934): move all icon and highlight info into a table

* docs(#2934): move all icon and highlight info into a new section

* docs(#2934): normalise all icon and highlight

* docs(#2934): extract nvim_tree.Config field descriptions to class overview

* docs(#2934): consistent string formatting

* docs(#2934): normalise all icon and highlight

* docs(#2934): remove @briefs as they are shown above the class tag

* docs(#2934): don't <pre> filters

* docs(#2934): add experimental example

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): temporarily --ignore-blank-lines during help check

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): move Nvim build and help-check to the end for faster turnaround

* docs(#2934): use make lintdoc target directly

* docs(#2934): CONTRIBUTING.md updates for help generation and lint

* docs(#2934): revert API opts classes changes

* docs(#2934): harden gen and lint scripts, moving things out of nvim-tree source to prevent luals upset

* docs(#2934): harden gen and lint scripts, moving things out of nvim-tree source to prevent luals upset

* docs(#2934): fix lintdoc make target

* docs(#3088): extract api/tree.lua

* docs(#3088): extract api/tree.lua

* docs(#2934): move help diff check after lint

* docs(#3088): update api.tree references, remove old doc

* docs(#3088): extract api/config/mappings.lua

* docs(#3088): api.config.mappings -> api.map

* docs(#3088): extract api/filter.lua

* docs(#3088): move api.git into api.tree

* docs(#3088): extract api/health.lua, tidy formatting

* docs(#3088): extract api/health.lua, tidy formatting

* docs(#3088): remove command descriptions, show lua, link to api

* docs(#3088): extract api/marks.lua

* docs(#3088): remove completed legacy api.lua members

* docs(#3088): extract api/events.lua

* docs(#3088): extract api/events.lua

* docs(#3088): extract api/fs.lua

* docs(#3088): extract api/commands.lua

* docs(#3088): extract api/node.lua functions, doc TODO

* docs(#3088): move all nvim-tree-api to generated doc

* docs(#3088): old api is now impl, use new api

* docs(#3088): extract api/node.lua functions, doc WIP

* docs(#3088): extract api/node.lua functions, doc WIP

* docs(#3088): extract api/node.lua functions, doc WIP

* docs(#3088): extract api/node.lua functions

* docs(#3088): move api into _meta

* docs(#3088): move api-impl out of meta, restore it to its original state with a hyrdrate function

* docs(#3088): remove old Api classes, rename impl Api -> api

* docs(#3088): tidy migrated annotations

* docs(#3088): format nvim_tree.api.commands

* docs(#3088): separate api classes at the end

* docs(#3088): polish vimdoc config

* docs(#3088): format

* docs(#2934): polish vimdoc config

* docs(#2934): snake case config class names

* docs(#2934): snake case config alias names

* docs(#2934): note help tag prefixes

* docs(#2934): snake case config sections

* docs(#2934): move default config to end

* docs(#2934): move setup doc from config to quickstart and setup

* docs(#2934): index contributing

* docs(#3088): cleanup following merge

* docs(#3088): tidy section names

* docs(#3088): split node.navigate and open, move all filters into api.filter

* docs(#3088): hidden->custom toggle default desc

* docs(#3088): add legacy filter api mappings

* docs(#3088): tidy api meta

* docs(#3088): always exclude meta from luacheck, it doesn't offer anything useful

* docs(#3088): extract fs.copy, fix api node param optionality, move legacy api to legacy.lua

* docs(#3088): correct optionality of nvim_tree.config.view.width fields

* docs(#3088): hydrate API before setup with error functions, with some hydrated with their concrete function, wrap removal TODO

* docs(#3088): remove api impl error wrap

* docs(#3088): split api pre and post, add more comments

* docs(#3088): split api pre and post, add more comments

* docs(#3088): extract post impl functions, lazy some requires

* docs(#3088): lazy api impl requires

* Revert "docs(#3088): lazy api impl requires"

This reverts commit c6ea6c5be1.

* docs(#3088): minimise api pre following tests

* docs(#3088): move node classes under api with logical order

* docs(#3088): document node and git classes, namespace internal and external git classes, redact node subclasses

* docs(#3088): fix merge issue

* docs(#3088): tidy pre comments

* docs(#3088): collapse node and fs submodules

* docs(#3088): move api help and classes back into api.lua

* docs(#3088): resize command calls api, not view

* docs(#3088): tidy api map names

* docs(#3088): revert api.git refactor, provide deprecated api meta

* docs(#3088): add deprecated api @see

* docs(#3088): class fixes following merge, disable luacheck on scripts

* docs(#3088): Node:should_expand nops

* docs(#3088): fix _git return

* docs(#3088): add meta for legacy api

* docs(#3088): mark deprecated api modules, use proper namespacing for deprecateds

* docs(#3088): gen_vimdoc now uses full paths, requires a patch for a FIXME

* docs(#3088): add vim_gendoc patches

* Revert "docs(#3088): add vim_gendoc patches"

This reverts commit ebb99093d2.

* Revert "docs(#3088): gen_vimdoc now uses full paths, requires a patch for a FIXME"

This reverts commit ff0184a260.

* docs(#3088): split gen_vimdoc_config into chunks to avoid filename clashes, as gen_vimdoc keys sections by filename

* docs(#3088): move legacy api into api.impl.legacy, fixes following testing

* docs(#3088): remove merge leftover

* docs(#3088): tidy contrib

* docs(#3088): clarify backwards compatibility

* docs(#3088): clarify help generation in contributing

* docs(#3088): contributing index

* docs(#3088): help-update.sh -> help-defaults.sh now using a /tmp file to prevent runaway FS updates

* docs(#3088): help-update.sh -> help-defaults.sh now using a /tmp file to prevent runaway FS updates

* docs(#3088): CI runs make lintdoc to avoid muddying help-check with unnecessary build and possible Nvim lint failures

* docs(#3088): unify help and readme, clarify some wording

* docs(#3088): test a readme toc

* docs(#3088): test a readme toc

* docs(#3088): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute (#3260)

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): namespace decorator types

* docs(#3241): namespace decorator types

* docs(#3241): namespace decorator types

* docs(#3241): api exposes nvim_tree.api.decorator.Decorator interface, UserDecorator deprecated

* docs(#3241): move decorator meta into place

* docs(#3241): generate decorator help

* docs(#3241): separate api and class generation configs, namespace classes without their filename, nicer generation placeholder

* docs(#3241): fix potential bug in builder: passing a nil node to a user decorator

* docs(#3241): nvim-tree-api-decorator brief

* docs(#3241): Decorator class documentation

* docs(#3241): add placholder to vimdoc.sh

* docs(#3241): extract nvim_tree.config.renderer.decorator, add decorators to hl help section

* docs(#3241): use general nvim_tree.api.highlighted_string

* docs(#3241): add nvim-tree-class-decorator-example

* docs(#3241): move renderer alias doc to the class

* docs(#3241): Class -> nvim_tree.Class

* docs(#3241): Class -> nvim_tree.Class

* docs(#3241): document nvim_tree.Class

* docs(#3241): fix help lua indentation

* docs(#3241): extract builtin decorator interface, partially complete

* docs(#3241): extracted BuiltinDecorator

* docs(#3241): add BuiltinDecorator constructor

* docs(#3241): revert api function changes

* docs(#3241): revert api function changes

* docs(#3241): vimdoc polish

* docs(#3241): tidy

* docs(#3241): tidy
2026-02-13 12:58:03 +11:00
Alexander Courtis
c0b18e4879 docs(#3088): add nvim-tree.api* function and class meta, generate help API, lazy load API before setup, rename some API in a non-breaking manner (#3244)
* doc(#2934): POC to use gen_vimdoc.lua for decorator meta

* doc(#2934): use injected gen_vimdoc.lua from nvim source, move decorators to main help

* doc(#2934): tidy and harden scripts

* doc(#2934): add nvim_tree.Config meta classes, add nvim_tree.api. meta classes

* doc(#2934): add nvim_tree.Config to help

* doc(#2934): add nvim_tree.api classes to help

* doc(#2934): tidy and correct nvim_tree.Config.UI and nvim_tree.Config.Actions

* doc(#2934): tidy and correct nvim_tree.Config.HijackDirectories, format and order nvim_tree.Config

* doc(#2934): tidy and nvim_tree.Config top level

* doc(#2934): tidy and format nvim_tree.Config.LiveFilter, Modified, Tab, Trash

* doc(#2934): tidy and format nvim_tree.Config.FilesystemWatchers, Log

* doc(#2934): split nvim_tree.Config into files, Sort and HijackDirectories populate others placeholder

* doc(#2934): more generic gen_vimdoc_config module config

* doc(#2934): tidy Config.HijackDirectories and Actions, move them into place

* doc(#2934): tidy Config.Modified, move into place

* doc(#2934): tidy Config.LiveFilter, move into place

* doc(#2934): tidy Config.FilesystemWatchers, move into place

* doc(#2934): tidy Config.Trash, move into place

* doc(#2934): tidy Config.Tab, move into place

* doc(#2934): tidy Config.UI, move into place

* doc(#2934): tidy Config.Log, move into place

* doc(#2934): tidy Config.Notify, move into place

* doc(#2934): tidy Config.SystemOpen, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Diagnostics, move into place

* doc(#2934): remove api/decorator as they are out of scope, revert api_decorator.lua changes, retain api opts classes but make them exact

* doc(#2934): tidy Config.Filters, move into place

* doc(#2934): tidy Config.FilesystemWatchers, move into place

* doc(#2934): tidy Config.FilesystemWatchers, Git, move into place

* doc(#2934): tidy Config.UpdateFocusedFile, move into place

* doc(#2934): tidy Config.Sort, move into place

* doc(#2934): tidy Config.View, move into place

* doc(#2934): tidy Config.View, move into place

* docs(#2934): type DEFAULT_OPTS, use bracketed references to keep luals happy

* docs(#2934): tidy, inline some classes

* docs(#2934): tidy Config.Renderer

* docs(#2934): remove problematic glyphs, tidy other glyphs

* docs(#2934): tidy Config.Renderer, ensure functions are parenthesised when necessary

* docs(#2934): tidy Config.Renderer

* docs(#2934): tidy Config.Renderer, move into place

* docs(#2934): tidy Config.Renderer

* docs(#2934): tidy Config.FilesystemWatchers

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): refer to nvim in help, not neovim, as this trips the lintdoc spell checker

* docs(#2934): fix broken links

* docs(#2934): add lintdoc.sh, fix some branding spelling

* docs(#2934): update old config links

* docs(#2934): remove 5. Opts, 8.2  Highlight: Overhaul, 15.1  Index: Opts, update links as needed

* docs(#2934): remove indices

* docs(#2934): add bookmarks, put long descriptions into @brief

* docs(#2934): move hidden display to verbatim new section

* docs(#2934): add config lsp examples, link setup

* docs(#2934): normalise heading formatting, remove TOC

* docs(#2934): move all icon and highlight info into a table

* docs(#2934): move all icon and highlight info into a new section

* docs(#2934): normalise all icon and highlight

* docs(#2934): extract nvim_tree.Config field descriptions to class overview

* docs(#2934): consistent string formatting

* docs(#2934): normalise all icon and highlight

* docs(#2934): remove @briefs as they are shown above the class tag

* docs(#2934): don't <pre> filters

* docs(#2934): add experimental example

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): temporarily --ignore-blank-lines during help check

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): move Nvim build and help-check to the end for faster turnaround

* docs(#2934): use make lintdoc target directly

* docs(#2934): CONTRIBUTING.md updates for help generation and lint

* docs(#2934): revert API opts classes changes

* docs(#2934): harden gen and lint scripts, moving things out of nvim-tree source to prevent luals upset

* docs(#2934): harden gen and lint scripts, moving things out of nvim-tree source to prevent luals upset

* docs(#2934): fix lintdoc make target

* docs(#3088): extract api/tree.lua

* docs(#3088): extract api/tree.lua

* docs(#2934): move help diff check after lint

* docs(#3088): update api.tree references, remove old doc

* docs(#3088): extract api/config/mappings.lua

* docs(#3088): api.config.mappings -> api.map

* docs(#3088): extract api/filter.lua

* docs(#3088): move api.git into api.tree

* docs(#3088): extract api/health.lua, tidy formatting

* docs(#3088): extract api/health.lua, tidy formatting

* docs(#3088): remove command descriptions, show lua, link to api

* docs(#3088): extract api/marks.lua

* docs(#3088): remove completed legacy api.lua members

* docs(#3088): extract api/events.lua

* docs(#3088): extract api/events.lua

* docs(#3088): extract api/fs.lua

* docs(#3088): extract api/commands.lua

* docs(#3088): extract api/node.lua functions, doc TODO

* docs(#3088): move all nvim-tree-api to generated doc

* docs(#3088): old api is now impl, use new api

* docs(#3088): extract api/node.lua functions, doc WIP

* docs(#3088): extract api/node.lua functions, doc WIP

* docs(#3088): extract api/node.lua functions, doc WIP

* docs(#3088): extract api/node.lua functions

* docs(#3088): move api into _meta

* docs(#3088): move api-impl out of meta, restore it to its original state with a hyrdrate function

* docs(#3088): remove old Api classes, rename impl Api -> api

* docs(#3088): tidy migrated annotations

* docs(#3088): format nvim_tree.api.commands

* docs(#3088): separate api classes at the end

* docs(#3088): polish vimdoc config

* docs(#3088): format

* docs(#2934): polish vimdoc config

* docs(#2934): snake case config class names

* docs(#2934): snake case config alias names

* docs(#2934): note help tag prefixes

* docs(#2934): snake case config sections

* docs(#2934): move default config to end

* docs(#2934): move setup doc from config to quickstart and setup

* docs(#2934): index contributing

* docs(#3088): cleanup following merge

* docs(#3088): tidy section names

* docs(#3088): split node.navigate and open, move all filters into api.filter

* docs(#3088): hidden->custom toggle default desc

* docs(#3088): add legacy filter api mappings

* docs(#3088): tidy api meta

* docs(#3088): always exclude meta from luacheck, it doesn't offer anything useful

* docs(#3088): extract fs.copy, fix api node param optionality, move legacy api to legacy.lua

* docs(#3088): correct optionality of nvim_tree.config.view.width fields

* docs(#3088): hydrate API before setup with error functions, with some hydrated with their concrete function, wrap removal TODO

* docs(#3088): remove api impl error wrap

* docs(#3088): split api pre and post, add more comments

* docs(#3088): split api pre and post, add more comments

* docs(#3088): extract post impl functions, lazy some requires

* docs(#3088): lazy api impl requires

* Revert "docs(#3088): lazy api impl requires"

This reverts commit c6ea6c5be1.

* docs(#3088): minimise api pre following tests

* docs(#3088): move node classes under api with logical order

* docs(#3088): document node and git classes, namespace internal and external git classes, redact node subclasses

* docs(#3088): fix merge issue

* docs(#3088): tidy pre comments

* docs(#3088): collapse node and fs submodules

* docs(#3088): move api help and classes back into api.lua

* docs(#3088): resize command calls api, not view

* docs(#3088): tidy api map names

* docs(#3088): revert api.git refactor, provide deprecated api meta

* docs(#3088): add deprecated api @see

* docs(#3088): class fixes following merge, disable luacheck on scripts

* docs(#3088): Node:should_expand nops

* docs(#3088): fix _git return

* docs(#3088): add meta for legacy api

* docs(#3088): mark deprecated api modules, use proper namespacing for deprecateds

* docs(#3088): gen_vimdoc now uses full paths, requires a patch for a FIXME

* docs(#3088): add vim_gendoc patches

* Revert "docs(#3088): add vim_gendoc patches"

This reverts commit ebb99093d2.

* Revert "docs(#3088): gen_vimdoc now uses full paths, requires a patch for a FIXME"

This reverts commit ff0184a260.

* docs(#3088): split gen_vimdoc_config into chunks to avoid filename clashes, as gen_vimdoc keys sections by filename

* docs(#3088): move legacy api into api.impl.legacy, fixes following testing

* docs(#3088): remove merge leftover

* docs(#3088): tidy contrib

* docs(#3088): clarify backwards compatibility

* docs(#3088): clarify help generation in contributing

* docs(#3088): contributing index

* docs(#3088): help-update.sh -> help-defaults.sh now using a /tmp file to prevent runaway FS updates

* docs(#3088): help-update.sh -> help-defaults.sh now using a /tmp file to prevent runaway FS updates

* docs(#3088): CI runs make lintdoc to avoid muddying help-check with unnecessary build and possible Nvim lint failures

* docs(#3088): unify help and readme, clarify some wording

* docs(#3088): test a readme toc

* docs(#3088): test a readme toc

* docs(#3088): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute (#3260)

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute

* docs(#3241): unify help generation and lint into vimdoc.sh, generation uses nvim source build to execute
2026-02-12 11:10:36 +11:00
Alexander Courtis
037d89e60f docs(#3243): renderer.hidden_display help documentation is generated (#3258) 2026-01-31 15:31:37 +11:00
Alexander Courtis
c07ce43527 fix(#3198): add filesystem_watchers.max_events to handle runaway filesystem events on PowerShell (#3232)
* fix(#3198): add filesystem_watchers.max_outstanding_events which disables filesystem watchers on a directory when runaway events are received

* fix(#3198): document filesystem_watchers.max_events

* fix(#3198): document filesystem_watchers.max_events
2026-01-27 12:28:05 +11:00
Uanela Como
89d9621aa4 refactor(#3225): multi instance expand (#3234)
* wip(explorer): rename :expand to :expand_dir_node and all places using it

* wip: move expand function to directory

* refactor: move expand functionality to node methods

Move expansion logic from actions/tree/modifiers/expand.lua into
DirectoryNode and Explorer methods. Update API to use new methods
via wrap_explorer. Remove expand module and its setup calls.

* refactor(directory): correctly calling class methods and passing correct values

* fix: moving directory and file nodes require to avoid circular depency

* fix(node): correctly calling expand on parent without circular dependency

* fix(node.directory): fix <E> not expanding all folders

* refactor(node.director): move gen_iterator to expand and safely call descend_until_empty

* feat: move collapse to tree actions and remove modifiers folder

* feat: move should_expand to Node for better OO

* chore: add should_expand in DirectoryNode to avoid circular dependecy

* fix(explorer): correctly calling expand as a class method

* fix: adding underscore on unsed variables

* chore: move descend_until_empty to local function

* fix(directory): correctly pass node instead of self ot should_descend
2026-01-27 11:01:15 +11:00
xiantang
b93dbb9b58 refactor: use Node objects in Filters:bookmark for improved type safety (#3252)
* refactor: use Node objects in Filters:bookmark for improved type safety

* docs(filters): add clarifying comments to bookmark filter logic
2026-01-25 10:35:57 +11:00
Uanela Como
1701e41b07 refactor(#2988): multi-instance change-dir (#3233)
* chore: moving root action dir-up to explorer class

* chore(explorer): moving requires to top level

* wip(explorer): moved all change-dir functions to explorer class

* wip(explorer): rename M.current_tab to self.current_tab

* chore(explorer): move change_dir root action to explorer and replace all places using change_dir to use it from explorer

* refactor(explorer): changing _foldername to folder_name

* wip(explorer): rename expand method to expand_dir_node and all places using it

* wip(explorer): mark private methods and remove add_profiling_to method

* fix: allow changing root to file`s parent directory and fix restrict_above_cwd config path

* refactor(api): moving change_root_to_node to Explorer:change_dir_to_node

* fix(explorer): get new explorer before redrawing on <C-]>

* docs(explorer): adding link to PR discussion for more context

* fix(explorer): correctly check config.actions.change_dir for change_dir related methods

* fix: correctly pass explorer as first arg when calling explorer methods in lua/nvim-tree

* fix: correctly pass args to change_dir

* fix: check for explorer before force_dirchange in open_on_directory

* fix: check if force_dirchange should call core.init
2026-01-24 12:58:32 +11:00
xiantang
5757bcf044 fix(#3248): bookmark filter shows contents of marked directories (#3249)
fix: restore bookmark filter for marked directories
2026-01-23 16:25:05 +11:00
Alexander Courtis
869fc957ed docs(#2934): add nvim_tree.config* classes with generated help and new nvim-tree-icons-highlighting help section (#3235)
* doc(#2934): POC to use gen_vimdoc.lua for decorator meta

* doc(#2934): use injected gen_vimdoc.lua from nvim source, move decorators to main help

* doc(#2934): tidy and harden scripts

* doc(#2934): add nvim_tree.Config meta classes, add nvim_tree.api. meta classes

* doc(#2934): add nvim_tree.Config to help

* doc(#2934): add nvim_tree.api classes to help

* doc(#2934): tidy and correct nvim_tree.Config.UI and nvim_tree.Config.Actions

* doc(#2934): tidy and correct nvim_tree.Config.HijackDirectories, format and order nvim_tree.Config

* doc(#2934): tidy and nvim_tree.Config top level

* doc(#2934): tidy and format nvim_tree.Config.LiveFilter, Modified, Tab, Trash

* doc(#2934): tidy and format nvim_tree.Config.FilesystemWatchers, Log

* doc(#2934): split nvim_tree.Config into files, Sort and HijackDirectories populate others placeholder

* doc(#2934): more generic gen_vimdoc_config module config

* doc(#2934): tidy Config.HijackDirectories and Actions, move them into place

* doc(#2934): tidy Config.Modified, move into place

* doc(#2934): tidy Config.LiveFilter, move into place

* doc(#2934): tidy Config.FilesystemWatchers, move into place

* doc(#2934): tidy Config.Trash, move into place

* doc(#2934): tidy Config.Tab, move into place

* doc(#2934): tidy Config.UI, move into place

* doc(#2934): tidy Config.Log, move into place

* doc(#2934): tidy Config.Notify, move into place

* doc(#2934): tidy Config.SystemOpen, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Help, move into place

* doc(#2934): tidy Config.Diagnostics, move into place

* doc(#2934): remove api/decorator as they are out of scope, revert api_decorator.lua changes, retain api opts classes but make them exact

* doc(#2934): tidy Config.Filters, move into place

* doc(#2934): tidy Config.FilesystemWatchers, move into place

* doc(#2934): tidy Config.FilesystemWatchers, Git, move into place

* doc(#2934): tidy Config.UpdateFocusedFile, move into place

* doc(#2934): tidy Config.Sort, move into place

* doc(#2934): tidy Config.View, move into place

* doc(#2934): tidy Config.View, move into place

* docs(#2934): type DEFAULT_OPTS, use bracketed references to keep luals happy

* docs(#2934): tidy, inline some classes

* docs(#2934): tidy Config.Renderer

* docs(#2934): remove problematic glyphs, tidy other glyphs

* docs(#2934): tidy Config.Renderer, ensure functions are parenthesised when necessary

* docs(#2934): tidy Config.Renderer

* docs(#2934): tidy Config.Renderer, move into place

* docs(#2934): tidy Config.Renderer

* docs(#2934): tidy Config.FilesystemWatchers

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): fix broken links

* docs(#2934): refer to nvim in help, not neovim, as this trips the lintdoc spell checker

* docs(#2934): fix broken links

* docs(#2934): add lintdoc.sh, fix some branding spelling

* docs(#2934): update old config links

* docs(#2934): remove 5. Opts, 8.2  Highlight: Overhaul, 15.1  Index: Opts, update links as needed

* docs(#2934): remove indices

* docs(#2934): add bookmarks, put long descriptions into @brief

* docs(#2934): move hidden display to verbatim new section

* docs(#2934): add config lsp examples, link setup

* docs(#2934): normalise heading formatting, remove TOC

* docs(#2934): move all icon and highlight info into a table

* docs(#2934): move all icon and highlight info into a new section

* docs(#2934): normalise all icon and highlight

* docs(#2934): extract nvim_tree.Config field descriptions to class overview

* docs(#2934): consistent string formatting

* docs(#2934): normalise all icon and highlight

* docs(#2934): remove @briefs as they are shown above the class tag

* docs(#2934): don't <pre> filters

* docs(#2934): add experimental example

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): add gen_vimdoc.sh to CI

* docs(#2934): temporarily --ignore-blank-lines during help check

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): add lintdoc.sh to CI

* docs(#2934): move Nvim build and help-check to the end for faster turnaround

* docs(#2934): use make lintdoc target directly

* docs(#2934): CONTRIBUTING.md updates for help generation and lint

* docs(#2934): revert API opts classes changes

* docs(#2934): harden gen and lint scripts, moving things out of nvim-tree source to prevent luals upset

* docs(#2934): harden gen and lint scripts, moving things out of nvim-tree source to prevent luals upset

* docs(#2934): fix lintdoc make target

* docs(#2934): polish vimdoc config

* docs(#2934): snake case config class names

* docs(#2934): snake case config alias names

* docs(#2934): note help tag prefixes

* docs(#2934): snake case config sections

* docs(#2934): move default config to end

* docs(#2934): move setup doc from config to quickstart and setup

* docs(#2934): index contributing

* docs(#2934): tidy gitignore

* docs(#2934): tidy formatting

* docs(#2934): lint and check scripts, rewrite luals check script following upstream changes

* docs(#2934): script tidying

* docs(#2934): add a temporary codestyle violation to test scripts

* Revert "docs(#2934): add a temporary codestyle violation to test scripts"

This reverts commit fca4dbf70a.

* docs(#2934): remove class from config section headers
2026-01-22 12:23:55 +11:00
Ivan Krasilnikov
ca8d82fff2 feat: add default <Del> mapping for api.fs.remove (#3238)
feat: add <Del> mapping

Co-authored-by: Alexander Courtis <alex@courtis.org>
2026-01-14 17:44:38 +11:00
github-actions[bot]
a0db8bf7d6 chore(master): release nvim-tree 1.15.0 (#3195)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-11 15:10:53 +11:00
xiantang
c89215d6a1 feat(#1851): add bookmarks.persist, default path: vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json", disabled by default (#3033)
* Support Persist Bookmarks

* Fix node new open error

* Fix filter marks index issue

* Made saving/loading conditional on enable_persistence,

* add method comments

* Use a pcall to prevent an error being raised

* Print errmsg and disable persistence on a bad path

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

* fix: use public list() method instead of accessing private marks field

* refactor: rename marks config to bookmarks and simplify persistence API

- Rename opts.marks to opts.bookmarks for better clarity
- Simplify persist option to boolean | string (true uses default path)
- Implement lazy node resolution for loaded bookmarks
- Update documentation with new bookmarks configuration
- Improve bulk operations to use public list() method consistently

* refactor: rename marks config to bookmarks and simplify persistence API

- Rename opts.marks to opts.bookmarks for better clarity
- Simplify persist option to boolean | string (true uses default path)
- Implement lazy node resolution for loaded bookmarks
- Update documentation with new bookmarks configuration
- Improve bulk operations to use public list() method consistently

* add errmsg in return

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

* fix: correct indentation in filters.lua to match codestyle

* fix: add missing else block to handle bookmark save errors and use errmsg variable

* refactor: use string.format for bookmark warnings and update documentation

- Replace string concatenation with string.format in bookmark error messages
- Remove outdated comment about bookmark persistence from documentation

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2026-01-11 15:06:36 +11:00
Šimon Mandlík
e66994d40d fix: incorrect window width when right_align icons present (#3239)
fix: correctly assign extmarks to lines when computing tree window width in `grow` when `nvim-tree.view.width.lines_excluded` contains "root"
2026-01-11 14:56:51 +11:00
xvzc
b8b44b6a24 fix: api.tree.change_root_to_node on a file now changes directory to parent as per documentation (#3228)
fix: wrong behavior for api.tree.change_root_to_node

As described in the documentation, `api.tree.change_root_to_node` should
set the tree root to the parent directory when the target node is a file.

Currently, calling this function on a file node does nothing.
This commit adds logic to handle cases where the input node is a `FileNode`
in the `change_root_to_node` function.

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-12-20 11:59:20 +11:00
Uanela Como
b63d3679a8 refactor(#2988): multi instance dir_up (#3222)
* chore: moving root action dir-up to explorer class

* chore(explorer): moving requires to top level

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-12-20 11:36:04 +11:00
Eric Wong
8298117311 fix(#3226): set &swapfile=false before setting tree buffer name, avoiding any potential collisions with a swapfile (#3227)
fix: set buffer option before set_buf_name

close https://github.com/nvim-tree/nvim-tree.lua/issues/3226
2025-12-20 11:29:08 +11:00
Alexander Courtis
eb33612bff chore: EmmyLuaCodeStyle style fixes following version update (#3221) 2025-12-09 13:44:33 +11:00
Alexander Courtis
59088b96a3 chore: set runtime.version 5.1 in .luarc.json (#3220) 2025-12-03 09:09:19 +11:00
Šimon Mandlík
776a5cdfac feat(#3213): add view.width.lines_excluded option (#3214)
feat(#3213): add `view.width.lines_excluded` option
2025-12-01 08:29:34 +11:00
dependabot[bot]
3fb91e18a7 chore(deps): bump actions/checkout from 5 to 6 (#3219)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-21 10:11:05 +02:00
Šimon Mandlík
81ede55c47 fix: renderer.add_trailing applies to symlink destination (#3217)
feat: use `add_trailing` also for symlink destination
2025-11-21 16:20:45 +11:00
Tomasz N
1eda256939 feat: load command definitions at nvim startup (#3211)
feat: load command definitions in `plugin` directory
2025-11-11 09:09:02 +11:00
Tomasz N
68c67adfab perf(commands): defer module loading (#3210) 2025-11-09 15:05:36 +11:00
Eric Wong
64e2192f52 feat: set filter input filetype to NvimTreeFilter (#3207)
feat: add NvimTreeFilter filetype
2025-10-20 00:50:08 +00:00
phanium
e397756d2a fix: prevent NvimTree to be alternate buffer when tab open (#3205)
* fix: prevent NvimTree to be alternate buffer when tab open

* fix: prevent tabnew leave a dangling "[No Name]" buffer

* Update lua/nvim-tree/actions/node/open-file.lua

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

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-10-08 10:57:48 +11:00
dependabot[bot]
87d096a39c chore(deps): bump leafo/gh-actions-luarocks from 5 to 6 (#3201)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 10:52:12 +02:00
dependabot[bot]
01b2e8e5f7 chore(deps): bump leafo/gh-actions-lua from 11 to 12 (#3202)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 09:52:10 +02:00
Alexander Courtis
e179ad2f83 refactor(#2942): multi instance: move utils functions to Explorer methods (#3200)
* refactor(#2942): multi instance: move find_node_line to Explorer

* refactor(#2942): multi instance: move get_node_from_path to Explorer

* refactor(#2942): multi instance: move focus_file to Explorer

* refactor(#2942): multi instance: move focus_node_or_parent to Explorer

* refactor(#2942): multi instance: move get_node_from_path to Explorer

* refactor(#2942): multi instance: move find_node to Explorer

* refactor(#2942): multi instance: move get_nodes_by_line to Explorer

* refactor(#2942): multi instance: remove unnecessary focus_file

* refactor(#2942): style
2025-09-08 16:26:42 +10:00
Alexander Courtis
f92cc3a91c chore(#3196): remove unused utils and refactor (#3199)
* chore(#3196): remove unused utils.read_file

* chore(#3196): move utils.move_missing_val to legacy

* chore(#3196): move utils.table_create_missing to legacy
2025-09-08 11:12:31 +10:00
Igor Lacerda
fefa335f1c feat(#1826): add diagnostics.diagnostic_opts: vim.diagnostic.Opts will override diagnostics.severity and diagnostics.icons (#3190)
* feat(#1826): allow using config from vim.diagnostic for icons + severity

* update help default options, add index

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-08-25 13:22:05 +10:00
dependabot[bot]
b70a741f5a chore(deps): bump amannn/action-semantic-pull-request from 6.1.0 to 6.1.1 (#3193)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 13:01:24 +02:00
dependabot[bot]
f4fa6ebd3c chore(deps): bump amannn/action-semantic-pull-request from 6.0.1 to 6.1.0 (#3192)
chore(deps): bump amannn/action-semantic-pull-request

Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 6.0.1 to 6.1.0.
- [Release notes](https://github.com/amannn/action-semantic-pull-request/releases)
- [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amannn/action-semantic-pull-request/compare/v6.0.1...v6.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 13:44:53 +10:00
dependabot[bot]
f0e9951778 chore(deps): bump amannn/action-semantic-pull-request from 5.5.3 to 6.0.1 (#3189)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 14:31:09 +02:00
dependabot[bot]
feb901a09e chore(deps): bump actions/checkout from 4 to 5 (#3188)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 09:08:14 +02:00
github-actions[bot]
321bc61580 chore(master): release nvim-tree 1.14.0 (#3146)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-12 10:43:00 +10:00
Alexander Courtis
442513648c perf(#3171): cache toplevel for untracked (#3185) 2025-08-12 10:34:24 +10:00
Alexander Courtis
000ca6bcdd docs: CONTRIBUTING.md: add diagnostics and backward compatibility 2025-08-11 14:22:46 +10:00
Kasper Kondzielski
1b876db049 feat(#2789): add optional function expand_until to api.tree.expand_all and api.node.expand (#3166)
* feat: Allow to expand nodes until certain condition is met

* Fix warnings

* Restore original position of edit function

* Rename field to match the api method name

* Rename ApiTreeExpandAllOpts to ApiTreeExpandOpts

* Remove toggle_descend_until

* Remove redundant empty line

* Update :help for changed methods

* Fix partial expansion of grouped nodes

* Fix lint error

* Fix linting error

* Fix incorrect open/close indicator state

* Update docs

* Rename descend_until option to expand_until

* Always check directory expansion limit

* Fix linter errors

* Ignore unused param warning

* Apply suggestions from code review

* simplify MAX_FOLDER_DISCOVERY warning

* fix bad comment whitespace

---------

Co-authored-by: ghostbuster91 <ghostbuster91@users.noreply.github.com>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-08-11 13:50:26 +10:00
Julien Vincent
0a52012d61 feat(#2685): highlight git new tracked with NvimTreeGitFileNewHL (#3176)
Highlight new, tracked files in working copy

Currently nvim-tree doesn't highlight new, tracked files in the working
copy/directory.

This change assigns the `NvimTreeGitFileNewHL` highlight to the `" A"`
git porcelain status marker.

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-08-10 15:00:38 +10:00
Alexander Courtis
a4699c0904 revert(#3180, #3177): resolve live filter failures (#3183)
* Revert "fix(#3172): live filter exception (#3173)"

This reverts commit 0a7fcdf3f8.

* Revert "refactor(#2826): move view to instanced window class (#3153)"

This reverts commit 0a06f65bf0.

* feat(#3157): add view.cursorlineopt
2025-08-10 14:45:25 +10:00
Alexander Courtis
9b289abd69 revert(#3180, #3177): invalid group or tabpage (#3181)
* Revert "feat(#2826): allow only one window with nvim-tree buffer per tab (#3174)"

This reverts commit dd2364d680.

* Revert "refactor(#2826): View tracks winids and bufnrs via events, unused for now (#3170)"

This reverts commit 65bae44922.

* Revert "refactor(#2826): remove view debug/cc, enable new codepaths for get_winid and get_bufnr (#3169)"

This reverts commit a9156c0139.
2025-08-10 11:18:31 +10:00
Alexander Courtis
dd2364d680 feat(#2826): allow only one window with nvim-tree buffer per tab (#3174)
* feat(#2826): allow only one window with nvim-tree buffer per tab

* feat(#2826): remove globals.BUFNR_BY_TABID

* Revert "feat(#2826): remove globals.BUFNR_BY_TABID"

This reverts commit 2651f9b34a.

* feat(#2826): remove unused View.winid_by_tabid

* feat(#2826): add feature gate experimental.close_other_windows_in_tab
2025-08-05 15:29:25 +10:00
Tomasz N
9a05b9e9f9 perf(#3171): use vim.system() instead of vim.fn.system() to execute git toplevel (#3175)
* fix(#3171): use vim.system() to determine git toplevel

* Don't use vim.trim

* Ensure sdtout is a string

* Keep Nvim 0.9 compatibility

* Use vim.system to query git config for status.showUntrackedFiles too
2025-08-05 13:23:51 +10:00
Alexander Courtis
0a7fcdf3f8 fix(#3172): live filter exception (#3173) 2025-08-04 11:51:42 +10:00
Alexander Courtis
65bae44922 refactor(#2826): View tracks winids and bufnrs via events, unused for now (#3170)
* refactor(#2826): add View.bufnr and use for all public methods

* use View.bufnr for all internal methods

* revert View.bufnr, add buffer local autocommands

* View revert to globals.BUFNR_BY_TABID until bufnr and winid are tracked for the view

* View track winids and bufnrs via events
2025-07-29 14:19:24 +10:00
Alexander Courtis
a9156c0139 refactor(#2826): remove view debug/cc, enable new codepaths for get_winid and get_bufnr (#3169)
* refactor(#2826): fuller error messages

* refactor(#2826): winnr->winid in view/globals, remove redundant get_winid and get_bufnr calls

* refactor(#2826): winnr->winid consistently

* refactor(#2826): consistent use of buffer registry, tidy, add todos

* refactor(#2826): remove unnecessary view members float, hide_root_folder; use explorer opts

* refactor(#2826): remove unused view members centralize_selection and preserve_window_proportions

* refactor(#2826): remove unused view member height

* refactor(#2826): temporarily reuse BUFNR_PER_TAB in view constructor

* refactor(#2826): get_winid returns new after consistency check

* refactor(#2826): globals.TABPAGES -> WINID_PER_TAB

* refactor(#2826): consistent naming of tabid

* refactor(#2826): more consistency checking

* refactor(#2826): more consistency checking

* refactor(#2826): move global CURSORS to view member

* Revert "refactor(#2826): move global CURSORS to view member"

This reverts commit d84dfad1c3.

* refactor(#2826): move global CURSORS to view member

* refactor(#2826): consistency check returns new

* refactor(#2826): remove consistency checks, enabling new path for view get_winid and get_bufnr

* refactor(#2826): restore CURSORS global
2025-07-28 12:44:17 +10:00
alexfinger21
10db6943cb fix(#3077): deleting a directory containing symlinked directory will delete the contents of the linked directory (#3168)
* fix(#3077) deleting a directory containing symlink file will delete all content inside the symlink

* fix(#3077): add diagnostic override TODO

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-07-28 09:42:38 +10:00
Tomasz N
543ed3cac2 fix(picker): exclude full_name window id from the choice (#3165)
Problem: `full_name` window from is considered as usable by picker
Solution: exclude its ID (also true for nil values)

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-07-21 13:22:56 +10:00
Alexander Courtis
2a386fe567 ci: luals_version 3.13.9 -> 3.15.0 (#3167) 2025-07-21 13:22:07 +10:00
Alexander Courtis
b0b49552c9 docs: polish example decorator (#3160) 2025-06-21 10:57:48 +10:00
Alexander Courtis
8eb5e0bfd1 feat(#3157): add view.cursorlineopt (#3158)
fix(#3157): add view.cursorlineopt
2025-06-20 13:46:38 +10:00
Alexander Courtis
0a06f65bf0 refactor(#2826): move view to instanced window class (#3153)
* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class, WIP

* refactor(#2826): singleton View class

* refactor(#2826): View is an Explorer member

* refactor(#2826): move autocmds to Explorer

* refactor(#2826): API uses Explorer's View

* refactor(#2826): move View into Explorer package

* refactor(#2826): retain necessary view globals

* refactor(#2826): move all winhl to appearance constants

* refactor(#2826): add lifecycle logging to all Explorer members

* refactor(#2826): fix bad cherry-pick

* refactor(#2826): better enumerate_options function

* refactor(#2826): add View.tab_line for debugging

* refactor(#2826): default lifecycle log off

* refactor(#2826): add experimental.multi_instance_debug, split globals out of view, move diagnostics to its own module

* refactor(#2826): instrument View:get_winnr

* refactor(#2826): instrument View:setup_tabpage

* refactor(#2826): instrument View:set_current_win, View:prevent_buffer_override

* refactor(#2826): instrument View:get_bufnr

* refactor(#2826): track member bufnr -> winid with global

* refactor(#2826): tidy experiment names and logs

* vim: nvim-tree: track bufnr via buffer-update channel

* vim: nvim-tree: more logging

* vim: nvim-tree: revert: track bufnr via buffer-update channel

* refactor(#2826): notify error on view winid and bufnr mismatches

* refactor(#2826): notify error on view winid and bufnr mismatches

* refactor(#2826): explorer init logging
2025-06-19 15:45:55 +10:00
Yavorski
d54a1875a9 fix: invalid window id for popup info window (#3147) 2025-06-17 16:59:28 +10:00
Garry Filakhtov
aa087788d7 docs: fix renderer.icons.bookmarks_placement parameter, misspelling (#3150)
Fix minor documentation issues

Add a missing double quotes around the default value for
`nvim-tree.renderer.icons.bookmarks_placement` config value and fix
spelling of `bookmarked`.
2025-06-17 10:23:34 +10:00
phanium
d87b41ca53 fix: window picker ignore hidden window (#3145) 2025-06-15 15:04:47 +10:00
github-actions[bot]
6b5b366596 chore(master): release nvim-tree 1.13.0 (#3120)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-14 17:30:22 +10:00
Rami Elwan
ae595611fb feat(#3132): add api.node.expand and api.node.collapse (#3133)
* feat: allow passing node to collapse all

* refactor: use snake case

* feat: handle api legacy calls and update signature

* refactor: make sure open is a boolean

* doc: collapse_all

* Revert "doc: collapse_all"

This reverts commit d243da3e14.

* add api.node.collapse

* add api.node.expand

* add api.node.expand

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-06-14 17:26:58 +10:00
Lucas Mendes
05d8172ebf fix(#3143): actions.open_file.window_picker.exclude applies when not using window picker (#3144)
* fix(#3143): ensure open.no_window_picker respects window_picker.exclude

* fix(#3143): doc

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-06-14 15:35:07 +10:00
Lorentz Lasson
1c733e8c19 chore: use portable shebangs consistently (#3141)
use portable shebangs consistently
2025-06-02 09:21:07 +10:00
Šimon Mandlík
ebcaccda1c fix(#3134): setting one glyph to "" no longer disables others (#3136)
fix: fixes #3134
2025-05-26 13:32:21 +10:00
Šimon Mandlík
cbc3165e08 fix(#2746): background and right aligned icons in floating windows (#3128)
* fix(#2746): fix cursorcolumn and right aligned icons in floating windows

* feat: remove right aligned icons from full name float, show float over right aligned icons

* refactoring: move `extmarks_length` to utils.lua

* fix: decrease `win_width` instead of increasing `text_width` when computing condition for full name float to show

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-05-24 13:19:19 +10:00
Arthur Roos
bd54d1d33c fix(#3117): windows: change file/dir case (#3135)
fix(#3117): allow changing filename's casing

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-05-24 12:52:25 +10:00
Christoph
25d16aab7d fix: "Invalid buffer id" on closing nvim-tree window (#3129)
fix: invalid buffer issue

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-05-18 04:35:59 +00:00
Ross W
e4cd856ebf fix(#3124): fix icon padding for "right_align" placements, notably for dotfiles (#3125)
fix(#3124): prevent empty icons_right_align response from breaking padding
2025-05-18 12:26:18 +10:00
Alexander Courtis
e7d1b7dadc fix(#3122): remove redundant vim.validate (#3123) 2025-05-09 10:00:28 +10:00
Spencer Chunn
ea5097a1e2 feat(#3113): add renderer.icons.folder_arrow_padding (#3114)
* Update padding.lua

* add folder_arrow_padding

* update help docs

* refactor: renderer.icons.padding

renderer.icons.padding -> renderer.icons.padding.icon
renderer.icons.folder_arrow_padding ->
renderer.icons.padding.folder_arrow

* refactor: renderer.icons.padding

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-05-05 11:51:29 +10:00
Alexander Courtis
582ae48c9e chore: fix incorrect @param (#3115) 2025-04-26 12:55:36 +10:00
github-actions[bot]
be5b788f2d chore(master): release nvim-tree 1.12.0 (#3099)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-04-21 12:04:41 +10:00
Alexander Courtis
64bb47f868 ci: simplify luarocks release tag pattern as it was not firing 2025-04-21 08:59:56 +10:00
Devansh Sharma
c24c0470d9 feat: add TreePreOpen event (#3105)
* feat: Add `TreePreOpen` and `TreePreClose` events

* docs: Update docs for `TreePreOpen` and `TreePreClose` events

* chore: remove `TreePreClose` event and update dispatch of `TreePreOpen`

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-04-21 08:39:48 +10:00
Devansh Sharma
3a63717d3d fix: reliably dispatch exactly one TreeOpen and TreeClose events (#3107)
* fix: correctly handle `TreeOpen` and `TreeClose` event dispatch

* fix: lint issues
2025-04-20 09:49:28 +10:00
Alexander Courtis
5bea2b3752 fix(#3101): when renderer.highlight_opened_files = "none" do not reload on BufUnload and BufReadPost (#3102)
* fix(#3101): fix bad reference to renderer.highlight_opened_files during BufUnload and BufReadPost

* fix(#3101): only redraw renderer.highlight_opened_files during BufUnload and BufReadPost

* fix(#3101): only redraw renderer.highlight_opened_files during BufUnload and BufReadPost

* fix(#3101): only redraw renderer.highlight_opened_files during BufUnload and BufReadPost
2025-04-11 12:48:34 +10:00
Šimon Mandlík
c3c1935942 fix: explicitly set border to "none" in full name float (#3094) 2025-04-04 17:29:38 +11:00
Alexander Courtis
44d9b58f11 chore: use builtin EmmyLuaCodeStyle for style checking (#3084)
* chore: sync EmmyLuaCodeStyle settings between .editorconfig and .luarc.json

* chore: lua-language-server 3.11.0 -> 3.13.9

* chore: fix incorrect definition of vim.loop.fs_lstat

* chore: add codestyle-check option to luals-check.sh

* chore: use luals for style check

* chore: use luals for style check

* Revert "chore: use luals for style check"

This reverts commit e5fde80fab.

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check

* chore: use luals for style check
2025-03-23 12:46:17 +11:00
dependabot[bot]
c09ff35de5 chore(deps): bump leafo/gh-actions-lua from 10 to 11 (#3069)
Bumps [leafo/gh-actions-lua](https://github.com/leafo/gh-actions-lua) from 10 to 11.
- [Release notes](https://github.com/leafo/gh-actions-lua/releases)
- [Commits](https://github.com/leafo/gh-actions-lua/compare/v10...v11)

---
updated-dependencies:
- dependency-name: leafo/gh-actions-lua
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-01 11:07:35 +11:00
github-actions[bot]
6709463b2d chore(master): release nvim-tree 1.11.0 (#3051)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-22 12:31:19 +11:00
Šimon Mandlík
b69914325a fix: window picker: hide fillchars: stl and stlnc (#3066)
fix: stl and stlnc fillchars are hidden in window picker

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-02-22 11:32:52 +11:00
Gabriel Crispino
3281f331f7 feat(#1984): add quit_on_open and focus opts to various api.node.open functions (#3054)
* feat: add quit_on_open opt to api.node.open.edit

* fix: fix missing @param annotation

* feat: add focus opt to api.node.open.edit

* fix: fix focus == false behaviour on api.node.open.tab command

* fix: add optional tabpage integer parameter to view.close

if tabpage is not nil, then the function closes the tabpage in this
specific tabpage

* fix: fix quit_on_open == true behaviour on api.node.open.tab command

* fix: add check to not use new opts for certain edit modes

* fix: add docs for new opts

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-02-22 11:09:49 +11:00
Hendrik Ziegler
80523101f0 fix: arithmetic on nil value error on first git project open (#3064)
* fixed error message when opening new git repo

* defensive nil + type check

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-02-10 08:06:02 +11:00
Alexander Courtis
70825f23db fix(#3059): test for presence of new 0.11 API vim.hl.range (#3060) 2025-02-03 15:42:22 +11:00
Alexander Courtis
d05881f65f docs: tidy readme contributing 2025-01-27 11:33:37 +11:00
Gabriel Crispino
fee1da8897 feat(#3037): add API node.buffer.delete, node.buffer.wipe (#3040)
* feat(mappings): add key map to close file buffer

* feat: implement Api.node.buffer.delete

* feat: implement Api.node.buffer.wipe

* refactor: add util fn for common delete ops on bufs

* fix: minor fixes

* refactor: fix lint issues

* fix: undo unintended ApiTreeToggleOpts change

* fix: change error message level to info

* fix: remove unused opts

* refactor: merge delete-buffer and wipe-buffer into single buffer file

* refactor: make wipe and delete fns take a node instead of a file path

* docs: update help with new API commands

* remove refactored utils.lua

* remove unused static setup

* tweak doc

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-01-25 12:47:34 +11:00
Alexander Courtis
db7403243d chore: resolve deprecated in 0.11 (#3053)
* chore: resolve deprecated in 0.11

* chore: resolve deprecated in 0.11

* chore: resolve deprecated in 0.11

* chore: resolve deprecated in 0.11

* chore: resolve deprecated in 0.11

* chore: resolve deprecated in 0.11
2025-01-24 11:57:18 +11:00
𝐍𝐆𝐏𝐎𝐍𝐆
fca0b67c0b fix(#3045): wipe scratch buffers for full name and show info popups (#3050) 2025-01-18 10:28:06 +11:00
github-actions[bot]
d529a99f88 chore(master): release nvim-tree 1.10.0 (#3021)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-13 15:41:35 +11:00
phanium
39bc630816 fix: hijack directory "BufEnter", "BufNewFile" events are nested (#3044)
Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-01-13 15:39:10 +11:00
Lev Yuvenskiy
aae01853dd fix(#3041): use vim.diagnostic.get for updating diagnostics (#3042)
* fix(#3041): use vim.diagnostic.get for updating diagnostics

* fix(#3041): remove unnecessary @type

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2025-01-13 15:15:48 +11:00
fdgdgerg
68fc4c20f5 feat(api): add node.open.vertical_no_picker, node.open.horizontal_no_picker (#3031)
* test

* add splits with no window pickers

removed the 1 buffer per file limitation

test

test2

* no-picker for splits

* help vertical/horizontal_no_picker

* revert whitespace changes

---------

Co-authored-by: JoeDaBu <joegbu@gmail.com>
Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-12-22 09:35:48 +11:00
Šimon Mandlík
f7b76cd1a7 fix(#3015): dynamic width no longer truncates on right_align icons (#3022) 2024-12-14 08:54:18 +11:00
Alexander Courtis
c3d9b1779f docs: notify users with a fs.inotify.max_user_watches message on EMFILE event (#3028)
* docs: notify users with a fs.inotify.max_user_watches message on EMFILE event

* type safety for newly created vim.v.event type
2024-12-13 10:39:46 +11:00
ShyRobin
db8d7ac1f5 fix(#3018): error when focusing nvim-tree when in terminal mode (#3019)
fix: Can't re-enter normal mode from terminal mode

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-12-08 12:05:33 +11:00
Jie Liu
6b4be1dc0c fix: view.width functions may return strings (#3020)
* Fix get_size() function when size is a function return string

* update view.width help

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-12-08 11:45:32 +11:00
github-actions[bot]
375e38673b chore(master): release nvim-tree 1.9.0 (#2999)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-07 16:04:32 +11:00
Alexander Courtis
7a4ff1a516 feat(#2948): add custom decorators, :help nvim-tree-decorators (#2996)
* feat(#2948): add UserDecorator, proof of concept

* feat(#2948): add UserDecorator, proof of concept

* feat(#2948): add UserDecorator, proof of concept

* feat(#2948): add UserDecorator

* feat(#2948): add UserDecorator

* feat(#2948): add UserDecorator

* feat(#2948): add Decorator node icon override

* feat(#2948): add nvim_tree.api.* node classes

* feat(#2948): extract _meta following nvim pattern

* feat(#2948): extract _meta following nvim pattern

* feat(#2948): add decorator registry and order

* feat(#2948): add decorator registry and order

* feat(#2948): tidy

* feat(#2948): document API

* feat(#2948): document API

* feat(#2948): document API

* feat(#2948): pass api nodes to user decorators

* feat(#2948): document API

* feat(#2948): use renderer.decorators to define order and register

* feat(#2948): tidy decorator args and complete documentation

* feat(#2948): decorator classes specified by prefix rather than suffix

* feat(#2948): improve doc

* feat(#2948): improve doc

* feat(#2948): improve doc

* feat(#2948): additional user decorator safety

* feat(#2948): create nvim_tree.api.decorator.UserDecorator class in API, add :extend

* feat(#2948): improve doc
2024-12-07 16:03:29 +11:00
Alexander Courtis
ca7c4c33ca fix(#3009): nvim < 0.10 apply view options locally (#3010) 2024-11-24 17:00:58 +11:00
Alexander Courtis
1f3ffd6af1 fix(#2954): more efficient LSP updates, increase diagnostics.debounce_delay from 50ms to 500ms (#3007)
* fix(#2954): use LSP diagnostic data deltas from events instead of a full query

* fix(#2954): use LSP diagnostic data deltas from events instead of a full query
2024-11-22 10:12:47 +11:00
devxpain
f7c65e11d6 fix(api): correct argument types in wrap_node and wrap_node_or_nil (#3006)
The `wrap_node` and `wrap_node_or_nil` functions now correctly accept `Node?` to handle nil values, resolving a warning about incorrect argument counts in `api.tree.change_root_to_node()`.
2024-11-18 10:00:19 +11:00
des-b
28eac2801b fix(#2990): Do not check if buffer is buflisted in diagnostics.update() (#2998)
This ensures that LSP diagnostics of files which are not manually opened
by users are rendered by nvim-tree diagnostic indicators.

However when users attach an LSP to nvim-tree this will bring back
flashing as attempted to fix in #2980. Fixing this should probably done
by checking data passed via diagnostic events (DiagnosticChanged and
CocDiagnosticChanged).

Signed-off-by: des-b <66919647+des-b@users.noreply.github.com>
2024-11-11 08:57:06 +11:00
github-actions[bot]
c7639482a1 chore(master): release nvim-tree 1.8.0 (#2943)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-09 14:46:26 +11:00
Alexander Courtis
2ee1c5e17f feat(#2819): add actions.open_file.relative_path, default enabled, following successful experiment (#2995) 2024-11-09 14:44:59 +11:00
Alexander Courtis
3fc8de198c chore: migrate to classic (#2991)
* add classic, migrating nodes classes

* add mixins to classic

* typechecked optargs constructors for nodes

* typechecked optargs constructors for watcher and event

* luacheck

* typechecked optargs constructors for GitRunner

* typechecked optargs constructors for Sorter

* typechecked optargs constructors for decorators, WIP

* typechecked optargs constructors for decorators, WIP

* typechecked optargs constructors for decorators

* remove class

* replace enums with named maps

* Renderer and Builder use classic, tidy opts

* LiveFilter uses classic, tidy opts

* Filter uses classic, tidy opts

* add FilterTypes named map

* move toggles into filters

* Marks uses classic, tidy opts

* Sorter uses classic, tidy opts

* Clipboard uses classic, tidy opts

* use supers for node methods

* HighlightDisplay uses classic

* protected :new

* Watcher tidy

* Revert "use supers for node methods"

This reverts commit 9fc7a866ec.

* Watcher tidy

* format

* format

* Filters private methods

* format

* Sorter type safety

* Sorter type safety

* Sorter type safety

* Sorter type safety

* Sorter type safety

* Sorter type safety

* tidy Runner

* tidy hi-test name
2024-11-09 14:14:04 +11:00
Alexander Courtis
610a1c189b chore: resolve undefined-field warnings, fix link git statuses, rewrite devicons (#2968)
* add todo

* refactor(#2886): multi instance: node class refactoring: extract links, *_git_status (#2944)

* extract DirectoryLinkNode and FileLinkNode, move Node methods to children

* temporarily move DirectoryNode methods into BaseNode for easier reviewing

* move mostly unchanged DirectoryNode methods back to BaseNode

* tidy

* git.git_status_file takes an array

* update git status of links

* luacheck hack

* safer git_status_dir

* refactor(#2886): multi instance: node class refactoring: DirectoryNode:expand_or_collapse (#2957)

move expand_or_collapse to DirectoryNode

* refactor(#2886): multi instance: node group functions refactoring (#2959)

* move last_group_node to DirectoryNode

* move add BaseNode:as and more doc

* revert parameter name changes

* revert parameter name changes

* add Class

* move group methods into DN

* tidy group methods

* tidy group methods

* tidy group methods

* tidy group methods

* parent is DirectoryNode

* tidy expand all

* BaseNode -> Node

* move watcher to DirectoryNode

* last_group_node is DirectoryNode only

* simplify create-file

* simplify parent

* simplify collapse-all

* simplify live-filter

* style

* move lib.get_cursor_position to Explorer

* move lib.get_node_at_cursor to Explorer

* move lib.get_nodes to Explorer

* move place_cursor_on_node to Explorer

* resolve resource leak in purge_all_state

* move many autocommands into Explorer

* post merge tidy

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* Revert "chore: resolve undefined-field"

This reverts commit be546ff18d41f28466b065c857e1e041659bd2c8.

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* Revert "chore: resolve undefined-field"

This reverts commit e82db1c44d.

* chore: resolve undefined-field

* chore: class new is now generic

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* Revert "chore: resolve undefined-field"

This reverts commit 0e9b844d22.

* move icon builders into node classes

* move icon builders into node classes

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* chore: resolve undefined-field

* move folder specifics from icons to Directory

* move folder specifics from icons to Directory

* move folder specifics from icons to Directory

* move folder specifics from icons to Directory

* move file specifics from icons to File

* clean up sorters

* chore: resolve undefined-field

* tidy hl icon name

* file devicon uses library to fall back

* file devicon uses library to fall back

* file devicon uses library to fall back
2024-11-03 14:06:12 +11:00
Jie Liu
c22124b374 fix(#2981): windows: root changed when navigating with LSP (#2982)
* fix #2981: nvim-tree root changed when navigating with LSP

* add error checks
2024-11-03 13:52:20 +11:00
Alexander Courtis
2156bc08c9 fix: symlink file icons rendered when renderer.icons.show.file = false, folder.symlink* was incorrectly rendered as folder.default|open (#2983)
* fix: folder.symlink* was incorrectly rendered as folder.default|open

* fix: symlink file icons rendered when renderer.icons.show.file = false
2024-11-03 12:10:00 +11:00
des-b
82ab19ebf7 fix(#2954): resolve occasional tree flashing on diagnostics, set tree buffer options in deterministic order (#2980)
* fix(#2954): set buffer options in deterministic order

This ensures related autocmd's (e.g. on FileType) will be called in a
similar environment.

* fix(#2954): redraw only for diagnostics if source buffer is 'buflisted'

is_buf_valid has been inlined since it is only used for diagnostics
and its name is misleading.
2024-11-02 12:07:42 +11:00
Alexander Courtis
120ba58254 fix(#2978): grouped folder not showing closed icon (#2979) 2024-10-29 11:07:48 +11:00
Alexander Courtis
00dff482f9 fix(#2976): use vim.loop to preserve neovim 0.9 compatibility (#2977) 2024-10-29 08:01:52 +11:00
Alexander Courtis
8f974879a0 chore: luals runtime.version only set during check, to prevent lua version ambuguity at dev time (#2975)
* chore: luals runtime.version only set during check, to prevent lua version ambuguity at dev time

* inject lua 5.1 check failure

* Revert "inject lua 5.1 check failure"

This reverts commit eed966dc7b.
2024-10-28 11:57:53 +11:00
cpp_programmer
14039337a5 fix(#2969): After a rename, the node loses selection (#2974)
Co-authored-by: Lucian Ion <lucian.ion.2005@gmail.com>
2024-10-27 17:26:47 +11:00
Alexander Courtis
e4bc05b415 doc: remove outdated warning from actions.change_dir.global 2024-10-27 10:48:17 +11:00
Alexander Courtis
3cddd28177 doc: add windows specifics to CONTRIBUTING 2024-10-27 10:32:41 +11:00
Alexander Courtis
6e5a204ca6 fix(#2972): error on :colorscheme (#2973) 2024-10-27 10:18:21 +11:00
Alexander Courtis
f3efc25e56 refactor(#2941): move lib methods to explorer (#2964)
* add todo

* refactor(#2886): multi instance: node class refactoring: extract links, *_git_status (#2944)

* extract DirectoryLinkNode and FileLinkNode, move Node methods to children

* temporarily move DirectoryNode methods into BaseNode for easier reviewing

* move mostly unchanged DirectoryNode methods back to BaseNode

* tidy

* git.git_status_file takes an array

* update git status of links

* luacheck hack

* safer git_status_dir

* refactor(#2886): multi instance: node class refactoring: DirectoryNode:expand_or_collapse (#2957)

move expand_or_collapse to DirectoryNode

* refactor(#2886): multi instance: node group functions refactoring (#2959)

* move last_group_node to DirectoryNode

* move add BaseNode:as and more doc

* revert parameter name changes

* revert parameter name changes

* add Class

* move group methods into DN

* tidy group methods

* tidy group methods

* tidy group methods

* tidy group methods

* parent is DirectoryNode

* tidy expand all

* BaseNode -> Node

* move watcher to DirectoryNode

* last_group_node is DirectoryNode only

* simplify create-file

* simplify parent

* simplify collapse-all

* simplify live-filter

* style

* move lib.get_cursor_position to Explorer

* move lib.get_node_at_cursor to Explorer

* move lib.get_nodes to Explorer

* move place_cursor_on_node to Explorer

* resolve resource leak in purge_all_state

* move many autocommands into Explorer

* post merge tidy
2024-10-27 09:03:26 +11:00
Alexander Courtis
8760d76c1d chore: enable missing-local-export-doc 2024-10-25 14:35:48 +11:00
Alexander Courtis
077af9f990 chore: enable incomplete-signature-doc, format nvt-min.lua, assorted formatting tidies (#2967)
* chore: luacheckrc uses table

* chore: format nvt-min.lua

* chore: complete lua doc

* chore: complete lua doc

* chore: complete lua doc

* chore: complete lua doc

* chore: complete lua doc

* chore: enable incomplete-signature-doc

* chore: enable incomplete-signature-doc

* chore: complete lua doc

* chore: complete lua doc
2024-10-25 14:25:30 +11:00
Alexander Courtis
68be6df2fc refactor(#2886): multi instance: node class refactoring (#2950)
* add todo

* refactor(#2886): multi instance: node class refactoring: extract links, *_git_status (#2944)

* extract DirectoryLinkNode and FileLinkNode, move Node methods to children

* temporarily move DirectoryNode methods into BaseNode for easier reviewing

* move mostly unchanged DirectoryNode methods back to BaseNode

* tidy

* git.git_status_file takes an array

* update git status of links

* luacheck hack

* safer git_status_dir

* refactor(#2886): multi instance: node class refactoring: DirectoryNode:expand_or_collapse (#2957)

move expand_or_collapse to DirectoryNode

* refactor(#2886): multi instance: node group functions refactoring (#2959)

* move last_group_node to DirectoryNode

* move add BaseNode:as and more doc

* revert parameter name changes

* revert parameter name changes

* add Class

* move group methods into DN

* tidy group methods

* tidy group methods

* tidy group methods

* tidy group methods

* parent is DirectoryNode

* tidy expand all

* BaseNode -> Node

* move watcher to DirectoryNode

* last_group_node is DirectoryNode only

* simplify create-file

* simplify parent

* simplify collapse-all

* simplify live-filter

* style

* more type safety
2024-10-25 12:24:59 +11:00
Jie Liu
63c7ad9037 fix(#2961): windows: escape brackets and parentheses when opening file (#2962)
* Revert "fix(#2862): windows path replaces backslashes with forward slashes (#2903)"

This reverts commit 45a93d9979.

* fix the case when '()' and '[]' are both in file path

* remove debug messages

* remove unnecessary comments

* add is_windows feature flag when normalizing path

* add is_windows flag for filename change

* Revert "add is_windows flag for filename change"

This reverts commit ada77cb7e9.

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-10-25 11:11:21 +11:00
Alexander Courtis
9b82ff9bba chore: fix lib prompt doc for neovim nightly (#2966) 2024-10-25 11:10:07 +11:00
Alexander Courtis
2a268f631d doc: help: syntax highlighting for lua and vimscript 2024-10-18 18:29:19 +11:00
Alexander Courtis
f5f6789299 fix(#2947): root is never a dotfile, so that it doesn't propagate to children (#2958) 2024-10-14 18:56:43 +11:00
Alexander Courtis
ce09bfb95f chore: TODO issue links 2024-10-14 10:47:41 +11:00
Alexander Courtis
0fede9f813 chore: nvt-min.lua: ensure only one instance of lua-language-server runs (#2956) 2024-10-14 10:24:15 +11:00
Alexander Courtis
1c9553a19f fix(#2951): highlights incorrect following cancelled pick (#2952) 2024-10-12 15:54:12 +11:00
Alexander Courtis
ca0904e4c5 chore: add utils.enumerate_options (#2953) 2024-10-12 15:53:23 +11:00
Alexander Courtis
5ad87620ec fix(#2945): stack overflow on api.git.reload or fugitive event with watchers disabled (#2949)
* Reapply "refactor(#2871, #2886): multi instance: node classes created (#2916)"

This reverts commit 50e919426a.

* fix(#2945): stack overflow on api.git.reload or fugitive event
2024-10-11 13:47:01 +11:00
Alexander Courtis
50e919426a Revert "refactor(#2871, #2886): multi instance: node classes created (#2916)"
This reverts commit 38aac09151.
2024-10-08 18:07:47 +11:00
Alexander Courtis
010ae0365a feat(#2938): add default filesystem_watchers.ignore_dirs = { "/.ccls-cache", "/build", "/node_modules", "/target", } (#2940)
* feat(#2938): filesystem_watchers.ignore_dirs defaults to { node_modules } to resolve pathalogical issues

* feat(#2938): more filesystem_watchers.ignore_dirs defaults to to resolve pathalogical issues

* feat(#2938): more filesystem_watchers.ignore_dirs defaults to to resolve pathalogical issues
2024-10-07 15:25:24 +11:00
Alexander Courtis
38aac09151 refactor(#2871, #2886): multi instance: node classes created (#2916)
* refactor(#2875): multi instance renderer

* refactor(#2875): multi instance renderer

* refactor(#2875): multi instance renderer

* refactor(#2875): multi instance renderer

* node classes and constructors

* node methods

* refactor(#2875): multi instance renderer

* node classes and constructors

* explorer is a directory node

* extract methods from explore_node

* extract methods from explore_node

* extract methods from explore_node

* extract methods from lib

* use .. name for root node for compatibility

* use node.explorer

* extract node factory, remove unused code

* factories for all nodes, add RootNode

* factories for all nodes, add RootNode

* use factory pattern for decorators

* note regression and commit

* fix dir git status regression

* destroy nodes, not explorer

* add BaseNode:is

* revert changes to create-file, handle in #2924

* extract methods from explorer

* extract methods from explorer

* extract methods from explorer

* use Node everywhere in luadoc

* extract methods from lib

* extract methods from lib

* lint

* remove unused code

* don't call methods on fake root node

* get_node_at_cursor returns explorer (root) node instead of { name = '..' }

* remove unused inject_node

* refactor(#2875): multi instance renderer

* refactor(#2875): multi instance renderer

* refactor(#2875): multi instance renderer

* extract methods from lib

* node factory uses stat only

* temporary DirectoryNode casting until method extraction into child classes

* lua-language-server 3.10.5 -> 3.11.0

* explicitly call Explorer constructor

* normalise explorer RootNode new call, tidy annotations
2024-10-07 13:46:56 +11:00
Alexander Courtis
c9104a5d07 chore: style: align_continuous_similar_call_args (#2937)
* chore: style: align_continuous_similar_call_args

* chore: style: align_continuous_similar_call_args

* chore: style: align_continuous_similar_call_args

* chore: style: align_continuous_similar_call_args

* chore: style: consistent use of double quotes
2024-09-30 15:34:01 +10:00
github-actions[bot]
4a9e82d10a chore(master): release nvim-tree 1.7.1 (#2921)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-09-30 11:26:35 +10:00
Alexander Courtis
4520c0355c fix(#2930): empty groups expanded on reload (#2935) 2024-09-30 11:21:27 +10:00
Alexander Courtis
1ae1c33ce1 chore(#2931): stylua -> EmmyLuaCodeStyle (#2932)
* stylua -> EmmyLuaCodeStyle: config and doc

* stylua -> EmmyLuaCodeStyle: CI

* stylua -> EmmyLuaCodeStyle: CI

* stylua -> EmmyLuaCodeStyle: CI

* stylua -> EmmyLuaCodeStyle: CI

* stylua -> EmmyLuaCodeStyle: CI

* stylua -> EmmyLuaCodeStyle

* stylua -> EmmyLuaCodeStyle: call_arg_parentheses = always

* stylua -> EmmyLuaCodeStyle

* stylua -> EmmyLuaCodeStyle
2024-09-29 14:05:52 +10:00
Eric 李
9650e735ba fix(#2794): sshfs compatibility (#2922)
* Revert "revert(#2794): sshfs compatibility (#2920)"

This reverts commit 8405ecfbd6.

Fix for symlinks is simple

* fix sshfs compatibility with symlinks

* add suggestions

* revert variable name change to ease multi-instance feature branch conflicts

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-09-29 09:21:02 +10:00
Lin Tinusgrag
59a8a6ae5e fix: invalid explorer on open (#2927)
* fix: use of possibly stale value

The return value of `core.get_explorer()` could be changed by `core.init(..)`.

* fix style

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-09-25 19:27:25 +10:00
Lucas Reyna Córdoba
0429f286b3 fix(#2928): nil explorer in parent move action (#2929) 2024-09-25 19:23:57 +10:00
Alexander Courtis
8405ecfbd6 revert(#2794): sshfs compatibility (#2920)
* refactor(#2875): multi instance renderer: remove unused code

* Revert "fix(#2794): sshfs compatibility (#2893)"

This reverts commit 2d6e64dd8c.
2024-09-22 15:23:42 +10:00
Alexander Courtis
0ae9ad4ded refactor(#2875): multi instance renderer: remove unused code (#2919) 2024-09-22 15:22:06 +10:00
159 changed files with 13950 additions and 8635 deletions

View File

@@ -4,9 +4,21 @@ root = true
insert_final_newline = true
end_of_line = lf
[*.lua]
indent_style = space
indent_size = 2
[nvim-tree-lua.txt]
max_line_length = 78
# keep these in sync with .luarc.json
# .editorconfig is used within nvim, overriding .luarc.json
# .luarc.json is used by style check
[*.lua]
indent_style = space
max_line_length = 140
indent_size = 2
# EmmyLuaCodeStyle specific, see
# https://github.com/CppCXY/EmmyLuaCodeStyle/blob/master/lua.template.editorconfig
continuation_indent = 2
quote_style = double
call_arg_parentheses = always
space_before_closure_open_parenthesis = false
align_continuous_similar_call_args = true

View File

@@ -1,12 +1,12 @@
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
vim.cmd [[set runtimepath=$VIMRUNTIME]]
vim.cmd [[set packpath=/tmp/nvt-min/site]]
vim.cmd([[set runtimepath=$VIMRUNTIME]])
vim.cmd([[set packpath=/tmp/nvt-min/site]])
local package_root = "/tmp/nvt-min/site/pack"
local install_path = package_root .. "/packer/start/packer.nvim"
local function load_plugins()
require("packer").startup {
require("packer").startup({
{
"wbthomason/packer.nvim",
"nvim-tree/nvim-tree.lua",
@@ -18,21 +18,21 @@ local function load_plugins()
compile_path = install_path .. "/plugin/packer_compiled.lua",
display = { non_interactive = true },
},
}
})
end
if vim.fn.isdirectory(install_path) == 0 then
print "Installing nvim-tree and dependencies."
vim.fn.system { "git", "clone", "--depth=1", "https://github.com/wbthomason/packer.nvim", install_path }
print("Installing nvim-tree and dependencies.")
vim.fn.system({ "git", "clone", "--depth=1", "https://github.com/wbthomason/packer.nvim", install_path })
end
load_plugins()
require("packer").sync()
vim.cmd [[autocmd User PackerComplete ++once echo "Ready!" | lua setup()]]
vim.cmd([[autocmd User PackerComplete ++once echo "Ready!" | lua setup()]])
vim.opt.termguicolors = true
vim.opt.cursorline = true
-- MODIFY NVIM-TREE SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
_G.setup = function()
require("nvim-tree").setup {}
require("nvim-tree").setup({})
end
-- UNCOMMENT this block for diagnostics issues, substituting pattern and cmd as appropriate.
@@ -41,7 +41,11 @@ end
vim.api.nvim_create_autocmd("FileType", {
pattern = "lua",
callback = function()
vim.lsp.start { cmd = { "lua-language-server" } }
vim.lsp.start {
name = "my-luals",
cmd = { "lua-language-server" },
root_dir = vim.loop.cwd(),
}
end,
})
]]

View File

@@ -5,6 +5,8 @@ on:
push:
branches: [master]
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # weekly on sundays
permissions:
contents: read
@@ -20,73 +22,79 @@ jobs:
strategy:
matrix:
lua_version: [ 5.1 ]
luacheck_version: [ 1.2.0 ]
steps:
- uses: actions/checkout@v4
- name: checkout
uses: actions/checkout@v7
- uses: leafo/gh-actions-lua@v10
- name: install lua ${{ matrix.lua_version }}
uses: leafo/gh-actions-lua@v13
with:
luaVersion: ${{ matrix.lua_version }}
- uses: leafo/gh-actions-luarocks@v4
- name: install luarocks
uses: leafo/gh-actions-luarocks@v6
- run: luarocks install luacheck 1.1.1
- name: install luacheck ${{ matrix.luacheck_version }}
run: luarocks install luacheck ${{ matrix.luacheck_version }}
- run: make lint
style:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ matrix.stylua_version }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
strategy:
matrix:
stylua_version: [ 0.19.1 ]
steps:
- uses: actions/checkout@v4
- name: stylua
uses: JohnnyMorganz/stylua-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: ${{ matrix.stylua_version }}
args: --check lua
- run: make style-doc
check:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ matrix.nvim_version }}-${{ matrix.luals_version }}-${{ github.head_ref || github.ref_name }}
group: ${{ github.workflow }}-${{ matrix.nvim_version }}-${{ matrix.luals_version }}-${{ matrix.emmyluacodestyle_version }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
strategy:
matrix:
nvim_version: [ stable, nightly ]
luals_version: [ 3.10.5 ]
luals_version: [ 3.15.0 ]
emmyluacodestyle_version: [ 1.6.0 ]
env:
VIMRUNTIME: /home/runner/nvim-${{ matrix.nvim_version }}/share/nvim/runtime
DIR_NVIM_SRC: /home/runner/src/neovim-${{ matrix.nvim_version }}
steps:
- uses: actions/checkout@v4
- name: checkout
uses: actions/checkout@v7
- uses: rhysd/action-setup-vim@v1
- name: install nvim ${{ matrix.nvim_version }}
uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.nvim_version }}
- name: install luals
- name: install lua-language-server ${{ matrix.luals_version }}
run: |
mkdir -p luals
curl -L "https://github.com/LuaLS/lua-language-server/releases/download/${{ matrix.luals_version }}/lua-language-server-${{ matrix.luals_version }}-linux-x64.tar.gz" | tar zx --directory luals
echo "luals/bin" >> "$GITHUB_PATH"
- run: echo "luals/bin" >> "$GITHUB_PATH"
- name: install EmmyLuaCodeStyle ${{ matrix.emmyluacodestyle_version }}
run: |
mkdir -p EmmyLuaCodeStyle
curl -L "https://github.com/CppCXY/EmmyLuaCodeStyle/releases/download/${{ matrix.emmyluacodestyle_version }}/linux-x64.tar.gz" | tar zx --directory EmmyLuaCodeStyle
echo "EmmyLuaCodeStyle/linux-x64/bin" >> "$GITHUB_PATH"
- name: make check
env:
VIMRUNTIME: /home/runner/nvim-${{ matrix.nvim_version }}/share/nvim/runtime
run: make check
- run: make check
- run: make style
- run: make style-doc
- run: make format-check
- name: build Nvim from source
if: ${{ matrix.nvim_version == 'stable' }}
run: |
mkdir -p "${DIR_NVIM_SRC}"
curl -L "https://github.com/neovim/neovim/archive/refs/tags/${{ matrix.nvim_version }}.tar.gz" | tar zx --directory "${DIR_NVIM_SRC}/.."
cd "${DIR_NVIM_SRC}"
make doc
- run: make help-check
if: ${{ matrix.nvim_version == 'stable' }}

View File

@@ -1,14 +1,16 @@
name: Luarocks Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- v*
workflow_dispatch:
jobs:
luarocks-upload:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: LuaRocks Upload
uses: nvim-neorocks/luarocks-tag-release@v7
env:

View File

@@ -14,15 +14,15 @@ jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v4
- uses: googleapis/release-please-action@v5
id: release
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: tag major and minor versions
if: ${{ steps.release.outputs.release_created }}
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git"
git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/googleapis/release-please-action.git"
git tag -d v${{ steps.release.outputs.major }} || true
git tag -d v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }} || true
git tag -d v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} || true

View File

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

View File

@@ -1,3 +1,3 @@
#!/bin/sh
#!/usr/bin/env sh
make

View File

@@ -1,14 +1,15 @@
-- vim: ft=lua tw=80
local M = {}
-- Don't report unused self arguments of methods.
self = false
M.self = false
ignore = {
M.ignore = {
"631", -- max_line_length
}
-- Global objects defined by the C code
globals = {
M.globals = {
"vim",
"TreeExplorer"
}
return M

View File

@@ -7,6 +7,18 @@
"${3rd}/luv/library"
]
},
"format": {
"defaultConfig": {
"indent_style": "space",
"max_line_length": "140",
"indent_size": "2",
"continuation_indent": "2",
"quote_style": "double",
"call_arg_parentheses": "always",
"space_before_closure_open_parenthesis": "false",
"align_continuous_similar_call_args": "true"
}
},
"diagnostics": {
"libraryFiles": "Disable",
"globals": [],
@@ -33,13 +45,13 @@
"empty-block": "Any",
"global-element": "Any",
"global-in-nil-env": "Any",
"incomplete-signature-doc": "None",
"incomplete-signature-doc": "Any",
"inject-field": "Any",
"invisible": "Any",
"lowercase-global": "Any",
"missing-fields": "Any",
"missing-global-doc": "Any",
"missing-local-export-doc": "None",
"missing-local-export-doc": "Any",
"missing-parameter": "Any",
"missing-return": "Any",
"missing-return-value": "Any",

View File

@@ -1,3 +1,3 @@
{
".": "1.7.0"
".": "1.18.0"
}

View File

@@ -1,6 +0,0 @@
column_width = 140
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses = "None"

View File

@@ -1,5 +1,260 @@
# Changelog
## [1.18.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.17.0...nvim-tree-v1.18.0) (2026-07-01)
### Features
* **#1549:** p will paste files from another nvim-tree instance, gp will cut/paste ([#3336](https://github.com/nvim-tree/nvim-tree.lua/issues/3336)) ([c760c92](https://github.com/nvim-tree/nvim-tree.lua/commit/c760c92667fe5febf99e2a5bb8a543b0208c743b))
### Bug Fixes
* **#3312:** correct cursor when full name shown, requires nvim 0.12.2+ ([#3316](https://github.com/nvim-tree/nvim-tree.lua/issues/3316)) ([d277467](https://github.com/nvim-tree/nvim-tree.lua/commit/d277467fc0d1d0e2bca88165a1de6b526f9f6fe8))
* **#3324:** deprecated BufModifiedSet event replaced by OptionSet event for Nvim 0.13+ ([#3325](https://github.com/nvim-tree/nvim-tree.lua/issues/3325)) ([f9bfc00](https://github.com/nvim-tree/nvim-tree.lua/commit/f9bfc0059eca24546b69a7006110463da4808f8f))
* **#3327:** pass nvim_tree.api.node.open.Opts to all applicable nvim_tree.api.node.open function ([#3329](https://github.com/nvim-tree/nvim-tree.lua/issues/3329)) ([07f541f](https://github.com/nvim-tree/nvim-tree.lua/commit/07f541fcaa4a5ae019598240362449ab7e9812b3))
* **full-name:** lower `zindex` to get consistent cursor shape ([d277467](https://github.com/nvim-tree/nvim-tree.lua/commit/d277467fc0d1d0e2bca88165a1de6b526f9f6fe8))
## [1.17.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.16.0...nvim-tree-v1.17.0) (2026-04-07)
### Features
* **#3292:** add nvim_tree.config.filesystem_watchers.whitelist_dirs - strongly recommended for windows ([#3298](https://github.com/nvim-tree/nvim-tree.lua/issues/3298)) ([6eaf74c](https://github.com/nvim-tree/nvim-tree.lua/commit/6eaf74c9919d121d2806ac3c20e757e66bd29c31))
* **#3310:** minimum neovim version 0.10 ([#3311](https://github.com/nvim-tree/nvim-tree.lua/issues/3311)) ([7adc567](https://github.com/nvim-tree/nvim-tree.lua/commit/7adc5679feb439275255aa9e95c434cca206d10c))
### Bug Fixes
* **#3257:** error when calling api.tree.find_file ([#3304](https://github.com/nvim-tree/nvim-tree.lua/issues/3304)) ([b548cfe](https://github.com/nvim-tree/nvim-tree.lua/commit/b548cfef00a79f0b3e3af24f91ae6bd14f22af95))
* **#3293:** marks.bulk operations filter descendant nodes ([#3294](https://github.com/nvim-tree/nvim-tree.lua/issues/3294)) ([f37ebac](https://github.com/nvim-tree/nvim-tree.lua/commit/f37ebac81164e1445c499cdd6de86e6d8e5d57e4))
* **#3301:** setup with empty config fails ([#3303](https://github.com/nvim-tree/nvim-tree.lua/issues/3303)) ([ec0efb0](https://github.com/nvim-tree/nvim-tree.lua/commit/ec0efb06b0abfa56492acfc8c34ac8958d18601e))
* system open native requires nvim 0.10 ([#3309](https://github.com/nvim-tree/nvim-tree.lua/issues/3309)) ([02ccbcd](https://github.com/nvim-tree/nvim-tree.lua/commit/02ccbcd4af9a7fa159251f721d763c0d16f91703))
### Performance Improvements
* **#3231:** decrease setup time: optimise setup and defer requires until tree open ([#3308](https://github.com/nvim-tree/nvim-tree.lua/issues/3308)) ([31503ad](https://github.com/nvim-tree/nvim-tree.lua/commit/31503ad5d869fca61461d82a9126f62480ecb0ab))
* **#3257:** remove setup for actions modules, full-name and explorer ([#3299](https://github.com/nvim-tree/nvim-tree.lua/issues/3299)) ([43a3bb4](https://github.com/nvim-tree/nvim-tree.lua/commit/43a3bb4b26b4578a704f24bb6a4889b454c0eb65))
* **#3257:** remove setup for modules: buffers, diagnostics, git, help, keymap, lib, watcher ([#3295](https://github.com/nvim-tree/nvim-tree.lua/issues/3295)) ([3d385d3](https://github.com/nvim-tree/nvim-tree.lua/commit/3d385d3346e4883d60dc37cf642bd47bed78a46e))
* **#3257:** remove setup for renderer, log, appearance ([#3307](https://github.com/nvim-tree/nvim-tree.lua/issues/3307)) ([3603f06](https://github.com/nvim-tree/nvim-tree.lua/commit/3603f069ff783c31986894a56c2a6755331b0802))
* **#3257:** remove setup for view, extracting view-state ([#3306](https://github.com/nvim-tree/nvim-tree.lua/issues/3306)) ([4cbe795](https://github.com/nvim-tree/nvim-tree.lua/commit/4cbe79526f8bc0bdb1a500d261dc5cc1be4e77d5))
## [1.16.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.15.0...nvim-tree-v1.16.0) (2026-03-17)
### Features
* **#2994:** add visual mode operations: copy, cut, delete, trash, toggle bookmark ([#3268](https://github.com/nvim-tree/nvim-tree.lua/issues/3268)) ([9197f3e](https://github.com/nvim-tree/nvim-tree.lua/commit/9197f3ee3f0c9a754aab5b16500db6d7da5f68fd))
* add default &lt;Del&gt; mapping for api.fs.remove ([#3238](https://github.com/nvim-tree/nvim-tree.lua/issues/3238)) ([ca8d82f](https://github.com/nvim-tree/nvim-tree.lua/commit/ca8d82fff26cb12ced239713e3222f4a9dcd0da0))
### Bug Fixes
* **##3288:** restore single-item paste conflict prompts ([#3289](https://github.com/nvim-tree/nvim-tree.lua/issues/3289)) ([b3772ad](https://github.com/nvim-tree/nvim-tree.lua/commit/b3772adec8db61ba9098c5624a0823a77be3a23d))
* **#3178:** handle Windows paths in ignore_dirs and add .zig-cache to defaults ([#3261](https://github.com/nvim-tree/nvim-tree.lua/issues/3261)) ([5499299](https://github.com/nvim-tree/nvim-tree.lua/commit/5499299746b95c49ef4fc89b30c29a79b577881f))
* **#3187:** prevent closing the last non-floating window when deleting files ([#3282](https://github.com/nvim-tree/nvim-tree.lua/issues/3282)) ([018a078](https://github.com/nvim-tree/nvim-tree.lua/commit/018a078c1e149bcd0c4e65c92aabb8e12501d769))
* **#3198:** add filesystem_watchers.max_events to handle runaway filesystem events on PowerShell ([#3232](https://github.com/nvim-tree/nvim-tree.lua/issues/3232)) ([c07ce43](https://github.com/nvim-tree/nvim-tree.lua/commit/c07ce43527e5f0242121f4eb1feb7ac0ecea8275))
* **#3248:** bookmark filter shows contents of marked directories ([#3249](https://github.com/nvim-tree/nvim-tree.lua/issues/3249)) ([5757bcf](https://github.com/nvim-tree/nvim-tree.lua/commit/5757bcf0447d22d8f78826bc5c59b28da2824c3b))
* **#3251:** pass git.timeout to all vim.system git calls ([#3277](https://github.com/nvim-tree/nvim-tree.lua/issues/3277)) ([e49b0d9](https://github.com/nvim-tree/nvim-tree.lua/commit/e49b0d9bfa70989cf8b5abf5557f51e6e57f68d6))
* **#3265:** rename module api.health to api.appearance, to avoid :checkhealth detection ([#3266](https://github.com/nvim-tree/nvim-tree.lua/issues/3266)) ([1df1960](https://github.com/nvim-tree/nvim-tree.lua/commit/1df1960d0e3a26643a4100f64fa03b991b9f4b85))
* **#3267:** renderer.icons.*_placement 'right_align' at the right hand edge, not the right of the name ([#3270](https://github.com/nvim-tree/nvim-tree.lua/issues/3270)) ([fa3c458](https://github.com/nvim-tree/nvim-tree.lua/commit/fa3c45875f9b1f56ace57711c6f2ac22484ed956))
* **#3281:** fix a bug when a view width of -1 is returned from a function ([#3283](https://github.com/nvim-tree/nvim-tree.lua/issues/3283)) ([c988e28](https://github.com/nvim-tree/nvim-tree.lua/commit/c988e289428d9202b28ba27479647033c7dd2956))
* allow 0 (unlimited) filesystem_watchers.max_events which is the new default, except for windows at 1000 ([#3279](https://github.com/nvim-tree/nvim-tree.lua/issues/3279)) ([87594aa](https://github.com/nvim-tree/nvim-tree.lua/commit/87594aa7c8808701b418b285e9adf690acead201))
* increase filesystem_watchers.max_events from 100 to 1000 ([#3263](https://github.com/nvim-tree/nvim-tree.lua/issues/3263)) ([0f4d2d6](https://github.com/nvim-tree/nvim-tree.lua/commit/0f4d2d6998dc324d54d6adce94186bee43725afb))
* restore bookmark filter for marked directories ([5757bcf](https://github.com/nvim-tree/nvim-tree.lua/commit/5757bcf0447d22d8f78826bc5c59b28da2824c3b))
### Performance Improvements
* **#3284:** re-draw instead of refreshing the tree when a buffer is modified ([#3285](https://github.com/nvim-tree/nvim-tree.lua/issues/3285)) ([c8d8d51](https://github.com/nvim-tree/nvim-tree.lua/commit/c8d8d515c29f0f0b1a352e0d75616f74f42fc03b))
## [1.15.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.14.0...nvim-tree-v1.15.0) (2026-01-11)
### Features
* **#1826:** add diagnostics.diagnostic_opts: vim.diagnostic.Opts will override diagnostics.severity and diagnostics.icons ([#3190](https://github.com/nvim-tree/nvim-tree.lua/issues/3190)) ([fefa335](https://github.com/nvim-tree/nvim-tree.lua/commit/fefa335f1c8f690eb668a1efd18ee4fc6d64cd3e))
* **#1851:** add bookmarks.persist, default path: vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json", disabled by default ([#3033](https://github.com/nvim-tree/nvim-tree.lua/issues/3033)) ([c89215d](https://github.com/nvim-tree/nvim-tree.lua/commit/c89215d6a1107a3c0c134750be48657ba8e6a9aa))
* **#3213:** add `view.width.lines_excluded` option ([776a5cd](https://github.com/nvim-tree/nvim-tree.lua/commit/776a5cdfac948b490e06f1d1d22c4cb986e40699))
* **#3213:** add view.width.lines_excluded option ([#3214](https://github.com/nvim-tree/nvim-tree.lua/issues/3214)) ([776a5cd](https://github.com/nvim-tree/nvim-tree.lua/commit/776a5cdfac948b490e06f1d1d22c4cb986e40699))
* add NvimTreeFilter filetype ([64e2192](https://github.com/nvim-tree/nvim-tree.lua/commit/64e2192f5250796aa4a7f33c6ad888515af50640))
* load command definitions at nvim startup ([#3211](https://github.com/nvim-tree/nvim-tree.lua/issues/3211)) ([1eda256](https://github.com/nvim-tree/nvim-tree.lua/commit/1eda2569394f866360e61f590f1796877388cb8a))
* load command definitions in `plugin` directory ([1eda256](https://github.com/nvim-tree/nvim-tree.lua/commit/1eda2569394f866360e61f590f1796877388cb8a))
* set filter input filetype to NvimTreeFilter ([#3207](https://github.com/nvim-tree/nvim-tree.lua/issues/3207)) ([64e2192](https://github.com/nvim-tree/nvim-tree.lua/commit/64e2192f5250796aa4a7f33c6ad888515af50640))
* use `add_trailing` also for symlink destination ([81ede55](https://github.com/nvim-tree/nvim-tree.lua/commit/81ede55c47528ff7c81b2a498fbee61b298c4e2f))
### Bug Fixes
* **#3226:** set &swapfile=false before setting tree buffer name, avoiding any potential collisions with a swapfile ([#3227](https://github.com/nvim-tree/nvim-tree.lua/issues/3227)) ([8298117](https://github.com/nvim-tree/nvim-tree.lua/commit/8298117311a1f23f039c278e4e4977ab80a15e33))
* api.tree.change_root_to_node on a file now changes directory to parent as per documentation ([#3228](https://github.com/nvim-tree/nvim-tree.lua/issues/3228)) ([b8b44b6](https://github.com/nvim-tree/nvim-tree.lua/commit/b8b44b6a2494d086a9177251a119f9daec6cace8))
* correctly assign extmarks to lines when computing tree window width in `grow` when `nvim-tree.view.width.lines_excluded` contains "root" ([e66994d](https://github.com/nvim-tree/nvim-tree.lua/commit/e66994d40db2d57c91bf9aeaee8bf7ab8b1131f6))
* incorrect window width when right_align icons present ([#3239](https://github.com/nvim-tree/nvim-tree.lua/issues/3239)) ([e66994d](https://github.com/nvim-tree/nvim-tree.lua/commit/e66994d40db2d57c91bf9aeaee8bf7ab8b1131f6))
* prevent NvimTree to be alternate buffer when tab open ([#3205](https://github.com/nvim-tree/nvim-tree.lua/issues/3205)) ([e397756](https://github.com/nvim-tree/nvim-tree.lua/commit/e397756d2a79d74314ea4cd3efc41300e91c0ff0))
* renderer.add_trailing applies to symlink destination ([#3217](https://github.com/nvim-tree/nvim-tree.lua/issues/3217)) ([81ede55](https://github.com/nvim-tree/nvim-tree.lua/commit/81ede55c47528ff7c81b2a498fbee61b298c4e2f))
### Performance Improvements
* **commands:** defer module loading ([#3210](https://github.com/nvim-tree/nvim-tree.lua/issues/3210)) ([68c67ad](https://github.com/nvim-tree/nvim-tree.lua/commit/68c67adfabfd1ce923839570507ef2e81ab8a408))
## [1.14.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.13.0...nvim-tree-v1.14.0) (2025-08-12)
### Features
* **#2685:** highlight git new tracked with NvimTreeGitFileNewHL ([#3176](https://github.com/nvim-tree/nvim-tree.lua/issues/3176)) ([0a52012](https://github.com/nvim-tree/nvim-tree.lua/commit/0a52012d611f3c1492b8d2aba363fabf734de91d))
* **#2789:** add optional function expand_until to api.tree.expand_all and api.node.expand ([#3166](https://github.com/nvim-tree/nvim-tree.lua/issues/3166)) ([1b876db](https://github.com/nvim-tree/nvim-tree.lua/commit/1b876db04903b93c78c97fd3f3dd85d59eeef5ff))
* **#2826:** allow only one window with nvim-tree buffer per tab ([#3174](https://github.com/nvim-tree/nvim-tree.lua/issues/3174)) ([dd2364d](https://github.com/nvim-tree/nvim-tree.lua/commit/dd2364d6802f7f57a98acb8b545ed484c6697626))
* **#3157:** add view.cursorlineopt ([#3158](https://github.com/nvim-tree/nvim-tree.lua/issues/3158)) ([8eb5e0b](https://github.com/nvim-tree/nvim-tree.lua/commit/8eb5e0bfd1c4da6efc03ab0c1ccf463dbaae831e))
### Bug Fixes
* **#3077:** deleting a directory containing symlinked directory will delete the contents of the linked directory ([#3168](https://github.com/nvim-tree/nvim-tree.lua/issues/3168)) ([10db694](https://github.com/nvim-tree/nvim-tree.lua/commit/10db6943cb40625941a35235eeb385ffdfbf827a))
* **#3157:** add view.cursorlineopt ([8eb5e0b](https://github.com/nvim-tree/nvim-tree.lua/commit/8eb5e0bfd1c4da6efc03ab0c1ccf463dbaae831e))
* **#3172:** live filter exception ([#3173](https://github.com/nvim-tree/nvim-tree.lua/issues/3173)) ([0a7fcdf](https://github.com/nvim-tree/nvim-tree.lua/commit/0a7fcdf3f8ba208f4260988a198c77ec11748339))
* invalid window id for popup info window ([#3147](https://github.com/nvim-tree/nvim-tree.lua/issues/3147)) ([d54a187](https://github.com/nvim-tree/nvim-tree.lua/commit/d54a1875a91e1a705795ea26074795210b92ce7f))
* **picker:** exclude full_name window id from the choice ([#3165](https://github.com/nvim-tree/nvim-tree.lua/issues/3165)) ([543ed3c](https://github.com/nvim-tree/nvim-tree.lua/commit/543ed3cac212dc3993ef9f042f6c0812e34ddd43))
* window picker ignore hidden window ([#3145](https://github.com/nvim-tree/nvim-tree.lua/issues/3145)) ([d87b41c](https://github.com/nvim-tree/nvim-tree.lua/commit/d87b41ca537e2131622d48a6c25ccf2fbe0e5d62))
### Performance Improvements
* **#3171:** cache toplevel for untracked ([#3185](https://github.com/nvim-tree/nvim-tree.lua/issues/3185)) ([4425136](https://github.com/nvim-tree/nvim-tree.lua/commit/442513648c6936e754c3308a1c58591a399493e5))
* **#3171:** use vim.system() instead of vim.fn.system() to execute git toplevel ([#3175](https://github.com/nvim-tree/nvim-tree.lua/issues/3175)) ([9a05b9e](https://github.com/nvim-tree/nvim-tree.lua/commit/9a05b9e9f928856ca23dbf876fab372003180c3f))
### Reverts
* **#3180, #3177:** invalid group or tabpage ([#3181](https://github.com/nvim-tree/nvim-tree.lua/issues/3181)) ([9b289ab](https://github.com/nvim-tree/nvim-tree.lua/commit/9b289abd6998e30fd24cbc9919e0b0cbed6364ce))
* **#3180, #3177:** resolve live filter failures ([#3183](https://github.com/nvim-tree/nvim-tree.lua/issues/3183)) ([a4699c0](https://github.com/nvim-tree/nvim-tree.lua/commit/a4699c0904103e7767334f6da05f5c2ea5514845))
## [1.13.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.12.0...nvim-tree-v1.13.0) (2025-06-14)
### Features
* **#3113:** add renderer.icons.folder_arrow_padding ([#3114](https://github.com/nvim-tree/nvim-tree.lua/issues/3114)) ([ea5097a](https://github.com/nvim-tree/nvim-tree.lua/commit/ea5097a1e2702b4827cb7380e7fa0bd6da87699c))
* **#3132:** add api.node.expand and api.node.collapse ([#3133](https://github.com/nvim-tree/nvim-tree.lua/issues/3133)) ([ae59561](https://github.com/nvim-tree/nvim-tree.lua/commit/ae595611fb2225f2041996c042aa4e4b8663b41e))
### Bug Fixes
* "Invalid buffer id" on closing nvim-tree window ([#3129](https://github.com/nvim-tree/nvim-tree.lua/issues/3129)) ([25d16aa](https://github.com/nvim-tree/nvim-tree.lua/commit/25d16aab7d29ca940a9feb92e6bb734697417009))
* **#2746:** background and right aligned icons in floating windows ([#3128](https://github.com/nvim-tree/nvim-tree.lua/issues/3128)) ([cbc3165](https://github.com/nvim-tree/nvim-tree.lua/commit/cbc3165e08893bb499da035c6f6f9d1512b57664))
* **#3117:** allow changing filename's casing ([bd54d1d](https://github.com/nvim-tree/nvim-tree.lua/commit/bd54d1d33c20d8630703b9842480291588dbad07))
* **#3117:** windows: change file/dir case ([#3135](https://github.com/nvim-tree/nvim-tree.lua/issues/3135)) ([bd54d1d](https://github.com/nvim-tree/nvim-tree.lua/commit/bd54d1d33c20d8630703b9842480291588dbad07))
* **#3122:** remove redundant vim.validate ([#3123](https://github.com/nvim-tree/nvim-tree.lua/issues/3123)) ([e7d1b7d](https://github.com/nvim-tree/nvim-tree.lua/commit/e7d1b7dadc62fe2eccc17d814354b0a5688621ce))
* **#3124:** fix icon padding for "right_align" placements, notably for dotfiles ([#3125](https://github.com/nvim-tree/nvim-tree.lua/issues/3125)) ([e4cd856](https://github.com/nvim-tree/nvim-tree.lua/commit/e4cd856ebf4fec51db10c69d63e43224b701cbce))
* **#3124:** prevent empty icons_right_align response from breaking padding ([e4cd856](https://github.com/nvim-tree/nvim-tree.lua/commit/e4cd856ebf4fec51db10c69d63e43224b701cbce))
* **#3134:** setting one glyph to "" no longer disables others ([#3136](https://github.com/nvim-tree/nvim-tree.lua/issues/3136)) ([ebcaccd](https://github.com/nvim-tree/nvim-tree.lua/commit/ebcaccda1c575fa19a8087445276e6671e2b9b37))
* **#3143:** actions.open_file.window_picker.exclude applies when not using window picker ([#3144](https://github.com/nvim-tree/nvim-tree.lua/issues/3144)) ([05d8172](https://github.com/nvim-tree/nvim-tree.lua/commit/05d8172ebf9cdb2d140cf25b75625374fbc3df7f))
* fixes [#3134](https://github.com/nvim-tree/nvim-tree.lua/issues/3134) ([ebcaccd](https://github.com/nvim-tree/nvim-tree.lua/commit/ebcaccda1c575fa19a8087445276e6671e2b9b37))
* invalid buffer issue ([25d16aa](https://github.com/nvim-tree/nvim-tree.lua/commit/25d16aab7d29ca940a9feb92e6bb734697417009))
## [1.12.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.11.0...nvim-tree-v1.12.0) (2025-04-20)
### Features
* add TreePreOpen event ([#3105](https://github.com/nvim-tree/nvim-tree.lua/issues/3105)) ([c24c047](https://github.com/nvim-tree/nvim-tree.lua/commit/c24c0470d9de277fbebecd718f33561ed7c90298))
### Bug Fixes
* **#3101:** when renderer.highlight_opened_files = "none" do not reload on BufUnload and BufReadPost ([#3102](https://github.com/nvim-tree/nvim-tree.lua/issues/3102)) ([5bea2b3](https://github.com/nvim-tree/nvim-tree.lua/commit/5bea2b37523a31288e0fcab42f3be5c1bd4516bb))
* explicitly set `border` to `"none"` in full name float ([#3094](https://github.com/nvim-tree/nvim-tree.lua/issues/3094)) ([c3c1935](https://github.com/nvim-tree/nvim-tree.lua/commit/c3c193594213c5e2f89ec5d7729cad805f76b256))
* reliably dispatch exactly one TreeOpen and TreeClose events ([#3107](https://github.com/nvim-tree/nvim-tree.lua/issues/3107)) ([3a63717](https://github.com/nvim-tree/nvim-tree.lua/commit/3a63717d3d332d8f39aaf65be7a0e4c2265af021))
## [1.11.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.10.0...nvim-tree-v1.11.0) (2025-02-22)
### Features
* **#1984:** add quit_on_open and focus opts to various api.node.open functions ([#3054](https://github.com/nvim-tree/nvim-tree.lua/issues/3054)) ([3281f33](https://github.com/nvim-tree/nvim-tree.lua/commit/3281f331f7f0bef13eb00fb2d5a9d28b2f6155a2))
* **#3037:** add API node.buffer.delete, node.buffer.wipe ([#3040](https://github.com/nvim-tree/nvim-tree.lua/issues/3040)) ([fee1da8](https://github.com/nvim-tree/nvim-tree.lua/commit/fee1da88972f5972a8296813f6c00d7598325ebd))
### Bug Fixes
* **#3045:** wipe scratch buffers for full name and show info popups ([#3050](https://github.com/nvim-tree/nvim-tree.lua/issues/3050)) ([fca0b67](https://github.com/nvim-tree/nvim-tree.lua/commit/fca0b67c0b5a31727fb33addc4d9c100736a2894))
* **#3059:** test for presence of new 0.11 API vim.hl.range ([#3060](https://github.com/nvim-tree/nvim-tree.lua/issues/3060)) ([70825f2](https://github.com/nvim-tree/nvim-tree.lua/commit/70825f23db61ecd900c4cfea169bffe931926a9d))
* arithmetic on nil value error on first git project open ([#3064](https://github.com/nvim-tree/nvim-tree.lua/issues/3064)) ([8052310](https://github.com/nvim-tree/nvim-tree.lua/commit/80523101f0ae48b7f1990e907b685a3d79776c01))
* stl and stlnc fillchars are hidden in window picker ([b699143](https://github.com/nvim-tree/nvim-tree.lua/commit/b69914325a945ee5157f0d21047210b42af5776e))
* window picker: hide fillchars: stl and stlnc ([#3066](https://github.com/nvim-tree/nvim-tree.lua/issues/3066)) ([b699143](https://github.com/nvim-tree/nvim-tree.lua/commit/b69914325a945ee5157f0d21047210b42af5776e))
## [1.10.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.9.0...nvim-tree-v1.10.0) (2025-01-13)
### Features
* **api:** add node.open.vertical_no_picker, node.open.horizontal_no_picker ([#3031](https://github.com/nvim-tree/nvim-tree.lua/issues/3031)) ([68fc4c2](https://github.com/nvim-tree/nvim-tree.lua/commit/68fc4c20f5803444277022c681785c5edd11916d))
### Bug Fixes
* **#3015:** dynamic width no longer truncates on right_align icons ([#3022](https://github.com/nvim-tree/nvim-tree.lua/issues/3022)) ([f7b76cd](https://github.com/nvim-tree/nvim-tree.lua/commit/f7b76cd1a75615c8d6254fc58bedd2a7304eb7d8))
* **#3018:** error when focusing nvim-tree when in terminal mode ([#3019](https://github.com/nvim-tree/nvim-tree.lua/issues/3019)) ([db8d7ac](https://github.com/nvim-tree/nvim-tree.lua/commit/db8d7ac1f524fc6f808764b29fa695c51e014aa6))
* **#3041:** use vim.diagnostic.get for updating diagnostics ([#3042](https://github.com/nvim-tree/nvim-tree.lua/issues/3042)) ([aae0185](https://github.com/nvim-tree/nvim-tree.lua/commit/aae01853ddbd790d1efd6ff04ff96cf38c02c95f))
* Can't re-enter normal mode from terminal mode ([db8d7ac](https://github.com/nvim-tree/nvim-tree.lua/commit/db8d7ac1f524fc6f808764b29fa695c51e014aa6))
* hijack directory "BufEnter", "BufNewFile" events are nested ([#3044](https://github.com/nvim-tree/nvim-tree.lua/issues/3044)) ([39bc630](https://github.com/nvim-tree/nvim-tree.lua/commit/39bc63081605c1d4b974131ebecaea11e8a8595f))
* view.width functions may return strings ([#3020](https://github.com/nvim-tree/nvim-tree.lua/issues/3020)) ([6b4be1d](https://github.com/nvim-tree/nvim-tree.lua/commit/6b4be1dc0cd4d5d5b8e8b56b510a75016e99746f))
## [1.9.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.8.0...nvim-tree-v1.9.0) (2024-12-07)
### Features
* **#2948:** add custom decorators, :help nvim-tree-decorators ([#2996](https://github.com/nvim-tree/nvim-tree.lua/issues/2996)) ([7a4ff1a](https://github.com/nvim-tree/nvim-tree.lua/commit/7a4ff1a516fe92a5ed6b79d7ce31ea4d8f341a72))
### Bug Fixes
* **#2954:** more efficient LSP updates, increase diagnostics.debounce_delay from 50ms to 500ms ([#3007](https://github.com/nvim-tree/nvim-tree.lua/issues/3007)) ([1f3ffd6](https://github.com/nvim-tree/nvim-tree.lua/commit/1f3ffd6af145af2a4930a61c50f763264922c3fe))
* **#2990:** Do not check if buffer is buflisted in diagnostics.update() ([#2998](https://github.com/nvim-tree/nvim-tree.lua/issues/2998)) ([28eac28](https://github.com/nvim-tree/nvim-tree.lua/commit/28eac2801b201f301449e976d7a9e8cfde053ba3))
* **#3009:** nvim &lt; 0.10 apply view options locally ([#3010](https://github.com/nvim-tree/nvim-tree.lua/issues/3010)) ([ca7c4c3](https://github.com/nvim-tree/nvim-tree.lua/commit/ca7c4c33cac2ad66ec69d45e465379716ef0cc97))
* **api:** correct argument types in `wrap_node` and `wrap_node_or_nil` ([#3006](https://github.com/nvim-tree/nvim-tree.lua/issues/3006)) ([f7c65e1](https://github.com/nvim-tree/nvim-tree.lua/commit/f7c65e11d695a084ca10b93df659bb7e68b71f9f))
## [1.8.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.7.1...nvim-tree-v1.8.0) (2024-11-09)
### Features
* **#2819:** add actions.open_file.relative_path, default enabled, following successful experiment ([#2995](https://github.com/nvim-tree/nvim-tree.lua/issues/2995)) ([2ee1c5e](https://github.com/nvim-tree/nvim-tree.lua/commit/2ee1c5e17fdfbf5013af31b1410e4a5f28f4cadd))
* **#2938:** add default filesystem_watchers.ignore_dirs = { "/.ccls-cache", "/build", "/node_modules", "/target", } ([#2940](https://github.com/nvim-tree/nvim-tree.lua/issues/2940)) ([010ae03](https://github.com/nvim-tree/nvim-tree.lua/commit/010ae0365aafd6275c478d932515d2e8e897b7bb))
### Bug Fixes
* **#2945:** stack overflow on api.git.reload or fugitive event with watchers disabled ([#2949](https://github.com/nvim-tree/nvim-tree.lua/issues/2949)) ([5ad8762](https://github.com/nvim-tree/nvim-tree.lua/commit/5ad87620ec9d1190d15c88171a3f0122bc16b0fe))
* **#2947:** root is never a dotfile, so that it doesn't propagate to children ([#2958](https://github.com/nvim-tree/nvim-tree.lua/issues/2958)) ([f5f6789](https://github.com/nvim-tree/nvim-tree.lua/commit/f5f67892996b280ae78b1b0a2d07c4fa29ae0905))
* **#2951:** highlights incorrect following cancelled pick ([#2952](https://github.com/nvim-tree/nvim-tree.lua/issues/2952)) ([1c9553a](https://github.com/nvim-tree/nvim-tree.lua/commit/1c9553a19f70df3dcb171546a3d5e034531ef093))
* **#2954:** resolve occasional tree flashing on diagnostics, set tree buffer options in deterministic order ([#2980](https://github.com/nvim-tree/nvim-tree.lua/issues/2980)) ([82ab19e](https://github.com/nvim-tree/nvim-tree.lua/commit/82ab19ebf79c1839d7351f2fed213d1af13a598e))
* **#2961:** windows: escape brackets and parentheses when opening file ([#2962](https://github.com/nvim-tree/nvim-tree.lua/issues/2962)) ([63c7ad9](https://github.com/nvim-tree/nvim-tree.lua/commit/63c7ad9037fb7334682dd0b3a177cee25c5c8a0f))
* **#2969:** After a rename, the node loses selection ([#2974](https://github.com/nvim-tree/nvim-tree.lua/issues/2974)) ([1403933](https://github.com/nvim-tree/nvim-tree.lua/commit/14039337a563f4efd72831888f332a15585f0ea1))
* **#2972:** error on :colorscheme ([#2973](https://github.com/nvim-tree/nvim-tree.lua/issues/2973)) ([6e5a204](https://github.com/nvim-tree/nvim-tree.lua/commit/6e5a204ca659bb8f2a564df75df2739edec03cb0))
* **#2976:** use vim.loop to preserve neovim 0.9 compatibility ([#2977](https://github.com/nvim-tree/nvim-tree.lua/issues/2977)) ([00dff48](https://github.com/nvim-tree/nvim-tree.lua/commit/00dff482f9a8fb806a54fd980359adc6cd45d435))
* **#2978:** grouped folder not showing closed icon ([#2979](https://github.com/nvim-tree/nvim-tree.lua/issues/2979)) ([120ba58](https://github.com/nvim-tree/nvim-tree.lua/commit/120ba58254835d412bbc91cffe847e9be835fadd))
* **#2981:** windows: root changed when navigating with LSP ([#2982](https://github.com/nvim-tree/nvim-tree.lua/issues/2982)) ([c22124b](https://github.com/nvim-tree/nvim-tree.lua/commit/c22124b37409bee6d1a0da77f4f3a1526f7a204d))
* symlink file icons rendered when renderer.icons.show.file = false, folder.symlink* was incorrectly rendered as folder.default|open ([#2983](https://github.com/nvim-tree/nvim-tree.lua/issues/2983)) ([2156bc0](https://github.com/nvim-tree/nvim-tree.lua/commit/2156bc08c982d3c4b4cfc2b8fd7faeff58a88e10))
## [1.7.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.7.0...nvim-tree-v1.7.1) (2024-09-30)
### Bug Fixes
* **#2794:** sshfs compatibility ([#2922](https://github.com/nvim-tree/nvim-tree.lua/issues/2922)) ([9650e73](https://github.com/nvim-tree/nvim-tree.lua/commit/9650e735baad0d39505f4cb4867a60f02858536a))
* **#2928:** nil explorer in parent move action ([#2929](https://github.com/nvim-tree/nvim-tree.lua/issues/2929)) ([0429f28](https://github.com/nvim-tree/nvim-tree.lua/commit/0429f286b350c65118d66b646775bf187936fa47))
* **#2930:** empty groups expanded on reload ([#2935](https://github.com/nvim-tree/nvim-tree.lua/issues/2935)) ([4520c03](https://github.com/nvim-tree/nvim-tree.lua/commit/4520c0355cc561830ee2cf90dc37a2a75abf7995))
* invalid explorer on open ([#2927](https://github.com/nvim-tree/nvim-tree.lua/issues/2927)) ([59a8a6a](https://github.com/nvim-tree/nvim-tree.lua/commit/59a8a6ae5e9d3eae99d08ab655d12fd51d5d17f3))
### Reverts
* **#2794:** sshfs compatibility ([#2920](https://github.com/nvim-tree/nvim-tree.lua/issues/2920)) ([8405ecf](https://github.com/nvim-tree/nvim-tree.lua/commit/8405ecfbd6bb08a94ffc9c68fef211eea56e8a3b))
## [1.7.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.6.1...nvim-tree-v1.7.0) (2024-09-21)

View File

@@ -2,17 +2,49 @@
Thank you for contributing.
See [Development](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development) for environment setup, tips and tools.
See [wiki: Development](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development) for environment setup, tips and tools.
<!--
https://github.com/jonschlinkert/markdown-toc
markdown-toc --maxdepth=2 -i CONTRIBUTING.md
-->
<!-- toc -->
- [Tools](#tools)
- [Quality](#quality)
* [lint](#lint)
* [style](#style)
* [format-fix](#format-fix)
* [check](#check)
* [format-check](#format-check)
- [Diagnostics](#diagnostics)
- [Backwards Compatibility](#backwards-compatibility)
- [:help Documentation](#help-documentation)
* [Generated Content](#generated-content)
* [Updating And Generating](#updating-and-generating)
* [Checking And Linting](#checking-and-linting)
- [Windows](#windows)
- [Pull Request](#pull-request)
* [Subject](#subject)
- [AI Usage Policy: Highly Discouraged](#ai-usage-policy-highly-discouraged)
* [nvim-tree Is A Community Project](#nvim-tree-is-a-community-project)
* [The Burden Of Review Must Not Increase](#the-burden-of-review-must-not-increase)
* [AI Generated PR Rules](#ai-generated-pr-rules)
<!-- tocstop -->
# Tools
Following are used during CI and strongly recommended during local development.
Language server: [luals](https://luals.github.io)
Lint: [luacheck](https://github.com/lunarmodules/luacheck/)
Style: [StyLua](https://github.com/JohnnyMorganz/StyLua)
nvim-tree migrated from stylua to EmmyLuaCodeStyle ~2024/10. `vim.lsp.buf.format()` may be used as it is the default formatter for luals, using an embedded [EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle)
Language server: [luals](https://luals.github.io)
Formatting: [EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle): `CodeFormat` executable
You can install them via you OS package manager e.g. `pacman`, `brew` or other via other package managers such as `cargo` or `luarocks`
@@ -34,17 +66,19 @@ make lint
## style
1. Runs stylua using `.stylua.toml` settings
1. Runs `scripts/doc-comments.sh` to validate annotated documentation
1. Runs lua language server `codestyle-check` only, using `.luarc.json` settings
1. Runs `scripts/doc-comments.sh` to normalise annotated documentation
```sh
make style
```
You can automatically fix stylua issues via:
## format-fix
You can automatically fix most style issues using `CodeFormat`:
```sh
make style-fix
make format-fix
```
## check
@@ -61,32 +95,113 @@ Assumes `$VIMRUNTIME` is `/usr/share/nvim/runtime`. Adjust as necessary e.g.
VIMRUNTIME="/my/path/to/runtime" make check
```
If `lua-language-server` is not available or `--check` doesn't function (e.g. Arch Linux 3.9.1-1) you can manually install it as per `ci.yml` e.g.
If `lua-language-server` is not available or `--check` doesn't function (e.g. Arch Linux 3.9.1-1) you can manually install it as per `ci.yml` using its current `luals_version` e.g.
```sh
mkdir luals
curl -L "https://github.com/LuaLS/lua-language-server/releases/download/3.9.1/lua-language-server-3.9.1-linux-x64.tar.gz" | tar zx --directory luals
curl -L "https://github.com/LuaLS/lua-language-server/releases/download/3.15.0/lua-language-server-3.15.0-linux-x64.tar.gz" | tar zx --directory luals
PATH="luals/bin:${PATH}" make check
```
# Adding New Actions
## format-check
To add a new action, add a file in `actions/name-of-the-action.lua`. You should export a `setup` function if some configuration is needed.
This is run in CI. Commit or stage your changes and run:
Once you did, you should run `make help-update`
```sh
make format-check
```
# Documentation
- Re-runs `make format-fix`
- Checks that `git diff` is empty, to ensure that all content has been generated. This is why a stage or commit is necessary.
## Opts
# Diagnostics
When adding new options, you should declare the defaults in the main `nvim-tree.lua` file.
Diagnostics issues may not be suppressed. See [luals](https://luals.github.io) documentation for details on how to structure the code and comments.
Documentation for options should also be added to `nvim-tree-opts` in `doc/nvim-tree-lua.txt`
Suppressions are permitted only in the following cases:
## API
- Backwards compatibility shims
- neovim API metadata incorrect, awaiting upstream fix
- classic class framework
When adding or changing API please update :help nvim-tree-api
# Backwards Compatibility
Whenever new neovim API is introduced, please ensure that it is available in older versions. See `:help deprecated.txt` and `$VIMRUNTIME/lua/vim/_meta/api.lua`
See `nvim-tree.setup` for the oldest supported version of neovim. If the API is not availble in that version, a backwards compatibility shim must be used e.g.
```lua
if vim.fn.has("nvim-0.11") == 1 and vim.hl and vim.hl.range then
vim.hl.range(0, ns_id, details.hl_group, { 0, col }, { 0, details.end_col, }, {})
else
vim.api.nvim_buf_add_highlight(0, ns_id, details.hl_group, 0, col, details.end_col) ---@diagnostic disable-line: deprecated
end
```
# :help Documentation
Please update or add to `doc/nvim-tree-lua.txt` as needed.
## Generated Content
`doc/nvim-tree-lua.txt` content starting at `*nvim-tree-config*` will be replaced with generated content. Do not manually edit that content.
### API and Config
Help is generated for:
- `nvim_tree.config` classes from `lua/nvim-tree/_meta/config/`
- `nvim_tree.api` functions from `lua/nvim-tree/_meta/api/`
Please add or update documentation when you make changes, see `:help dev-lua-doc` for docstring format.
`scripts/vimdoc_config.lua` contains the manifest of help sources.
### Config And Mappings
Help is updated for:
- Default keymap at `keymap.on_attach_default`
- Default config at `--- config-default-start`
## Updating And Generating
Nvim sources are required. You will be prompted with instructions on fetching and using the sources.
See comments at the start of each script for complete details.
```sh
make help-update
```
- `scripts/help-defaults.sh`
- Update config defaults `*nvim-tree-config-default*`
- Update default mappings:
- `*nvim-tree-mappings-default*`
- `*nvim-tree-quickstart-help*`
- `scripts/vimdoc.sh doc`
- Remove content starting at `*nvim-tree-config*`
- Generate config classes `*nvim-tree-config*`
- Generate API `*nvim-tree-api*`
## Checking
This is run in CI. Commit or stage your changes and run:
```sh
make help-check
```
- Re-runs `make help-update`
- Checks that `git diff` is empty, to ensure that all content has been generated. This is why a stage or commit is necessary.
# Windows
Please note that nvim-tree team members do not have access to nor expertise with Windows.
You will need to be an active participant during development and raise a PR to resolve any issues that may arise.
Please ensure that windows specific features and fixes are behind the appropriate feature flag, see [wiki: OS Feature Flags](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development#os-feature-flags)
# Pull Request
@@ -94,6 +209,8 @@ Please reference any issues in the description e.g. "resolves #1234", which will
Please check "allow edits by maintainers" to allow nvim-tree developers to make small changes such as documentation tweaks.
Do not enable or use any AI review tools (e.g. Copilot) on the Pull Request.
## Subject
The merge commit message will be the subject of the PR.
@@ -118,3 +235,45 @@ Available types:
If in doubt, look at previous commits.
See also [The Conventional Commits ultimate cheatsheet](https://gist.github.com/gabrielecanepa/fa6cca1a8ae96f77896fe70ddee65527)
# AI Usage Policy: Highly Discouraged
## nvim-tree Is A Community Project
nvim-tree is a work of passion, building a community of free thinking individuals who contribute highly polished, elegant and maintainable code. Community members range from novices to professionals and all are welcome. Teaching, encouraging and celebrating the growth of less experienced developers is of great importance.
AI generated code is discouraged as this doesn't match these nvim-tree values.
Human PR reviews will always be prioritised over AI generated PRs.
## The Burden Of Review Must Not Increase
There must be a human in the loop at all times. The contributor is the author of and is fully accountable for AI generated contributions.
AI generated PRs have low, or even non-existent entry level. Human generated PRs require you to be motivated, do research, familiarise yourself with code, make effort to write the code and test it.
Low effort or unqualified code increases the burden of review and testing on maintainers, who are limited in time.
Contributors must:
- Read and review all generated code and documentation before raising a PR
- Fully understand all code and documentation
- Be able to answer any questions during review
## AI Generated PR Rules
The PR description and comments must be written by the contributor, with no AI assistance beyond grammar or English translations.
The description must:
- Describe the solution design, justifying all decisions
- Clearly state which AI was used
- List which code and documentation was written by a human and which was written by AI
- Detail all testing performed
There must be far greater than usual number of detailed, explicit comments:
- File level: overview of the changes made
- Line level: 1-2 comments per function/method
Do not enable or use any AI review tools (e.g. Copilot) on the Pull Request.

View File

@@ -5,7 +5,7 @@ all: lint style check
#
lint: luacheck
style: stylua style-doc
style: style-check style-doc
check: luals
@@ -13,28 +13,33 @@ check: luals
# subtasks
#
luacheck:
luacheck -q lua
luacheck --codes --quiet lua --exclude-files "**/_meta/**"
stylua:
stylua lua --check
style-check:
scripts/luals-check.sh codestyle-check
style-doc:
scripts/doc-comments.sh
luals:
@scripts/luals-check.sh
scripts/luals-check.sh
#
# fixes
# format
#
style-fix:
stylua lua
format-fix:
CodeFormat format --config .editorconfig --workspace lua
format-check:
CodeFormat format --config .editorconfig --workspace lua
git diff --exit-code lua
#
# utility
#
help-update:
scripts/help-update.sh
scripts/vimdoc.sh doc
scripts/help-defaults.sh
#
# CI
@@ -43,5 +48,5 @@ help-check: help-update
git diff --exit-code doc/nvim-tree-lua.txt
.PHONY: all lint style check luacheck stylua style-doc luals style-fix help-update help-check
.PHONY: all lint style check luacheck style-check style-doc luals format-fix format-check help-update help-check

220
README.md
View File

@@ -15,7 +15,9 @@
(Live) filtering
Cut, copy, paste, rename, delete, create
Rename, delete, create
Cut, copy, paste locally and between instances
Highly customisable
@@ -26,93 +28,123 @@ Take a look at the [wiki](https://github.com/nvim-tree/nvim-tree.lua/wiki) for S
Questions and general support: [Discussions](https://github.com/nvim-tree/nvim-tree.lua/discussions)
## Requirements
<!--
https://github.com/jonschlinkert/markdown-toc
markdown-toc --maxdepth=2 -i README.md
-->
[neovim >=0.9.0](https://github.com/neovim/neovim/wiki/Installing-Neovim)
<!-- toc -->
- [Requirements](#requirements)
- [Installing](#installing)
- [Quick Start](#quick-start)
* [Setup](#setup)
* [Help](#help)
* [Custom Mappings](#custom-mappings)
* [Highlight Groups](#highlight-groups)
- [Commands](#commands)
- [Roadmap](#roadmap)
- [API](#api)
- [Contributing](#contributing)
- [Screenshots](#screenshots)
- [Team](#team)
<!-- tocstop -->
# Requirements
[Nvim >= 0.10](https://github.com/neovim/neovim/wiki/Installing-Neovim). At least `n - 0.1` stable Nvim compatibility is guaranteed. You may use a `compat-nvim-0.X` tag for earlier Nvim versions, however they will receive no updates or support.
[nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) is optional and used to display file icons. It requires a [patched font](https://www.nerdfonts.com/). Your terminal emulator must be configured to use that font, usually "Hack Nerd Font"
## Install
# Installing
Please install via your preferred package manager. See [Installation](https://github.com/nvim-tree/nvim-tree.lua/wiki/Installation) for specific package manager instructions.
`nvim-tree/nvim-tree.lua`
Please install via your preferred package manager. See [Installation](https://github.com/nvim-tree/nvim-tree.lua/wiki/Installation) for some specific package manager instructions.
Major or minor versions may be specified via tags: `v<MAJOR>` e.g. `v1` or `v<MAJOR>.<MINOR>` e.g. `v1.23`
`nvim-tree/nvim-web-devicons` optional, for file icons
# Quick Start
Install the plugins via your package manager:
`"nvim-tree/nvim-tree.lua"`
`"nvim-tree/nvim-web-devicons"`
Disabling [netrw](https://neovim.io/doc/user/pi_netrw.html) is strongly advised, see [:help nvim-tree-netrw](doc/nvim-tree-lua.txt)
## Quick Start
## Setup
### Setup
Setup the plugin in your `init.lua`.
Setup the plugin in your `init.lua`
See [:help nvim-tree-setup](doc/nvim-tree-lua.txt) and [:help nvim-tree-config-default](doc/nvim-tree-lua.txt)
```lua
-- disable netrw at the very start of your init.lua
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
-- disable netrw at the very start of your init.lua
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
-- optionally enable 24-bit colour
vim.opt.termguicolors = true
-- optionally enable 24-bit colour
vim.opt.termguicolors = true
-- empty setup using defaults
require("nvim-tree").setup()
-- empty setup using defaults
require("nvim-tree").setup()
-- OR setup with some options
require("nvim-tree").setup({
sort = {
sorter = "case_sensitive",
},
view = {
width = 30,
},
renderer = {
group_empty = true,
},
filters = {
dotfiles = true,
},
})
-- OR setup with a config
---@type nvim_tree.config
local config = {
sort = {
sorter = "case_sensitive",
},
view = {
width = 30,
},
renderer = {
group_empty = true,
},
filters = {
dotfiles = true,
},
}
require("nvim-tree").setup(config)
```
### Help
## Help
Open the tree: `:NvimTreeOpen`
Show the mappings: `g?`
### Custom Mappings
## Custom Mappings
[:help nvim-tree-mappings-default](doc/nvim-tree-lua.txt) are applied by default however you may customise via |nvim-tree.on_attach| e.g.
[:help nvim-tree-mappings-default](doc/nvim-tree-lua.txt) are applied by default however you may customise via [:help nvim_tree.config](doc/nvim-tree-lua.txt) `{on_attach}` e.g.
```lua
local function my_on_attach(bufnr)
local api = require "nvim-tree.api"
local function my_on_attach(bufnr)
local api = require "nvim-tree.api"
local function opts(desc)
return { desc = "nvim-tree: " .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }
local function opts(desc)
return { desc = "nvim-tree: " .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }
end
-- default mappings
api.map.on_attach.default(bufnr)
-- custom mappings
vim.keymap.set("n", "<C-t>", api.tree.change_root_to_parent, opts("Up"))
vim.keymap.set("n", "?", api.tree.toggle_help, opts("Help"))
end
-- default mappings
api.config.mappings.default_on_attach(bufnr)
-- custom mappings
vim.keymap.set('n', '<C-t>', api.tree.change_root_to_parent, opts('Up'))
vim.keymap.set('n', '?', api.tree.toggle_help, opts('Help'))
end
-- pass to setup along with your other options
require("nvim-tree").setup {
---
on_attach = my_on_attach,
---
}
-- pass to setup along with your other config
require("nvim-tree").setup({
---
on_attach = my_on_attach,
---
})
```
### Highlight
## Highlight Groups
See [:help nvim-tree-highlight-groups](doc/nvim-tree-lua.txt)
Run `:NvimTreeHiTest` to show all the highlights that nvim-tree uses.
@@ -120,65 +152,93 @@ They can be customised before or after setup is called and will be immediately
applied at runtime. e.g.
```lua
vim.cmd([[
:hi NvimTreeExecFile guifg=#ffa0a0
:hi NvimTreeSpecialFile guifg=#ff80ff gui=underline
:hi NvimTreeSymlink guifg=Yellow gui=italic
:hi link NvimTreeImageFile Title
]])
vim.cmd([[
:hi NvimTreeExecFile guifg=#ffa0a0
:hi NvimTreeSpecialFile guifg=#ff80ff gui=underline
:hi NvimTreeSymlink guifg=Yellow gui=italic
:hi link NvimTreeImageFile Title
]])
```
See [:help nvim-tree-highlight](doc/nvim-tree-lua.txt) for details.
## Commands
# Commands
See [:help nvim-tree-commands](doc/nvim-tree-lua.txt)
Basic commands:
Some commands may be executed with a bang `!` or take a `path` string argument.
`:NvimTreeToggle` Open or close the tree. Takes an optional path argument.
All commands execute public API.
`:NvimTreeFocus` Open the tree if it is closed, and then focus on the tree.
Some basic commands:
`:NvimTreeFindFile` Move the cursor in the tree for the current buffer, opening folders if needed.
`:NvimTreeFocus` [:help nvim_tree.api.tree.open()](doc/nvim-tree-lua.txt)
```lua
require("nvim-tree.api").tree.open()
```
`:NvimTreeCollapse` Collapses the nvim-tree recursively.
`:NvimTreeToggle` [:help nvim_tree.api.tree.toggle()](doc/nvim-tree-lua.txt)
```lua
require("nvim-tree.api").tree.toggle({
path = "<args>",
find_file = false,
update_root = false,
focus = true,
})
```
## Roadmap
`:NvimTreeFindFile` [:help nvim_tree.api.tree.find_file()](doc/nvim-tree-lua.txt)
```lua
require("nvim-tree.api").tree.find_file({
open = true,
update_root = "<bang>",
focus = true,
})
```
`:NvimTreeCollapse` [:help nvim_tree.api.tree.collapse_all()](doc/nvim-tree-lua.txt)
```lua
require("nvim-tree.api").tree.collapse_all({
keep_buffers = false
})
```
# Roadmap
nvim-tree is stable and new major features will not be added. The focus is on existing user experience.
Users are encouraged to add their own custom features via the public [API](#api).
Development is focused on:
* Bug fixes
* Performance
* Quality of Life improvements
* API / Events
* Enhancements to existing features
- Bug fixes
- Performance
- Quality of Life improvements
- API / Events
- Enhancements to existing features
- Multi-instance capabilities
## API
# API
nvim-tree exposes a public API. This is non breaking, with additions made as necessary. See [:help nvim-tree-api](doc/nvim-tree-lua.txt)
See wiki [Recipes](https://github.com/nvim-tree/nvim-tree.lua/wiki/Recipes) and [Tips](https://github.com/nvim-tree/nvim-tree.lua/wiki/Tips) for ideas and inspiration.
Please raise a [feature request](https://github.com/nvim-tree/nvim-tree.lua/issues/new?assignees=&labels=feature+request&template=feature_request.md&title=) if the API is insufficient for your needs. [Contributions](#Contributing) are always welcome.
Please raise a [feature request](https://github.com/nvim-tree/nvim-tree.lua/issues/new?assignees=&labels=feature+request&template=feature_request.md&title=) if the API is insufficient for your needs. Contributions are always welcome, see below.
You may also subscribe to events that nvim-tree will dispatch in a variety of situations, see [:help nvim-tree-events](doc/nvim-tree-lua.txt)
## Contributing
# Contributing
PRs are always welcome. See [wiki](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development) to get started.
PRs are always welcome. See [CONTRIBUTING](CONTRIBUTING.md) and [wiki: Development](https://github.com/nvim-tree/nvim-tree.lua/wiki/Development) to get started.
See [bug](https://github.com/nvim-tree/nvim-tree.lua/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and [PR Please](https://github.com/nvim-tree/nvim-tree.lua/issues?q=is%3Aopen+is%3Aissue+label%3A%22PR+please%22) issues if you are looking for some work to get you started.
## Screenshots
# Screenshots
See [Showcases](https://github.com/nvim-tree/nvim-tree.lua/wiki/Showcases) wiki page for examples of user's configurations with sources.
Please add your own!
## Team
# Team
* [@alex-courtis](https://github.com/alex-courtis) Arch Linux
* [@gegoune](https://github.com/gegoune) macOS

File diff suppressed because it is too large Load Diff

View File

@@ -1,876 +1,56 @@
local lib = require "nvim-tree.lib"
local log = require "nvim-tree.log"
local appearance = require "nvim-tree.appearance"
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils"
local actions = require "nvim-tree.actions"
local core = require "nvim-tree.core"
local notify = require "nvim-tree.notify"
local M = {}
local _config = {}
---`require("nvim-tree").setup` must be called once to initialise nvim-tree.
---
---Call again to apply a change in configuration without restarting Nvim.
---
---See :help nvim-tree-setup
---
---@param config_user? nvim_tree.config subset, uses defaults when nil
function M.setup(config_user)
local api = require("nvim-tree.api")
local api_impl = require("nvim-tree.api.impl")
local appearance = require("nvim-tree.appearance")
local autocmd = require("nvim-tree.autocmd")
local config = require("nvim-tree.config")
local log = require("nvim-tree.log")
local view_state = require("nvim-tree.view-state")
local M = {
init_root = "",
}
--- Update the tree root to a directory or the directory containing
---@param path string relative or absolute
---@param bufnr number|nil
function M.change_root(path, bufnr)
-- skip if current file is in ignore_list
if type(bufnr) == "number" then
local ft
if vim.fn.has "nvim-0.10" == 1 then
ft = vim.api.nvim_get_option_value("filetype", { buf = bufnr }) or ""
else
ft = vim.api.nvim_buf_get_option(bufnr, "filetype") or "" ---@diagnostic disable-line: deprecated
end
for _, value in pairs(_config.update_focused_file.update_root.ignore_list) do
if utils.str_find(path, value) or utils.str_find(ft, value) then
return
end
end
end
-- don't find inexistent
if vim.fn.filereadable(path) == 0 then
-- Nvim version check
if vim.fn.has("nvim-0.10") == 0 then
require("nvim-tree.notify").warn(
"nvim-tree.lua requires Nvim >= 0.10. You may use a compat-nvim-0.X tag for earlier Nvim versions, however they will receive no updates or support.")
return
end
local cwd = core.get_cwd()
if cwd == nil then
return
end
-- validate and merge with defaults as config.g
config.setup(config_user)
local vim_cwd = vim.fn.getcwd()
-- optionally create the log file
log.start()
-- test if in vim_cwd
if utils.path_relative(path, vim_cwd) ~= path then
if vim_cwd ~= cwd then
actions.root.change_dir.fn(vim_cwd)
end
return
end
-- test if in cwd
if utils.path_relative(path, cwd) ~= path then
return
end
-- otherwise test M.init_root
if _config.prefer_startup_root and utils.path_relative(path, M.init_root) ~= path then
actions.root.change_dir.fn(M.init_root)
return
end
-- otherwise root_dirs
for _, dir in pairs(_config.root_dirs) do
dir = vim.fn.fnamemodify(dir, ":p")
if utils.path_relative(path, dir) ~= path then
actions.root.change_dir.fn(dir)
return
end
end
-- finally fall back to the folder containing the file
actions.root.change_dir.fn(vim.fn.fnamemodify(path, ":p:h"))
end
function M.tab_enter()
if view.is_visible { any_tabpage = true } then
local bufname = vim.api.nvim_buf_get_name(0)
local ft
if vim.fn.has "nvim-0.10" == 1 then
ft = vim.api.nvim_get_option_value("filetype", { buf = 0 }) or ""
else
ft = vim.api.nvim_buf_get_option(0, "ft") ---@diagnostic disable-line: deprecated
end
for _, filter in ipairs(M.config.tab.sync.ignore) do
if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then
return
end
end
view.open { focus_tree = false }
local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end
end
end
function M.open_on_directory()
local should_proceed = _config.hijack_directories.auto_open or view.is_visible()
if not should_proceed then
return
end
local buf = vim.api.nvim_get_current_buf()
local bufname = vim.api.nvim_buf_get_name(buf)
if vim.fn.isdirectory(bufname) ~= 1 then
return
end
actions.root.change_dir.force_dirchange(bufname, true)
end
function M.place_cursor_on_node()
local ok, search = pcall(vim.fn.searchcount)
if ok and search and search.exact_match == 1 then
return
end
local node = lib.get_node_at_cursor()
if not node or node.name == ".." then
return
end
node = utils.get_parent_of_group(node)
local line = vim.api.nvim_get_current_line()
local cursor = vim.api.nvim_win_get_cursor(0)
local idx = vim.fn.stridx(line, node.name)
if idx >= 0 then
vim.api.nvim_win_set_cursor(0, { cursor[1], idx })
end
end
---@return table
function M.get_config()
return M.config
end
---@param disable_netrw boolean
---@param hijack_netrw boolean
local function manage_netrw(disable_netrw, hijack_netrw)
if hijack_netrw then
vim.cmd "silent! autocmd! FileExplorer *"
vim.cmd "autocmd VimEnter * ++once silent! autocmd! FileExplorer *"
end
if disable_netrw then
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
end
end
---@param name string|nil
function M.change_dir(name)
if name then
actions.root.change_dir.fn(name)
end
if _config.update_focused_file.update_root.enable then
actions.tree.find_file.fn()
end
end
---@param opts table
local function setup_autocommands(opts)
local augroup_id = vim.api.nvim_create_augroup("NvimTree", { clear = true })
local function create_nvim_tree_autocmd(name, custom_opts)
local default_opts = { group = augroup_id }
vim.api.nvim_create_autocmd(name, vim.tbl_extend("force", default_opts, custom_opts))
end
-- reset and draw (highlights) when colorscheme is changed
create_nvim_tree_autocmd("ColorScheme", {
callback = function()
appearance.setup()
view.reset_winhl()
local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end
end,
})
-- prevent new opened file from opening in the same window as nvim-tree
create_nvim_tree_autocmd("BufWipeout", {
pattern = "NvimTree_*",
callback = function()
if not utils.is_nvim_tree_buf(0) then
return
end
if opts.actions.open_file.eject then
view._prevent_buffer_override()
else
view.abandon_current_window()
end
end,
})
create_nvim_tree_autocmd("BufWritePost", {
callback = function()
if opts.auto_reload_on_write and not opts.filesystem_watchers.enable then
local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end
end,
})
create_nvim_tree_autocmd("BufReadPost", {
callback = function(data)
-- update opened file buffers
local explorer = core.get_explorer()
if not explorer then
return
end
if
(explorer.filters.config.filter_no_buffer or explorer.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
explorer:reload_explorer()
end)
end
end,
})
create_nvim_tree_autocmd("BufUnload", {
callback = function(data)
-- update opened file buffers
local explorer = core.get_explorer()
if not explorer then
return
end
if
(explorer.filters.config.filter_no_buffer or explorer.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
explorer:reload_explorer()
end)
end
end,
})
create_nvim_tree_autocmd("User", {
pattern = { "FugitiveChanged", "NeogitStatusRefreshed" },
callback = function()
if not opts.filesystem_watchers.enable and opts.git.enable then
local explorer = core.get_explorer()
if explorer then
explorer:reload_git()
end
end
end,
})
if opts.tab.sync.open then
create_nvim_tree_autocmd("TabEnter", { callback = vim.schedule_wrap(M.tab_enter) })
end
if opts.hijack_cursor then
create_nvim_tree_autocmd("CursorMoved", {
pattern = "NvimTree_*",
callback = function()
if utils.is_nvim_tree_buf(0) then
M.place_cursor_on_node()
end
end,
})
end
if opts.sync_root_with_cwd then
create_nvim_tree_autocmd("DirChanged", {
callback = function()
M.change_dir(vim.loop.cwd())
end,
})
end
if opts.update_focused_file.enable then
create_nvim_tree_autocmd("BufEnter", {
callback = function(event)
local exclude = opts.update_focused_file.exclude
if type(exclude) == "function" and exclude(event) then
return
end
utils.debounce("BufEnter:find_file", opts.view.debounce_delay, function()
actions.tree.find_file.fn()
end)
end,
})
end
if opts.hijack_directories.enable then
create_nvim_tree_autocmd({ "BufEnter", "BufNewFile" }, { callback = M.open_on_directory })
end
create_nvim_tree_autocmd("BufEnter", {
pattern = "NvimTree_*",
callback = function()
if utils.is_nvim_tree_buf(0) then
if vim.fn.getcwd() ~= core.get_cwd() or (opts.reload_on_bufenter and not opts.filesystem_watchers.enable) then
local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end
end
end,
})
if opts.view.centralize_selection then
create_nvim_tree_autocmd("BufEnter", {
pattern = "NvimTree_*",
callback = function()
vim.schedule(function()
vim.api.nvim_buf_call(0, function()
vim.cmd [[norm! zz]]
end)
end)
end,
})
end
if opts.diagnostics.enable then
create_nvim_tree_autocmd("DiagnosticChanged", {
callback = function()
log.line("diagnostics", "DiagnosticChanged")
require("nvim-tree.diagnostics").update()
end,
})
create_nvim_tree_autocmd("User", {
pattern = "CocDiagnosticChange",
callback = function()
log.line("diagnostics", "CocDiagnosticChange")
require("nvim-tree.diagnostics").update()
end,
})
end
if opts.view.float.enable and opts.view.float.quit_on_focus_loss then
create_nvim_tree_autocmd("WinLeave", {
pattern = "NvimTree_*",
callback = function()
if utils.is_nvim_tree_buf(0) then
view.close()
end
end,
})
end
if opts.modified.enable then
create_nvim_tree_autocmd({ "BufModifiedSet", "BufWritePost" }, {
callback = function()
utils.debounce("Buf:modified", opts.view.debounce_delay, function()
require("nvim-tree.buffers").reload_modified()
local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end)
end,
})
end
end
local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
on_attach = "default",
hijack_cursor = false,
auto_reload_on_write = true,
disable_netrw = false,
hijack_netrw = true,
hijack_unnamed_buffer_when_opening = false,
root_dirs = {},
prefer_startup_root = false,
sync_root_with_cwd = false,
reload_on_bufenter = false,
respect_buf_cwd = false,
select_prompts = false,
sort = {
sorter = "name",
folders_first = true,
files_first = false,
},
view = {
centralize_selection = false,
cursorline = true,
debounce_delay = 15,
side = "left",
preserve_window_proportions = false,
number = false,
relativenumber = false,
signcolumn = "yes",
width = 30,
float = {
enable = false,
quit_on_focus_loss = true,
open_win_config = {
relative = "editor",
border = "rounded",
width = 30,
height = 30,
row = 1,
col = 1,
},
},
},
renderer = {
add_trailing = false,
group_empty = false,
full_name = false,
root_folder_label = ":~:s?$?/..?",
indent_width = 2,
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none",
symlink_destination = true,
highlight_git = "none",
highlight_diagnostics = "none",
highlight_opened_files = "none",
highlight_modified = "none",
highlight_hidden = "none",
highlight_bookmarks = "none",
highlight_clipboard = "name",
indent_markers = {
enable = false,
inline_arrows = true,
icons = {
corner = "",
edge = "",
item = "",
bottom = "",
none = " ",
},
},
icons = {
web_devicons = {
file = {
enable = true,
color = true,
},
folder = {
enable = false,
color = true,
},
},
git_placement = "before",
modified_placement = "after",
hidden_placement = "after",
diagnostics_placement = "signcolumn",
bookmarks_placement = "signcolumn",
padding = " ",
symlink_arrow = "",
show = {
file = true,
folder = true,
folder_arrow = true,
git = true,
modified = true,
hidden = false,
diagnostics = true,
bookmarks = true,
},
glyphs = {
default = "",
symlink = "",
bookmark = "󰆤",
modified = "",
hidden = "󰜌",
folder = {
arrow_closed = "",
arrow_open = "",
default = "",
open = "",
empty = "",
empty_open = "",
symlink = "",
symlink_open = "",
},
git = {
unstaged = "",
staged = "",
unmerged = "",
renamed = "",
untracked = "",
deleted = "",
ignored = "",
},
},
},
},
hijack_directories = {
enable = true,
auto_open = true,
},
update_focused_file = {
enable = false,
update_root = {
enable = false,
ignore_list = {},
},
exclude = false,
},
system_open = {
cmd = "",
args = {},
},
git = {
enable = true,
show_on_dirs = true,
show_on_open_dirs = true,
disable_for_dirs = {},
timeout = 400,
cygwin_support = false,
},
diagnostics = {
enable = false,
show_on_dirs = false,
show_on_open_dirs = true,
debounce_delay = 50,
severity = {
min = vim.diagnostic.severity.HINT,
max = vim.diagnostic.severity.ERROR,
},
icons = {
hint = "",
info = "",
warning = "",
error = "",
},
},
modified = {
enable = false,
show_on_dirs = true,
show_on_open_dirs = true,
},
filters = {
enable = true,
git_ignored = true,
dotfiles = false,
git_clean = false,
no_buffer = false,
no_bookmark = false,
custom = {},
exclude = {},
},
live_filter = {
prefix = "[FILTER]: ",
always_show_folders = true,
},
filesystem_watchers = {
enable = true,
debounce_delay = 50,
ignore_dirs = {},
},
actions = {
use_system_clipboard = true,
change_dir = {
enable = true,
global = false,
restrict_above_cwd = false,
},
expand_all = {
max_folder_discovery = 300,
exclude = {},
},
file_popup = {
open_win_config = {
col = 1,
row = 1,
relative = "cursor",
border = "shadow",
style = "minimal",
},
},
open_file = {
quit_on_open = false,
eject = true,
resize_window = true,
window_picker = {
enable = true,
picker = "default",
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
exclude = {
filetype = { "notify", "packer", "qf", "diff", "fugitive", "fugitiveblame" },
buftype = { "nofile", "terminal", "help" },
},
},
},
remove_file = {
close_window = true,
},
},
trash = {
cmd = "gio trash",
},
tab = {
sync = {
open = false,
close = false,
ignore = {},
},
},
notify = {
threshold = vim.log.levels.INFO,
absolute_path = true,
},
help = {
sort_by = "key",
},
ui = {
confirm = {
remove = true,
trash = true,
default_yes = false,
},
},
experimental = {
actions = {
open_file = {
relative_path = false,
},
},
},
log = {
enable = false,
truncate = false,
types = {
all = false,
config = false,
copy_paste = false,
dev = false,
diagnostics = false,
git = false,
profile = false,
watcher = false,
},
},
} -- END_DEFAULT_OPTS
local function merge_options(conf)
return vim.tbl_deep_extend("force", DEFAULT_OPTS, conf or {})
end
local FIELD_SKIP_VALIDATE = {
open_win_config = true,
}
local ACCEPTED_TYPES = {
on_attach = { "function", "string" },
sort = {
sorter = { "function", "string" },
},
view = {
width = {
"string",
"function",
"number",
"table",
min = { "string", "function", "number" },
max = { "string", "function", "number" },
padding = { "function", "number" },
},
},
renderer = {
hidden_display = { "function", "string" },
group_empty = { "boolean", "function" },
root_folder_label = { "function", "string", "boolean" },
},
update_focused_file = {
exclude = { "function" },
},
git = {
disable_for_dirs = { "function" },
},
filters = {
custom = { "function" },
},
filesystem_watchers = {
ignore_dirs = { "function" },
},
actions = {
open_file = {
window_picker = {
picker = { "function", "string" },
},
},
},
}
local ACCEPTED_STRINGS = {
sort = {
sorter = { "name", "case_sensitive", "modification_time", "extension", "suffix", "filetype" },
},
view = {
side = { "left", "right" },
signcolumn = { "yes", "no", "auto" },
},
renderer = {
hidden_display = { "none", "simple", "all" },
highlight_git = { "none", "icon", "name", "all" },
highlight_opened_files = { "none", "icon", "name", "all" },
highlight_modified = { "none", "icon", "name", "all" },
highlight_hidden = { "none", "icon", "name", "all" },
highlight_bookmarks = { "none", "icon", "name", "all" },
highlight_diagnostics = { "none", "icon", "name", "all" },
highlight_clipboard = { "none", "icon", "name", "all" },
icons = {
git_placement = { "before", "after", "signcolumn", "right_align" },
modified_placement = { "before", "after", "signcolumn", "right_align" },
hidden_placement = { "before", "after", "signcolumn", "right_align" },
diagnostics_placement = { "before", "after", "signcolumn", "right_align" },
bookmarks_placement = { "before", "after", "signcolumn", "right_align" },
},
},
help = {
sort_by = { "key", "desc" },
},
}
---@param conf table|nil
local function validate_options(conf)
local msg
---@param user any
---@param def any
---@param strs table
---@param types table
---@param prefix string
local function validate(user, def, strs, types, prefix)
-- if user's option is not a table there is nothing to do
if type(user) ~= "table" then
return
end
-- only compare tables with contents that are not integer indexed
if type(def) ~= "table" or not next(def) or type(next(def)) == "number" then
-- unless the field can be a table (and is not a table in default config)
if vim.tbl_contains(types, "table") then
-- use a dummy default to allow all checks
def = {}
else
return
end
end
for k, v in pairs(user) do
if not FIELD_SKIP_VALIDATE[k] then
local invalid
if def[k] == nil and types[k] == nil then
-- option does not exist
invalid = string.format("Unknown option: %s%s", prefix, k)
elseif type(v) ~= type(def[k]) then
local expected
if types[k] and #types[k] > 0 then
if not vim.tbl_contains(types[k], type(v)) then
expected = table.concat(types[k], "|")
end
else
expected = type(def[k])
end
if expected then
-- option is of the wrong type
invalid = string.format("Invalid option: %s%s. Expected %s, got %s", prefix, k, expected, type(v))
end
elseif type(v) == "string" and strs[k] and not vim.tbl_contains(strs[k], v) then
-- option has type `string` but value is not accepted
invalid = string.format("Invalid value for field %s%s: '%s'", prefix, k, v)
end
if invalid then
if msg then
msg = string.format("%s\n%s", msg, invalid)
else
msg = invalid
end
user[k] = nil
else
validate(v, def[k], strs[k] or {}, types[k] or {}, prefix .. k .. ".")
end
end
end
end
validate(conf, DEFAULT_OPTS, ACCEPTED_STRINGS, ACCEPTED_TYPES, "")
if msg then
notify.warn(msg .. "\n\nsee :help nvim-tree-opts for available configuration options")
end
end
--- Apply OS specific localisations to DEFAULT_OPTS
local function localise_default_opts()
if utils.is_macos or utils.is_windows then
DEFAULT_OPTS.trash.cmd = "trash"
end
end
function M.purge_all_state()
require("nvim-tree.watcher").purge_watchers()
view.close_all_tabs()
view.abandon_all_windows()
if core.get_explorer() ~= nil then
require("nvim-tree.git").purge_state()
core.reset_explorer()
end
end
---@param conf table|nil
function M.setup(conf)
if vim.fn.has "nvim-0.9" == 0 then
notify.warn "nvim-tree.lua requires Neovim 0.9 or higher"
return
end
M.init_root = vim.fn.getcwd()
localise_default_opts()
require("nvim-tree.legacy").migrate_legacy_options(conf or {})
validate_options(conf)
local opts = merge_options(conf)
local netrw_disabled = opts.disable_netrw or opts.hijack_netrw
_config.root_dirs = opts.root_dirs
_config.prefer_startup_root = opts.prefer_startup_root
_config.update_focused_file = opts.update_focused_file
_config.hijack_directories = opts.hijack_directories
_config.hijack_directories.enable = _config.hijack_directories.enable and netrw_disabled
manage_netrw(opts.disable_netrw, opts.hijack_netrw)
M.config = opts
require("nvim-tree.notify").setup(opts)
require("nvim-tree.log").setup(opts)
if log.enabled "config" then
-- optionally log the configuration
if log.enabled("config") then
log.line("config", "default config + user")
log.raw("config", "%s\n", vim.inspect(opts))
log.raw("config", "%s\n", vim.inspect(config.g))
end
require("nvim-tree.actions").setup(opts)
require("nvim-tree.keymap").setup(opts)
require("nvim-tree.appearance").setup()
require("nvim-tree.diagnostics").setup(opts)
require("nvim-tree.explorer").setup(opts)
require("nvim-tree.git").setup(opts)
require("nvim-tree.git.utils").setup(opts)
require("nvim-tree.view").setup(opts)
require("nvim-tree.lib").setup(opts)
require("nvim-tree.renderer.components").setup(opts)
require("nvim-tree.buffers").setup(opts)
require("nvim-tree.help").setup(opts)
require("nvim-tree.watcher").setup(opts)
if M.config.renderer.icons.show.file and pcall(require, "nvim-web-devicons") then
require("nvim-web-devicons").setup()
-- idempotent highlight definition
appearance.highlight()
-- set the initial view state based on config
view_state.initialize()
-- idempotent au (re)definition
autocmd.global()
-- subsequent calls to setup clear all state
if vim.g.NvimTreeSetup == 1 then
require("nvim-tree.core").purge_all_state()
end
setup_autocommands(opts)
if vim.g.NvimTreeSetup ~= 1 then
-- first call to setup
require("nvim-tree.commands").setup()
else
-- subsequent calls to setup
M.purge_all_state()
end
-- hydrate post setup API
api_impl.hydrate_post_setup(api)
vim.g.NvimTreeSetup = 1
vim.api.nvim_exec_autocmds("User", { pattern = "NvimTreeSetup" })

View File

@@ -0,0 +1,11 @@
---@meta
local nvim_tree = { api = { appearance = {} } }
---
---Open a new buffer displaying all nvim-tree highlight groups, their link chain and concrete definition.
---
---Similar to `:so $VIMRUNTIME/syntax/hitest.vim` as per |:highlight|
---
function nvim_tree.api.appearance.hi_test() end
return nvim_tree.api.appearance

View File

@@ -0,0 +1,20 @@
---@meta
local nvim_tree = { api = { commands = {} } }
---
---@class nvim_tree.api.commands.Command
---@inlinedoc
---
---@field name string name of the `:NvimTree*` command
---@field command fun(args: vim.api.keyset.create_user_command.command_args) function that the command will execute
---@field opts vim.api.keyset.user_command [command-attributes]
---
---Retrieve all [nvim-tree-commands]
---
---They have been created via [nvim_create_user_command()], see also [lua-guide-commands-create]
---
---@return nvim_tree.api.commands.Command[]
function nvim_tree.api.commands.get() end
return nvim_tree.api.commands

View File

@@ -0,0 +1,33 @@
---@meta
local nvim_tree = { api = { config = {} } }
---
---Default nvim-tree config.
---
---@return nvim_tree.config immutable deep clone
function nvim_tree.api.config.default() end
---
---Global current nvim-tree config.
---
---@return nvim_tree.config immutable deep clone
function nvim_tree.api.config.global() end
---
---Reference to config passed to [nvim-tree-setup]
---
---@return nvim_tree.config? nil when no config passed to setup
function nvim_tree.api.config.user() end
nvim_tree.api.config.mappings = {}
---@deprecated use `nvim_tree.api.map.keymap.current()`
function nvim_tree.api.config.mappings.get_keymap() end
---@deprecated use `nvim_tree.api.map.keymap.default()`
function nvim_tree.api.config.mappings.get_keymap_default() end
---@deprecated use `nvim_tree.api.map.on_attach.default()`
function nvim_tree.api.config.mappings.default_on_attach(bufnr) end
return nvim_tree.api.config

View File

@@ -0,0 +1,103 @@
---@meta
---@brief
---Highlighting and icons for nodes are provided by Decorators, see [nvim-tree-icons-highlighting] for an overview.
---
---Decorators are rendered in [nvim_tree.config.renderer] {decorators} order of additive precedence, with later decorators applying additively over earlier.
---
---You may provide your own in addition to the builtin decorators, see |nvim-tree-class-decorator-example|.
---
---Decorators may:
---- Add icons
---- Set a highlight group name for the name or icons
---- Override node icon
---
---To register your decorator:
---- Create a class that extends [nvim_tree.api.Decorator]
---- Register it by adding the class to [nvim_tree.config.renderer] {decorators}
---
---Your decorator will be constructed and executed each time the tree is rendered.
---
---Your class must:
---- [nvim_tree.Class:extend()] the interface [nvim_tree.api.Decorator]
---- Provide a no-arguments constructor [nvim_tree.Class:new()] that sets the mandatory fields:
--- - {enabled}
--- - {highlight_range}
--- - {icon_placement}
---- Call [nvim_tree.api.Decorator:define_sign()] in your constructor when {icon_placement} is `"signcolumn"`
---
---Your class may:
---- Implement methods to provide decorations:
--- - [nvim_tree.api.Decorator:highlight_group()]
--- - [nvim_tree.api.Decorator:icon_node()]
--- - [nvim_tree.api.Decorator:icons()]
local nvim_tree = { api = {} }
local Class = require("nvim-tree.classic")
---
---Text or glyphs with optional highlight group names to apply to it.
---
---@class nvim_tree.api.highlighted_string
---
---One or many glyphs/characters.
---@field str string
---
---Highlight group names to apply in order. Empty table for no highlighting.
---@field hl string[]
---
---Decorator interface
---
---@class nvim_tree.api.Decorator: nvim_tree.Class
---
---Enable this decorator.
---@field enabled boolean
---
---What to highlight: [nvim_tree.config.renderer.highlight]
---@field highlight_range nvim_tree.config.renderer.highlight
---
---Where to place the icons: [nvim_tree.config.renderer.icons.placement]
---@field icon_placement "none"|nvim_tree.config.renderer.icons.placement
---
local Decorator = Class:extend()
nvim_tree.api.Decorator = Decorator
---
---Icon to override for the node.
---
---Abstract, optional to implement.
---
---@param node nvim_tree.api.Node
---@return nvim_tree.api.highlighted_string? icon `nil` for no override
function Decorator:icon_node(node) end
---
---Icons to add to the node as per {icon_placement}
---
---Abstract, optional to implement.
---
---@param node nvim_tree.api.Node
---@return nvim_tree.api.highlighted_string[]? icons `nil` or empty table for no icons. Only the first glyph of {str} is used when {icon_placement} is `"signcolumn"`
function Decorator:icons(node) end
---
---One highlight group that applies additively to the {node} name for {highlight_range}.
---
---Abstract, optional to implement.
---
---@param node nvim_tree.api.Node
---@return string? highlight group name `nil` when no highlighting to apply to the node
function Decorator:highlight_group(node) end
---
---Defines a sign for an icon. This is mandatory and necessary only when {icon_placement} is `"signcolumn"`
---
---This must be called during your constructor for all icons that you will return from [nvim_tree.api.Decorator:icons()]
---
---@param icon nvim_tree.api.highlighted_string? does nothing if nil
function Decorator:define_sign(icon) end
return nvim_tree.api.Decorator

View File

@@ -0,0 +1,96 @@
---@meta
error("Cannot require a meta file")
---@brief
---
---A decorator class for nodes named "example", overriding all builtin decorators except for Cut.
---- Highlights node name with `IncSearch`
---- Creates two icons `"1"` and `"2"` placed after the node name, highlighted with `DiffAdd` and `DiffText`
---- Replaces the node icon with `"N"`, highlighted with `Error `
---
---Create a class file `~/.config/nvim/lua/my-decorator.lua`
---
---Require and register it during |nvim-tree-setup|:
---```lua
---
--- local MyDecorator = require("my-decorator")
---
--- require("nvim-tree").setup({
--- renderer = {
--- decorators = {
--- "Git",
--- "Open",
--- "Hidden",
--- "Modified",
--- "Bookmark",
--- "Diagnostics",
--- "Copied",
--- MyDecorator,
--- "Cut",
--- },
--- },
--- })
---```
---Contents of `my-decorator.lua`:
---```lua
---
--- ---@class (exact) MyDecorator: nvim_tree.api.Decorator
--- ---@field private my_icon1 nvim_tree.api.highlighted_string
--- ---@field private my_icon2 nvim_tree.api.highlighted_string
--- ---@field private my_icon_node nvim_tree.api.highlighted_string
--- ---@field private my_highlight_group string
--- local MyDecorator = require("nvim-tree.api").Decorator:extend()
---
--- ---Mandatory constructor :new() will be called once per tree render, with no arguments.
--- function MyDecorator:new()
--- self.enabled = true
--- self.highlight_range = "name"
--- self.icon_placement = "after"
---
--- -- create your icons and highlights once, applied to every node
--- self.my_icon1 = { str = "1", hl = { "DiffAdd" } }
--- self.my_icon2 = { str = "2", hl = { "DiffText" } }
--- self.my_icon_node = { str = "N", hl = { "Error" } }
--- self.my_highlight_group = "IncSearch"
---
--- -- Define the icon signs only once
--- -- Only needed if you are using icon_placement = "signcolumn"
--- -- self:define_sign(self.my_icon1)
--- -- self:define_sign(self.my_icon2)
--- end
---
--- ---Override node icon
--- ---@param node nvim_tree.api.Node
--- ---@return nvim_tree.api.highlighted_string? icon_node
--- function MyDecorator:icon_node(node)
--- if node.name == "example" then
--- return self.my_icon_node
--- else
--- return nil
--- end
--- end
---
--- ---Return two icons for DecoratorIconPlacement "after"
--- ---@param node nvim_tree.api.Node
--- ---@return nvim_tree.api.highlighted_string[]? icons
--- function MyDecorator:icons(node)
--- if node.name == "example" then
--- return { self.my_icon1, self.my_icon2, }
--- else
--- return nil
--- end
--- end
---
--- ---Exactly one highlight group for DecoratorHighlightRange "name"
--- ---@param node nvim_tree.api.Node
--- ---@return string? highlight_group
--- function MyDecorator:highlight_group(node)
--- if node.name == "example" then
--- return self.my_highlight_group
--- else
--- return nil
--- end
--- end
---
--- return MyDecorator
---```

View File

@@ -0,0 +1,26 @@
---@meta
-- Deprecated top level API modules.
-- Remember to add mappings in legacy.lua `api_map`
local nvim_tree = { api = {} }
nvim_tree.api.live_filter = {}
---@deprecated use `nvim_tree.api.filter.live.start()`
function nvim_tree.api.live_filter.start() end
---@deprecated use `nvim_tree.api.filter.live.clear()`
function nvim_tree.api.live_filter.clear() end
nvim_tree.api.diagnostics = {}
---@deprecated use `nvim_tree.api.appearance.hi_test()`
function nvim_tree.api.diagnostics.hi_test() end
nvim_tree.api.decorator = {}
---@class nvim_tree.api.decorator.UserDecorator: nvim_tree.api.Decorator
---@deprecated use `nvim_tree.api.Decorator`
nvim_tree.api.decorator.UserDecorator = nvim_tree.api.Decorator
return nvim_tree.api

View File

@@ -0,0 +1,30 @@
---@meta
local nvim_tree = { api = { events = {} } }
---@enum nvim_tree.api.events.Event
nvim_tree.api.events.Event = {
Ready = "Ready",
WillRenameNode = "WillRenameNode",
NodeRenamed = "NodeRenamed",
TreePreOpen = "TreePreOpen",
TreeOpen = "TreeOpen",
TreeClose = "TreeClose",
WillCreateFile = "WillCreateFile",
FileCreated = "FileCreated",
WillRemoveFile = "WillRemoveFile",
FileRemoved = "FileRemoved",
FolderCreated = "FolderCreated",
FolderRemoved = "FolderRemoved",
Resize = "Resize",
TreeAttachedPost = "TreeAttachedPost",
TreeRendered = "TreeRendered",
}
---
---Register a handler for an event, see [nvim-tree-events].
---
---@param event_type nvim_tree.api.events.Event [nvim_tree_events_kind]
---@param callback fun(payload: table?)
function nvim_tree.api.events.subscribe(event_type, callback) end
return nvim_tree.api.events

View File

@@ -0,0 +1,65 @@
---@meta
local nvim_tree = { api = { filter = {} } }
---
---Toggle [nvim_tree.config.filters] {enable} which applies to ALL filters.
---
function nvim_tree.api.filter.toggle() end
nvim_tree.api.filter.live = {}
---
---Enter live filter mode. Opens an input window with [filetype] `NvimTreeFilter`
---
function nvim_tree.api.filter.live.start() end
---
---Exit live filter mode.
---
function nvim_tree.api.filter.live.clear() end
nvim_tree.api.filter.git = {}
nvim_tree.api.filter.git.clean = {}
---
---Toggle [nvim_tree.config.filters] {git_clean} filter.
---
function nvim_tree.api.filter.git.clean.toggle() end
nvim_tree.api.filter.git.ignored = {}
---
---Toggle [nvim_tree.config.filters] {git_ignored} filter.
---
function nvim_tree.api.filter.git.ignored.toggle() end
nvim_tree.api.filter.dotfiles = {}
---
---Toggle [nvim_tree.config.filters] {dotfiles} filter.
---
function nvim_tree.api.filter.dotfiles.toggle() end
nvim_tree.api.filter.no_buffer = {}
---
---Toggle [nvim_tree.config.filters] {no_buffer} filter.
---
function nvim_tree.api.filter.no_buffer.toggle() end
nvim_tree.api.filter.no_bookmark = {}
---
---Toggle [nvim_tree.config.filters] {no_bookmark} filter.
---
function nvim_tree.api.filter.no_bookmark.toggle() end
nvim_tree.api.filter.custom = {}
---
---Toggle [nvim_tree.config.filters] {custom} filter.
---
function nvim_tree.api.filter.custom.toggle() end
return nvim_tree.api.filter

View File

@@ -0,0 +1,130 @@
---@meta
local nvim_tree = { api = { fs = {} } }
---
---Clear the nvim-tree clipboard.
---
function nvim_tree.api.fs.clear_clipboard() end
nvim_tree.api.fs.copy = {}
---
---Copy the absolute path to the system clipboard.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.copy.absolute_path(node) end
---
---Copy the name with extension omitted to the system clipboard.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.copy.basename(node) end
---
---Copy the name to the system clipboard.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.copy.filename(node) end
---
---Copy to the nvim-tree clipboard.
---In visual mode, copies all nodes in the visual selection.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.copy.node(node) end
---
---Copy the path relative to the tree root to the system clipboard.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.copy.relative_path(node) end
---
---Prompt to create a file or directory.
---
---When {node} is a file it will be created in the parent directory.
---
---Use a trailing `"/"` to create a directory e.g. `"foo/"`
---
---Multiple directories/files may be created e.g. `"foo/bar/baz"`
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.create(node) end
---
---Cut to the nvim-tree clipboard.
---In visual mode, cuts all nodes in the visual selection.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.cut(node) end
---
---Paste files from:
---- current nvim-tree clipboard (if present)
---- another nvim-tree instance via system clipboard
---
---If {node} is a file it will pasted in the parent directory.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.paste(node) end
---
---Paste while cutting files from:
---- current nvim-tree clipboard (if present)
---- another nvim-tree instance via system clipboard
---
---If {node} is a file it will pasted in the parent directory.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.move(node) end
---
---Print the contents of the nvim-tree clipboard.
---
function nvim_tree.api.fs.print_clipboard() end
---
---Delete from the file system.
---In visual mode, deletes all nodes in the visual selection with a single prompt.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.remove(node) end
---
---Prompt to rename by name.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.rename(node) end
---
---Prompt to rename by name with extension omitted.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.rename_basename(node) end
---
---Prompt to rename by absolute path.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.rename_full(node) end
---
---Prompt to rename.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.rename_node(node) end
---
---Prompt to rename by absolute path with name omitted.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.rename_sub(node) end
---
---Trash as per |nvim_tree.config.trash|.
---In visual mode, trashes all nodes in the visual selection with a single prompt.
---
---@param node? nvim_tree.api.Node
function nvim_tree.api.fs.trash(node) end
return nvim_tree.api.fs

View File

@@ -0,0 +1,9 @@
---@meta
local nvim_tree = { api = { git = {} } }
---
---Update the git status of the entire tree.
---
function nvim_tree.api.git.reload() end
return nvim_tree.api.git

View File

@@ -0,0 +1,26 @@
---@meta
local nvim_tree = { api = { map = {} } }
nvim_tree.api.map.keymap = {}
---
---Retrieve all buffer local mappings for nvim-tree. These are the mappings that are applied by [nvim_tree.config] {on_attach}, which may include default mappings.
---
---@return vim.api.keyset.get_keymap[]
function nvim_tree.api.map.keymap.current() end
---
--- Retrieves the buffer local mappings for nvim-tree that are applied by [nvim_tree.api.map.on_attach.default()]
---
---@return vim.api.keyset.get_keymap[]
function nvim_tree.api.map.keymap.default() end
nvim_tree.api.map.on_attach = {}
---
---Apply all [nvim-tree-mappings-default]. Call from your [nvim_tree.config] {on_attach}.
---
---@param bufnr integer use the `bufnr` passed to {on_attach}
function nvim_tree.api.map.on_attach.default(bufnr) end
return nvim_tree.api.map

View File

@@ -0,0 +1,63 @@
---@meta
local nvim_tree = { api = { marks = {} } }
---
---Return the node if it is marked.
---
---@param node? nvim_tree.api.Node file or directory
---@return nvim_tree.api.Node?
function nvim_tree.api.marks.get(node) end
---
---Retrieve all marked nodes.
---
---@return nvim_tree.api.Node[]
function nvim_tree.api.marks.list() end
---
---Toggle mark. In visual mode, toggles all nodes in the visual selection.
---
---@param node? nvim_tree.api.Node file or directory
function nvim_tree.api.marks.toggle(node) end
---
---Clear all marks.
---
function nvim_tree.api.marks.clear() end
nvim_tree.api.marks.bulk = {}
---
---Delete all marked, prompting if [nvim_tree.config.ui.confirm] {remove}
---
function nvim_tree.api.marks.bulk.delete() end
---
---Delete all marked, prompting if [nvim_tree.config.ui.confirm] {trash}
---
function nvim_tree.api.marks.bulk.trash() end
---
---Prompts for a directory to move all marked nodes into.
---
function nvim_tree.api.marks.bulk.move() end
nvim_tree.api.marks.navigate = {}
---
---Navigate to the next marked node, wraps.
---
function nvim_tree.api.marks.navigate.next() end
---
---Navigate to the previous marked node, wraps.
---
function nvim_tree.api.marks.navigate.prev() end
---
---Prompts for selection of a marked node, sorted by absolute paths.
---A folder will be focused, a file will be opened.
---
function nvim_tree.api.marks.navigate.select() end
return nvim_tree.api.marks

View File

@@ -0,0 +1,299 @@
---@meta
local nvim_tree = { api = { node = {} } }
nvim_tree.api.node.open = {}
---
---@class nvim_tree.api.node.open.Opts
---@inlinedoc
---
---Quits the tree when opening the file.
---(default: false)
---@field quit_on_open? boolean
---
---Keep focus in the tree when opening the file.
---(default: false)
---@field focus? boolean
---
---Switch to window with selected file if it exists, otherwise open, see [:drop]
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.open.drop(node) end
---
---- file: open as per [nvim_tree.config.actions.open_file]
---- directory: expand or collapse
---- root: change directory up
---
---@param node? nvim_tree.api.Node directory or file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.edit(node, opts) end
---
---Open file in a new horizontal split.
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.horizontal(node, opts) end
---
---Open file in a new horizontal split without using the window picker.
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.horizontal_no_picker(node, opts) end
---
---Open file without using the window picker.
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.no_window_picker(node, opts) end
---
---Open file with ['bufhidden'] set to `delete`.
---
---@param node? nvim_tree.api.Node directory or file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.preview(node, opts) end
---
---Open file with ['bufhidden'] set to `delete` without using the window picker.
---
---@param node? nvim_tree.api.Node directory or file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.preview_no_picker(node, opts) end
---
---Open file in place: in the nvim-tree window.
---
---@param node? nvim_tree.api.Node file
function nvim_tree.api.node.open.replace_tree_buffer(node) end
---
---Open file in a new tab.
---
---@param node? nvim_tree.api.Node directory or file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.tab(node, opts) end
---
---Switch to tab containing window with selected file if it exists, otherwise open in new tab, see [:drop]
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.open.tab_drop(node) end
---
---Toggle [nvim_tree.config.renderer] {group_empty} for a directory. Needs {group_empty} set.
---
---@param node? nvim_tree.api.Node directory
function nvim_tree.api.node.open.toggle_group_empty(node) end
---
---Open file in a new vertical split.
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.vertical(node, opts) end
---
---Open file in a new vertical split without using the window picker.
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.open.Opts optional
function nvim_tree.api.node.open.vertical_no_picker(node, opts) end
---
---@class nvim_tree.api.node.buffer.RemoveOpts
---@inlinedoc
---
---Proceed even if the buffer is modified.
---(default: false)
---@field force? boolean
nvim_tree.api.node.buffer = {}
---
---Deletes node's related buffer, if one exists. Executes [:bdelete] or [:bdelete]!
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.buffer.RemoveOpts
function nvim_tree.api.node.buffer.delete(node, opts) end
---
---Wipes node's related buffer, if one exists. Executes [:bwipe] or [:bwipe]!
---
---@param node? nvim_tree.api.Node file
---@param opts? nvim_tree.api.node.buffer.RemoveOpts optional
function nvim_tree.api.node.buffer.wipe(node, opts) end
---
---Collapse the tree under a directory or a file's parent directory.
---
---@param node? nvim_tree.api.Node directory or file
---@param opts? nvim_tree.api.node.collapse.Opts optional
function nvim_tree.api.node.collapse(node, opts) end
---@class nvim_tree.api.node.collapse.Opts
---@inlinedoc
---
---Do not collapse nodes with open buffers.
---(default: false)
---@field keep_buffers? boolean
---
---Recursively expand all nodes under a directory or a file's parent directory.
---
---@param node? nvim_tree.api.Node directory or file
---@param opts? nvim_tree.api.node.expand.Opts optional
function nvim_tree.api.node.expand(node, opts) end
---@class nvim_tree.api.node.expand.Opts
---@inlinedoc
---
---Return `true` if `node` should be expanded. `expansion_count` is the total number of folders expanded.
---@field expand_until? fun(expansion_count: integer, node: Node): boolean
nvim_tree.api.node.navigate = {}
---
---Navigate to the parent directory of the node.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.parent(node) end
---
---Navigate to the parent directory of the node, closing it.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.parent_close(node) end
nvim_tree.api.node.navigate.diagnostics = {}
---
---Navigate to the next item showing diagnostic status.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.diagnostics.next(node) end
---
---Navigate to the next item showing diagnostic status, recursively. Needs [nvim_tree.config.diagnostics] {show_on_dirs}
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.diagnostics.next_recursive(node) end
---
---Navigate to the previous item showing diagnostic status.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.diagnostics.prev(node) end
---
---Navigate to the previous item showing diagnostic status, recursively. Needs [nvim_tree.config.diagnostics] {show_on_dirs}
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.diagnostics.prev_recursive(node) end
nvim_tree.api.node.navigate.git = {}
---
---Navigate to the next item showing git status.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.git.next(node) end
---
---Navigate to the next item showing git status, recursively. Needs [nvim_tree.config.git] {show_on_dirs}
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.git.next_recursive(node) end
---
---Navigate to the next item showing git status, skipping `.gitignore`
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.git.next_skip_gitignored(node) end
---
---Navigate to the previous item showing git status.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.git.prev(node) end
---
---Navigate to the previous item showing git status, recursively. Needs [nvim_tree.config.git] {show_on_dirs}
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.git.prev_recursive(node) end
---
---Navigate to the previous item showing git status, skipping `.gitignore`
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.git.prev_skip_gitignored(node) end
nvim_tree.api.node.navigate.opened = {}
---
---Navigate to the next [bufloaded()] file.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.opened.next(node) end
---
---Navigate to the previous [bufloaded()] file.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.opened.prev(node) end
nvim_tree.api.node.navigate.sibling = {}
---
---Navigate to the first node in the current node's folder.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.sibling.first(node) end
---
---Navigate to the last node in the current node's folder.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.sibling.last(node) end
---
---Navigate to the next node in the current node's folder, wraps.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.sibling.next(node) end
---
---Navigate to the previous node in the current node's folder, wraps.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.navigate.sibling.prev(node) end
nvim_tree.api.node.run = {}
---
---Enter [cmdline] with the full path of the node and the cursor at the start of the line.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.run.cmd(node) end
---
---Open with the system default handler: [vim.ui.open()].
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.run.system(node) end
---
---Open a popup window showing: fullpath, size, accessed, modified, created.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.node.show_info_popup(node) end
return nvim_tree.api.node

View File

@@ -0,0 +1,253 @@
---@meta
local nvim_tree = { api = { tree = {} } }
---
---Open the tree, focusing it if already open.
---
---@param opts? nvim_tree.api.tree.open.Opts optional
function nvim_tree.api.tree.open(opts) end
---@class nvim_tree.api.tree.open.Opts
---@inlinedoc
---
---Root directory for the tree
---@field path? string
---
---Open the tree in the current window
---(default: false)
---@field current_window? boolean
---
---Open the tree in the specified [window-ID], overrides {current_window}
---@field winid? integer
---
---Find the current buffer.
---(default: false)
---@field find_file? boolean
---
---Update root following {find_file}, see [nvim_tree.config.update_focused_file] {update_root}
---(default: false)
---@field update_root? boolean
---
---Open or close the tree.
---
---@param opts? nvim_tree.api.tree.toggle.Opts optional
function nvim_tree.api.tree.toggle(opts) end
---@class nvim_tree.api.tree.toggle.Opts
---@inlinedoc
---
---Root directory for the tree
---@field path? string
---
---Open the tree in the current window
---(default: false)
---@field current_window? boolean
---
---Open the tree in the specified [window-ID], overrides {current_window}
---@field winid? integer
---
---Find the current buffer.
---(default: false)
---@field find_file? boolean
---
---Update root following {find_file}, see [nvim_tree.config.update_focused_file] {update_root}
---(default: false)
---@field update_root? boolean
---
---Focus the tree when opening.
---(default: true)
---@field focus? boolean
---
---Close the tree, affecting all tabs as per [nvim_tree.config.tab.sync] {close}
---
function nvim_tree.api.tree.close() end
---
---Close the tree in this tab only.
---
function nvim_tree.api.tree.close_in_this_tab() end
---
---Close the tree in all tabs.
---
function nvim_tree.api.tree.close_in_all_tabs() end
---
---Focus the tree, opening it if necessary. Retained for compatibility, use [nvim_tree.api.tree.open()] with no arguments instead.
---
function nvim_tree.api.tree.focus() end
---
---Refresh the tree. Does nothing if closed.
---
function nvim_tree.api.tree.reload() end
---
---Resize the tree, persisting the new size. Resets to [nvim_tree.config.view] {width} when no {opts} provided.
---
---Only one option is supported, priority order: {width}, {absolute}, {relative}.
---
---{absolute} and {relative} do nothing when [nvim_tree.config.view] {width} is a function.
---
---@param opts? nvim_tree.api.tree.resize.Opts optional
function nvim_tree.api.tree.resize(opts) end
---@class nvim_tree.api.tree.resize.Opts
---@inlinedoc
---
---New [nvim_tree.config.view] {width} value.
---@field width? nvim_tree.config.view.width.spec|nvim_tree.config.view.width
---
---Set the width.
---@field absolute? integer
---
---Increase or decrease the width.
---@field relative? integer
---
---Change the tree's root to a path.
---
---@param path? string absolute or relative path.
function nvim_tree.api.tree.change_root(path) end
---
---Change the tree's root to a folder node or the parent of a file node.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.tree.change_root_to_node(node) end
---
---Change the tree's root to the parent of a node.
---
---@param node? nvim_tree.api.Node directory or file
function nvim_tree.api.tree.change_root_to_parent(node) end
---
---Retrieve the currently focused node.
---
---@return nvim_tree.api.Node? nil if tree is not visible.
function nvim_tree.api.tree.get_node_under_cursor() end
---
---Retrieve a hierarchical list of all the nodes.
---
---@return nvim_tree.api.Node[]
function nvim_tree.api.tree.get_nodes() end
---
---Find and focus a file or folder in the tree. Finds current buffer unless otherwise specified.
---
---@param opts? nvim_tree.api.tree.find_file.Opts optional
function nvim_tree.api.tree.find_file(opts) end
---@class nvim_tree.api.tree.find_file.Opts
---@inlinedoc
---
---Absolute/relative path OR `bufnr` to find.
---@field buf? string|integer
---
---Open the tree if necessary.
---(default: false)
---@field open? boolean
---
---Requires {open}: open in the current window.
---(default: false)
---@field current_window? boolean
---
---Open the tree in the specified [window-ID], overrides {current_window}
---@field winid? integer
---
---Update root after find, see [nvim_tree.config.update_focused_file] {update_root}
---(default: false)
---@field update_root? boolean
---
---Focus the tree window.
---(default: false)
---@field focus? boolean
---
---Open the search dialogue.
---
function nvim_tree.api.tree.search_node() end
---
---Collapse the tree.
---
---@param opts? nvim_tree.api.node.collapse.Opts optional
function nvim_tree.api.tree.collapse_all(opts) end
---
---Recursively expand all nodes under the tree root or specified folder.
---
---@param node? nvim_tree.api.Node directory
---@param opts? nvim_tree.api.node.expand.Opts optional
function nvim_tree.api.tree.expand_all(node, opts) end
---
---Toggle help view.
---
function nvim_tree.api.tree.toggle_help() end
---
---Checks if a buffer is an nvim-tree.
---
---@param bufnr? integer 0 or nil for current buffer.
---
---@return boolean
function nvim_tree.api.tree.is_tree_buf(bufnr) end
---
---Checks if nvim-tree is visible on the current, specified or any tab.
---
---@param opts? nvim_tree.api.tree.is_visible.Opts optional
---@return boolean
function nvim_tree.api.tree.is_visible(opts) end
---@class nvim_tree.api.tree.is_visible.Opts
---@inlinedoc
---
--- [tab-ID] 0 or nil for current.
---@field tabpage? integer
---
---Visible on any tab.
---(default: false)
---@field any_tabpage? boolean
---
---Retrieve the window of the open tree.
---
---@param opts? nvim_tree.api.tree.winid.Opts optional
---@return integer? [window-ID], nil if tree is not visible.
function nvim_tree.api.tree.winid(opts) end
---@class nvim_tree.api.tree.winid.Opts
---@inlinedoc
---
---[tab-ID] 0 or nil for current.
---@field tabpage? integer
---@deprecated use `nvim_tree.api.filter.toggle()`
function nvim_tree.api.tree.toggle_enable_filters() end
---@deprecated use `nvim_tree.api.filter.git.ignored.toggle()`
function nvim_tree.api.tree.toggle_gitignore_filter() end
---@deprecated use `nvim_tree.api.filter.git.clean.toggle()`
function nvim_tree.api.tree.toggle_git_clean_filter() end
---@deprecated use `nvim_tree.api.filter.no_buffer.toggle()`
function nvim_tree.api.tree.toggle_no_buffer_filter() end
---@deprecated use `nvim_tree.api.filter.custom.toggle()`
function nvim_tree.api.tree.toggle_custom_filter() end
---@deprecated use `nvim_tree.api.filter.dotfiles.toggle()`
function nvim_tree.api.tree.toggle_hidden_filter() end
---@deprecated use `nvim_tree.api.filter.no_bookmark.toggle()`
function nvim_tree.api.tree.toggle_no_bookmark_filter() end
return nvim_tree.api.tree

View File

@@ -0,0 +1,38 @@
---@meta
error("Cannot require a meta file")
---
---File
---
---@class (exact) nvim_tree.api.FileNode: nvim_tree.api.Node
---@field extension string
---
---Directory
---
---@class (exact) nvim_tree.api.DirectoryNode: nvim_tree.api.Node
---@field has_children boolean
---@field nodes nvim_tree.api.Node[]
---@field open boolean
---
---Root Directory
---
---@class (exact) nvim_tree.api.RootNode: nvim_tree.api.DirectoryNode
---
---Link mixin
---
---@class (exact) nvim_tree.api.LinkNode
---@field link_to string
---@field fs_stat_target uv.fs_stat.result
---
---File Link
---
---@class (exact) nvim_tree.api.FileLinkNode: nvim_tree.api.FileNode, nvim_tree.api.LinkNode
---
---DirectoryLink
---
---@class (exact) nvim_tree.api.DirectoryLinkNode: nvim_tree.api.DirectoryNode, nvim_tree.api.LinkNode

View File

@@ -0,0 +1,133 @@
---@meta
error("Cannot require a meta file")
-- Root class {field}s are documented manually above "Fields:" as there is insufficent room for them in the column.
---Arguments to pass to [nvim-tree-setup].
---
---When a value is not present/nil, the default will be used.
---
---{on_attach} Runs when creating the nvim-tree buffer. Use this to set your [nvim-tree-mappings]. When not a function, [nvim-tree-mappings-default] will be used.
---
---{hijack_cursor} keep the cursor on the first letter of the filename when moving in the tree.
---
---{auto_reload_on_write} reload the explorer every time a buffer is written to.
---
---{disable_netrw} completely disables [netrw], see [nvim-tree-netrw] for details. It is strongly advised to eagerly disable netrw, due to race conditions at vim startup.
---
---{hijack_netrw} hijacks netrw windows, ignored when {disable_netrw}.
---
---{hijack_unnamed_buffer_when_opening} opens in place of the unnamed buffer if it's empty.
---
---{root_dirs} preferred root directories, requires [nvim_tree.config.update_focused_file.update_root].
---
---{prefer_startup_root} prefer startup root directory when updating root directory of the tree. Requires [nvim_tree.config.update_focused_file.update_root].
---
---{sync_root_with_cwd} changes the tree root directory on [DirChanged] and refreshes the tree.
---
---{reload_on_bufenter} automatically reloads the tree on [BufEnter] nvim-tree.
---
---{respect_buf_cwd} changes the [current-directory] of nvim-tree to that of new buffer's when opening nvim-tree.
---
---{select_prompts} uses [vim.ui.select()] style prompts. Necessary when using a UI prompt decorator such as dressing.nvim or telescope-ui-select.nvim
---@class nvim_tree.config
---
---(default: `default`)
---@field on_attach? "default"|(fun(bufnr: integer))
---
---(default: `false`)
---@field hijack_cursor? boolean
---
---(default: `true`)
---@field auto_reload_on_write? boolean
---
---(default: `false`)
---@field disable_netrw? boolean
---
---(default: `true`)
---@field hijack_netrw? boolean
---
---(default: `false`)
---@field hijack_unnamed_buffer_when_opening? boolean
---
---(default: `{}`)
---@field root_dirs? string[]
---
---(default: `false`)
---@field prefer_startup_root? boolean
---
---(default: `false`)
---@field sync_root_with_cwd? boolean
---
---(default: `false`)
---@field reload_on_bufenter? boolean
---
---(default: `false`)
---@field respect_buf_cwd? boolean
---
---(default: `false`)
---@field select_prompts? boolean
---
---[nvim_tree.config.sort]
---@field sort? nvim_tree.config.sort
---
---[nvim_tree.config.view]
---@field view? nvim_tree.config.view
---
---[nvim_tree.config.renderer]
---@field renderer? nvim_tree.config.renderer
---
---[nvim_tree.config.hijack_directories]
---@field hijack_directories? nvim_tree.config.hijack_directories
---
---[nvim_tree.config.update_focused_file]
---@field update_focused_file? nvim_tree.config.update_focused_file
---
---[nvim_tree.config.git]
---@field git? nvim_tree.config.git
---
---[nvim_tree.config.diagnostics]
---@field diagnostics? nvim_tree.config.diagnostics
---
---[nvim_tree.config.modified]
---@field modified? nvim_tree.config.modified
---
---[nvim_tree.config.filters]
---@field filters? nvim_tree.config.filters
---
---[nvim_tree.config.live_filter]
---@field live_filter? nvim_tree.config.live_filter
---
---[nvim_tree.config.filesystem_watchers]
---@field filesystem_watchers? nvim_tree.config.filesystem_watchers
---
---[nvim_tree.config.actions]
---@field actions? nvim_tree.config.actions
---
---[nvim_tree.config.trash]
---@field trash? nvim_tree.config.trash
---
---[nvim_tree.config.tab]
---@field tab? nvim_tree.config.tab
---
---[nvim_tree.config.bookmarks]
---@field bookmarks? nvim_tree.config.bookmarks
---
---[nvim_tree.config.notify]
---@field notify? nvim_tree.config.notify
---
---[nvim_tree.config.help]
---@field help? nvim_tree.config.help
---
---[nvim_tree.config.ui]
---@field ui? nvim_tree.config.ui
---
---[nvim_tree.config.experimental]
---@field experimental? nvim_tree.config.experimental
---
---[nvim_tree.config.log]
---@field log? nvim_tree.config.log

View File

@@ -0,0 +1,140 @@
---@meta
error("Cannot require a meta file")
---@class nvim_tree.config.actions
---
---Use the system clipboard for copy/paste. Copied text will be stored in registers `+` (system), otherwise, it will be stored in `1` and `"`
---(default: `true`)
---@field use_system_clipboard? boolean
---
---[nvim_tree.config.actions.change_dir]
---@field change_dir? nvim_tree.config.actions.change_dir
---
---[nvim_tree.config.actions.expand_all]
---@field expand_all? nvim_tree.config.actions.expand_all
---
---[nvim_tree.config.actions.file_popup]
---@field file_popup? nvim_tree.config.actions.file_popup
---
---[nvim_tree.config.actions.open_file]
---@field open_file? nvim_tree.config.actions.open_file
---
---[nvim_tree.config.actions.remove_file]
---@field remove_file? nvim_tree.config.actions.remove_file
--- vim [current-directory] behaviour
---@class nvim_tree.config.actions.change_dir
---
---Change the working directory when changing directories in the tree
---(default: `true`)
---@field enable? boolean
---
---Use `:cd` instead of `:lcd` when changing directories.
---(default: `false`)
---@field global? boolean
---
--- Restrict changing to a directory above the global cwd.
---(default: `false`)
---@field restrict_above_cwd? boolean
---Configure [nvim_tree.api.tree.expand_all()] and [nvim_tree.api.node.expand()]
---@class nvim_tree.config.actions.expand_all
---
---Limit the number of folders being explored when expanding every folder. Avoids hanging Nvim when running this action on very large folders.
---(default: `300`)
---@field max_folder_discovery? integer
---
---A list of directories that should not be expanded automatically e.g `{ ".git", "target", "build" }`
---(default: `{}`)
---@field exclude? string[]
---{file_popup} floating window.
---
---{open_win_config} is passed to [nvim_open_win()], default:
---```lua
--- {
--- col = 1,
--- row = 1,
--- relative = "cursor",
--- border = "shadow",
--- style = "minimal",
--- }
---```
---You shouldn't define {width} and {height} values here. They will be overridden to fit the file_popup content.
---@class nvim_tree.config.actions.file_popup
---
---(default: `{ col = 1, row = 1, relative = "cursor", border = "shadow", style = "minimal", }`)
---@field open_win_config? vim.api.keyset.win_config
---Opening files.
---@class nvim_tree.config.actions.open_file
---
---Closes the explorer when opening a file
---(default: `false`)
---@field quit_on_open? boolean
---
---Prevent new opened file from opening in the same window as the tree.
---(default: `true`)
---@field eject? boolean
---
---Resizes the tree when opening a file
---(default: `true`)
---@field resize_window? boolean
---
---[nvim_tree.config.actions.open_file.window_picker]
---@field window_picker? nvim_tree.config.actions.open_file.window_picker
---A window picker will be shown when there are multiple windows available to open a file. It will show a single character identifier in each window's status line.
---
---When it is not enabled the file will open in the window from which you last opened the tree, obeying {exclude}
---
---You may define a {picker} function that should return the window id that will open the node, or `nil` if an invalid window is picked or user cancelled the action. The picker may create a new window.
---
---@class nvim_tree.config.actions.open_file.window_picker
---
---(default: `true`)
---@field enable? boolean
---
---Change the default window picker or define your own.
---(default: `"default"`)
---@field picker? "default"|(fun(): integer)
---
---Identifier characters to use.
---(default: `"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"`)
---@field chars? string
---
---[nvim_tree.config.actions.open_file.window_picker.exclude]
---@field exclude? nvim_tree.config.actions.open_file.window_picker.exclude
---Tables of buffer option names mapped to a list of option values. Windows containing matching buffers will not be:
--- - available when using a window picker
--- - selected when not using a window picker
---@class nvim_tree.config.actions.open_file.window_picker.exclude
---
---(default: `{ "notify", "lazy", "qf", "diff", "fugitive", "fugitiveblame", }`)
---@field filetype? string[]
---
---(default: `{ "nofile", "terminal", "help", }`)
---@field buftype? string[]
---Removing files.
---@class nvim_tree.config.actions.remove_file
---
---Close any window that displays a file when removing that file from the tree.
---(default: `true`)
---@field close_window? boolean

View File

@@ -0,0 +1,13 @@
---@meta
error("Cannot require a meta file")
---Optionally {persist} bookmarks to a json file:
---- `true` use default: `stdpath("data") .. "/nvim-tree-bookmarks.json"`
---- `false` do not persist
---- `string` absolute path of your choice
---
---@class nvim_tree.config.bookmarks
---
---(default: `false`)
---@field persist? boolean|string

View File

@@ -0,0 +1,10 @@
---@brief
---Following is the default configuration, see |nvim_tree.config| for details.
---
---```lua
---
--- ---@type nvim_tree.config
--- local config = {
--- config-default-injection-placeholder
--- }
---```

View File

@@ -0,0 +1,65 @@
---@meta
error("Cannot require a meta file")
---Integrate with [lsp] or COC diagnostics.
---
---See [nvim-tree-icons-highlighting].
---
---@class nvim_tree.config.diagnostics
---
---(default: `false`)
---@field enable? boolean
---
---Idle milliseconds between diagnostic event and tree update.
---(default: `500`)
---@field debounce_delay? integer
---
---Show diagnostic icons on parent directories.
---(default: `false`)
---@field show_on_dirs? boolean
---
---Show diagnostics icons on directories that are open. Requires {show_on_dirs}.
---(default: `true`)
---@field show_on_open_dirs? boolean
---
---Global [vim.diagnostic.Opts] overrides {severity} and {icons}
---(default: `false`)
---@field diagnostic_opts? boolean
---
---@field severity? nvim_tree.config.diagnostics.severity
---
---@field icons? nvim_tree.config.diagnostics.icons
---[nvim_tree.config.diagnostics.severity]()
---@class nvim_tree.config.diagnostics.severity
---@inlinedoc
---
---[vim.diagnostic.severity]
---(default: HINT)
---@field min? vim.diagnostic.Severity
---
---[vim.diagnostic.severity]
---(default: ERROR)
---@field max? vim.diagnostic.Severity
---[nvim_tree.config.diagnostics.icons]()
---@class nvim_tree.config.diagnostics.icons
---@inlinedoc
---
---(default: `""` )
---@field hint? string
---
---(default: `""` )
---@field info? string
---
---(default: `""` )
---@field warning? string
---
---(default: `""` )
---@field error? string

View File

@@ -0,0 +1,19 @@
---@meta
error("Cannot require a meta file")
---Experimental features that may become default or optional functionality.
---
---In the event of a problem please disable the experiment and raise an issue.
---
---@class nvim_tree.config.experimental
---
---Restore nvim-tree buffers when restoring vim sessions (requires 0.13+).
---(default: `false`)
---@field session_restore_nvim? boolean
--Example below for future reference:
--
--Buffers opened by nvim-tree will use with relative paths instead of absolute.
--(default: false)
--@field relative_path? boolean

View File

@@ -0,0 +1,47 @@
---@meta
error("Cannot require a meta file")
---Use file system watchers (libuv `uv_fs_event_t`) to monitor the filesystem for changes and update the tree.
---
---With this feature, the tree will be partially updated on specific directory changes, resulting in better performance.
---
---Blacklist watching absolute directory paths via {ignore_dirs} and optionally whitelist via {whitelist_dirs}:
---- A table of |regular-expression| that will be passed to `vim.fn.match`
--- - Backslashes must be escaped e.g. `"my-proj/\\.build$"`
--- - Literal backslashes must be double escaped to avoid |/magic| e.g. `"C:\\\\src\\\\my-proj"`
---- OR a `fun(path: string): boolean` that is passed an absolute path:
--- - {ignore_dirs}: return `true` to disable
--- - {whitelist_dirs}: return `true` to enable
---
---{whitelist_dirs} is STRONGLY recommended for windows: [nvim-tree-os-specific].
---
---After {max_events} consecutive filesystem events on a single directory with an interval < {debounce_delay}:
---- The filesystem watcher will be disabled for that directory.
---- A warning notification will be shown.
---- Consider adding this directory to {ignore_dirs}
---
---By default, {max_events} is only enabled for windows.
---
---@class nvim_tree.config.filesystem_watchers
---
---(default: `true`)
---@field enable? boolean
---
---Idle milliseconds between filesystem change and tree update.
---(default: `50`)
---@field debounce_delay? integer
---
---Disable for specific directories.
---(default: `{ "/.ccls-cache", "/build", "/node_modules", "/target", "/.zig-cache"}`)
---@field ignore_dirs? string[]|(fun(path: string): boolean)
---
---Optionally enable only for specific directories.
---(default: `{}`)
---@field whitelist_dirs? string[]|(fun(path: string): boolean)
---
---Disable for a single directory after {max_events} consecutive events with an interval < {debounce_delay}.
---Set to 0 to allow unlimited consecutive events.
---(default: `0` or `1000` on windows)
---@field max_events? integer

View File

@@ -0,0 +1,60 @@
---@meta
error("Cannot require a meta file")
---Filters may be applied to the tree to exclude the display of file/directories.
---
---Multiple filters may be applied at once.
---
---Filters can be set at startup or toggled live via API with default mappings.
---
---`I `{git_ignored}` `|nvim_tree.api.filter.git.ignored.toggle()|
---Ignore files based on `.gitignore`. Requires |nvim_tree.config.git|
---
---`H `{dotfiles}` `|nvim_tree.api.filter.dotfiles.toggle()|
---Filter dotfiles: files/directories starting with a `.`
---
---`C `{git_clean}` `|nvim_tree.api.filter.git.clean.toggle()|
---Filter files with no git status. `.gitignore` files will not be filtered when {git_ignored}, as they are effectively dirty.
---
---`B `{no_buffer}` `|nvim_tree.api.filter.no_buffer.toggle()|
---Filter files that have no |buflisted()| buffer. For performance reasons buffer delete/wipe may not be immediately shown. A reload or filesystem event will always result in an update.
---
---`M `{no_bookmark}` `|nvim_tree.api.filter.no_bookmark.toggle()|
---Filter files that are not bookmarked. Enabling this is not useful as there is no means yet to persist bookmarks.
---
---`U `{custom}` `|nvim_tree.api.filter.custom.toggle()|
---Disable specific file/directory names via:
---- a list of backslash escaped |regular-expression| e.g. `"^\\.git""`
---- a function passed the absolute path of the directory.
---
---All filters including live filter may be disabled via {enable} and toggled with |nvim_tree.api.filter.toggle()|
---
---Files/directories may be {exclude}d from filtering: they will always be shown, overriding {git_ignored}, {dotfiles} and {custom}.
---@class nvim_tree.config.filters
---
---Enable all filters.
---(default: `true`)
---@field enable? boolean
---
---(default: `true`)
---@field git_ignored? boolean
---
---(default: `false`)
---@field dotfiles? boolean
---
---(default: `false`)
---@field git_clean? boolean
---
---(default: `false`)
---@field no_buffer? boolean
---
---(default: `false`)
---@field no_bookmark? boolean
---
---(default: `{}`)
---@field custom? string[]|(fun(absolute_path: string): boolean)
---
---(default: `{}`)
---@field exclude? string[]

View File

@@ -0,0 +1,39 @@
---@meta
error("Cannot require a meta file")
---Git operations are run in the background thus status may not immediately appear.
---
---Processes will be killed if they exceed {timeout} ms. Git integration will be disabled following 5 timeouts and you will be notified.
---
---Git integration may be disabled for git top-level directories via {disable_for_dirs}:
--- - A list of relative paths evaluated with [fnamemodify()] `:p` OR
--- - A function that is passed an absolute path and returns `true` to disable
---
---See [nvim-tree-icons-highlighting].
---
---@class nvim_tree.config.git
---
---(default: `true`)
---@field enable? boolean
---
---Show status icons of children when directory itself has no status icon
---(default: `true`)
---@field show_on_dirs? boolean
---
---Show status icons of children on directories that are open. Requires {show_on_dirs}.
---(default: `true`)
---@field show_on_open_dirs? boolean
---
---Disable for top level paths.
---(default: `{}`)
---@field disable_for_dirs? string[]|(fun(path: string): boolean)
---
---`git` processes timeout milliseconds.
---(default: `400`)
---@field timeout? integer
---
---Use `cygpath` if available to resolve paths for git.
---(Default: `false`)
---@field cygwin_support? boolean

View File

@@ -0,0 +1,16 @@
---@meta
error("Cannot require a meta file")
---{sort_by} [nvim_tree.config.help.sort_by]()
---
---Sort help entries alphabetically by:
---- `"key"`: map lhs
---- `"desc"`: description
---@alias nvim_tree.config.help.sort_by "key"|"desc"
---
---@class nvim_tree.config.help
---
---(default: `"key"`)
---@field sort_by? nvim_tree.config.help.sort_by

View File

@@ -0,0 +1,19 @@
---@meta
error("Cannot require a meta file")
---Hijack directory buffers by replacing the directory buffer with the tree.
---
---Disable this option if you use vim-dirvish or dirbuf.nvim.
---
---If [nvim_tree.config] {hijack_netrw} and {disable_netrw} are `false` this feature will be disabled.
---
---@class nvim_tree.config.hijack_directories
---
---(default: `true`)
---@field enable? boolean
---
---Open if the tree was previously closed.
---(default: `true`)
---@field auto_open? boolean

View File

@@ -0,0 +1,18 @@
---@meta
error("Cannot require a meta file")
--- Live filter allows you to filter the tree nodes dynamically using [regular-expression] matching.
---
--- This feature is bound to the `f` key by default. The filter can be cleared with the `F` key by default.
---
---@class nvim_tree.config.live_filter
---
---Prefix of the filter displayed in the buffer.
---(default: `"[FILTER]: "`)
---@field prefix? string
---
---Whether to filter folders or not.
---(default: `true`)
---@field always_show_folders? boolean

View File

@@ -0,0 +1,53 @@
---@meta
error("Cannot require a meta file")
---Log to a file `nvim-tree.log` in [stdpath()] `log`, usually `${XDG_STATE_HOME}/nvim`
---
---@class nvim_tree.config.log
---
---(default: `false`)
---@field enable? boolean
---
---Remove existing log file at startup.
---(default: `false`)
---@field truncate? boolean
---
---[nvim_tree.config.log.types]
---@field types? nvim_tree.config.log.types
---Specify which information to log.
---@class nvim_tree.config.log.types
---
---Everything.
---(default: `false`)
---@field all? boolean
---
--- Timing of some operations.
---(default: `false`)
---@field profile? boolean
---
---Config and mappings, at startup.
---(default: `false`)
---@field config? boolean
---
---File copy and paste actions.
---(default: `false`)
---@field copy_paste? boolean
---
---Used for local development only. Not useful for users.
---(default: `false`)
---@field dev? boolean
---
---LSP and COC processing, verbose.
---(default: `false`)
---@field diagnostics? boolean
---
---Git processing, verbose.
---(default: `false`)
---@field git? boolean
---
---[nvim_tree.config.filesystem_watchers] processing, verbose.
---(default: `false`)
---@field watcher? boolean

View File

@@ -0,0 +1,24 @@
---@meta
error("Cannot require a meta file")
---Indicate which files have unsaved modification.
---To see modified status in the tree you will need:
--- - [nvim_tree.config.renderer.icons.show] {modified} OR
--- - [nvim_tree.config.renderer] {highlight_modified}
---
---See [nvim-tree-icons-highlighting].
---
---@class nvim_tree.config.modified
---
---(default: `false`)
---@field enable? boolean
---
---Show modified indication on directory whose children are modified.
---(default: `true`)
---@field show_on_dirs? boolean
---
---Show modified indication on open directories. Requires {show_on_dirs}.
---(default: `false`)
---@field show_on_open_dirs? boolean

View File

@@ -0,0 +1,20 @@
---@meta
error("Cannot require a meta file")
---nvim-tree |vim.log.levels|
---- `ERROR`: hard errors e.g. failure to read from the file system.
---- `WARN`: non-fatal errors e.g. unable to system open a file.
---- `INFO`: information only e.g. file copy path confirmation.
---- `DEBUG`: information for troubleshooting, e.g. failures in some window closing operations.
---
---@class nvim_tree.config.notify
---
---Specify minimum notification |vim.log.levels|
---(Default: `vim.log.levels.INFO`)
---@field threshold? vim.log.levels
---
---Use absolute paths in FS action notifications, otherwise item names.
---(default: `true`)
---@field absolute_path? boolean

View File

@@ -0,0 +1,385 @@
---@meta
error("Cannot require a meta file")
---Controls the appearance of the tree.
---
---{highlight_} [nvim_tree.config.renderer.highlight]()
---
---See [nvim-tree-icons-highlighting]
---
---- `"none"`: no highlighting
---- `"icon"`: icon only
---- `"name"`: name only
---- `"all"`: icon and name
---@alias nvim_tree.config.renderer.highlight "none"|"icon"|"name"|"all"
---
---
---
---{decorators} [nvim_tree.config.renderer.decorator]()
---
---See [nvim-tree-icons-highlighting]
---
---A builtin decorator name `string` or |nvim_tree.api.Decorator| class.
---
---Builtin decorators in their default order:
---- `"Git"`
---- `"Open"`
---- `"Hidden"`
---- `"Modified"`
---- `"Bookmark"`
---- `"Diagnostics"`
---- `"Copied"`
---- `"Cut"`
---
---Specify {decorators} is a list e.g. `{ "Git", MyDecorator, "Cut" }`
---
---@alias nvim_tree.config.renderer.decorator nvim_tree.api.Decorator|"Git"|"Open"|"Hidden"|"Modified"|"Bookmark"|"Diagnostics"|"Copied"|"Cut"
---
---
---
---{root_folder_label} [nvim_tree.config.renderer.root_folder_label]()
---
---Controls the root folder name and visibility:
---- `string`: [filename-modifiers] format string, default `":~:s?$?/..?"`
---- `false`: to disable
---- `fun(root_cwd: string): string`: return a literal string from root's absolute path e.g.
---```lua
--- my_root_folder_label = function(path)
--- return ".../" .. vim.fn.fnamemodify(path, ":t")
--- end
---```
---@alias nvim_tree.config.renderer.root_folder_label string|false|(fun(root_cwd: string): string)
---
---
---
---{hidden_display} [nvim_tree.config.renderer.hidden_display]()
---
---Summary of hidden nodes, below the last node in the directory, highlighted with `NvimTreeHiddenDisplay`.
---- `"none"`: disabled, default
---- `"simple"`: total number of hidden files e.g.
--- - (3 hidden)
---- `"all"`: total and by reason: the filter that hid the node e.g.
--- - (14 total git: 5, dotfile: 9)
---- `(fun(hidden_stats: nvim_tree.config.renderer.hidden_stats): string)`
---
---See [nvim_tree.config.renderer.hidden_stats] for details and example.
---@alias nvim_tree.config.renderer.hidden_display "none"|"simple"|"all"|(fun(hidden_stats: nvim_tree.config.renderer.hidden_stats): string?)
---
---
---
---@class nvim_tree.config.renderer
---
---Appends a trailing slash to folder and symlink folder target names.
---(default: `false`)
---@field add_trailing? boolean
---
---Compact folders that only contain a single folder into one node. Function variant takes the relative path of grouped folders and returns a string to be displayed.
---(default: `false`)
---@field group_empty? boolean|(fun(relative_path: string): string)
---
---Display nodes whose name length is wider than the width of nvim-tree window in floating window.
---(default: `false`)
---@field full_name? boolean
---
---[nvim_tree.config.renderer.root_folder_label]
---(default: `":~:s?$?/..?"`)
---@field root_folder_label? nvim_tree.config.renderer.root_folder_label
---
---Number of spaces for each tree nesting level. Minimum 1.
---(default: `2`)
---@field indent_width? integer
---
---[nvim_tree.config.renderer.hidden_display]
---(default: `none`)
---@field hidden_display? nvim_tree.config.renderer.hidden_display
---
---Appends an arrow followed by the target of the symlink.
---(default: `true`)
---@field symlink_destination? boolean
---
---List in order of additive precedence.
---(default: `{ "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", }`)
---@field decorators? nvim_tree.config.renderer.decorator[]
---
---(default: `"none"`)
---@field highlight_git? nvim_tree.config.renderer.highlight
---
---(default: `"none"`)
---@field highlight_opened_files? nvim_tree.config.renderer.highlight
---
---(default: `"none"`)
---@field highlight_hidden? nvim_tree.config.renderer.highlight
---
---(default: `"none"`)
---@field highlight_modified? nvim_tree.config.renderer.highlight
---
---(default: `"none"`)
---@field highlight_bookmarks? nvim_tree.config.renderer.highlight
---
---(default: `"none"`)
---@field highlight_diagnostics? nvim_tree.config.renderer.highlight
---
---(default: `"name"`)
---@field highlight_clipboard? nvim_tree.config.renderer.highlight
---
---Highlight special files and directories with `NvimTreeSpecial*`.
---(default: `{ "Cargo.toml", "Makefile", "README.md", "readme.md", }`)
---@field special_files? string[]
---
---[nvim_tree.config.renderer.indent_markers]
---@field indent_markers? nvim_tree.config.renderer.indent_markers
---
---[nvim_tree.config.renderer.icons]
---@field icons? nvim_tree.config.renderer.icons
---@class nvim_tree.config.renderer.indent_markers
---
---Display indent markers when folders are open.
---(default: `false`)
---@field enable? boolean
---
---Display folder arrows in the same column as indent marker when using [nvim_tree.config.renderer.icons.padding] {folder_arrow}
---(default: `true`)
---@field inline_arrows? boolean
---
---@field icons? nvim_tree.config.renderer.indent_markers.icons
---[nvim_tree.config.renderer.indent_markers.icons]()
---Before the file/directory, length 1.
---@class nvim_tree.config.renderer.indent_markers.icons
---@inlinedoc
---
---(default: `"└"`)
---@field corner? string
---(default: `"│"`)
---@field edge? string
---(default: `"│"`)
---@field item? string
---(default: `"─"`)
---@field bottom? string
---(default: `" "`)
---@field none? string
---Icons and separators
---
---{_placement} [nvim_tree.config.renderer.icons.placement]()
---- `"before"`: before file/folder, after the file/folders icons
---- `"after"`: after file/folder
---- `"signcolumn"`: far left, requires |nvim_tree.config.view| {signcolumn}.
---- `"right_align"`: far right
---@alias nvim_tree.config.renderer.icons.placement "before"|"after"|"signcolumn"|"right_align"
---
---
---@class nvim_tree.config.renderer.icons
---
---(default: `before`)
---@field git_placement? nvim_tree.config.renderer.icons.placement
---
---(default: `after`)
---@field hidden_placement? nvim_tree.config.renderer.icons.placement
---
---(default: `after`)
---@field modified_placement? nvim_tree.config.renderer.icons.placement
---
---(default: `signcolumn`)
---@field bookmarks_placement? nvim_tree.config.renderer.icons.placement
---
---(default: `signcolumn`)
---@field diagnostics_placement? nvim_tree.config.renderer.icons.placement
---
---@field padding? nvim_tree.config.renderer.icons.padding
---
---Separator between symlink source and target.
---(default: `" ➛ "`)
---@field symlink_arrow? string
---
---[nvim_tree.config.renderer.icons.show]
---@field show? nvim_tree.config.renderer.icons.show
---
---[nvim_tree.config.renderer.icons.glyphs]
---@field glyphs? nvim_tree.config.renderer.icons.glyphs
---
---[nvim_tree.config.renderer.icons.web_devicons]
---@field web_devicons? nvim_tree.config.renderer.icons.web_devicons
---Configure optional plugin `nvim-tree/nvim-web-devicons`, see [nvim-tree-icons-highlighting].
---
---@class nvim_tree.config.renderer.icons.web_devicons
---
---@field file? nvim_tree.config.renderer.icons.web_devicons.file
---
---@field folder? nvim_tree.config.renderer.icons.web_devicons.folder
---[nvim_tree.config.renderer.icons.web_devicons.file]()
---@class nvim_tree.config.renderer.icons.web_devicons.file
---@inlinedoc
---
---(default: `true`)
---@field enable? boolean
---
---(default: `true`)
---@field color? boolean
---[nvim_tree.config.renderer.icons.web_devicons.folder]()
---@class nvim_tree.config.renderer.icons.web_devicons.folder
---@inlinedoc
---
---(default: `false`)
---@field enable? boolean
---
---(default: `true`)
---@field color? boolean
---[nvim_tree.config.renderer.icons.padding]()
---@class nvim_tree.config.renderer.icons.padding
---@inlinedoc
---
---Between icon and filename.
---(default: `" "`)
---@field icon? string
---
---Between folder arrow icon and file/folder icon.
---(default: `" "`)
---@field folder_arrow? string
---See [nvim-tree-icons-highlighting].
---@class nvim_tree.config.renderer.icons.show
---
---(default: `true`)
---@field file? boolean
---
---(default: `true`)
---@field folder? boolean
---
---(default: `true`)
---@field git? boolean
---
---(default: `true`)
---@field modified? boolean
---
---(default: `false`)
---@field hidden? boolean
---
---(default: `true`)
---@field diagnostics? boolean
---
---(default: `true`)
---@field bookmarks? boolean
---
---Show a small arrow before the folder node. Arrow will be a part of the node when using [nvim_tree.config.renderer.indent_markers].
---(default: `true`)
---@field folder_arrow? boolean
---See [nvim-tree-icons-highlighting].
---
---Glyphs that appear in the sign column must have length <= 2
---@class nvim_tree.config.renderer.icons.glyphs
---
---Files
---(default: `""`)
---@field default? string
---
---(default: `""`)
---@field symlink? string
---
---(default: `"󰆤"`)
---@field bookmark? string
---
---(default: `"●"`)
---@field modified? string
---
---(default: `"󰜌"`)
---@field hidden? string
---
---@field folder? nvim_tree.config.renderer.icons.glyphs.folder
---
---@field git? nvim_tree.config.renderer.icons.glyphs.git
---[nvim_tree.config.renderer.icons.glyphs.folder]()
---@class nvim_tree.config.renderer.icons.glyphs.folder
---@inlinedoc
---(default: left arrow)
---@field arrow_closed? string
---(default: down arrow)
---@field arrow_open? string
---(default: `""`)
---@field default? string
---(default: `""`)
---@field open? string
---(default: `""`)
---@field empty? string
---(default: `""`)
---@field empty_open? string
---(default: `""`)
---@field symlink? string
---(default: `""`)
---@field symlink_open? string
---[nvim_tree.config.renderer.icons.glyphs.git]()
---@class nvim_tree.config.renderer.icons.glyphs.git
---@inlinedoc
---(default: `"✗"`)
---@field unstaged? string
---(default: `"✓"`)
---@field staged? string
---(default: `""`)
---@field unmerged? string
---(default: `"➜"`)
---@field renamed? string
---(default: `"★"`)
---@field untracked? string
---(default: `""`)
---@field deleted? string
---(default: `"◌"`)
---@field ignored? string
---Number of hidden nodes in a directory by reason: the filter that hid the node.
---
---Passed to your [nvim_tree.config.renderer.hidden_display] function e.g.
---```lua
---
--- ---@param hidden_stats nvim_tree.config.renderer.hidden_stats
--- ---@return string? summary
--- local my_hidden_display = 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
---```
---
---@class nvim_tree.config.renderer.hidden_stats
---@field bookmark integer
---@field buf integer
---@field custom integer
---@field dotfile integer
---@field git integer
---@field live_filter integer

View File

@@ -0,0 +1,42 @@
---@meta
error("Cannot require a meta file")
---Sort files within a directory.
---
---{sorter} presets [nvim_tree.config.sort.Sorter]()
---- `"name"`
---- `"case_sensitive"` name
---- `"modification_time"`
---- `"extension"` uses all suffixes e.g. `foo.tar.gz` -> `.tar.gz`
---- `"suffix"` uses the last e.g. `foo.tar.gz` -> `.gz`
---- `"filetype"` [filetype]
---@alias nvim_tree.config.sort.Sorter "name"|"case_sensitive"|"modification_time"|"extension"|"suffix"|"filetype"
---
---{sorter} may be a function that is passed a list of `nvim_tree.api.Node` to be sorted in place e.g.
---```lua
---
--- ---Sort by name length
--- ---@param nodes nvim_tree.api.Node[]
--- ---@return nvim_tree.config.sort.Sorter?
--- local sorter = function(nodes)
--- table.sort(nodes, function(a, b)
--- return #a.name < #b.name
--- end)
--- end
---```
---{sorter} may be a function that returns a [nvim_tree.config.sort.Sorter]
---
---@class nvim_tree.config.sort
---
---(default: `"name"`)
---@field sorter? nvim_tree.config.sort.Sorter|(fun(nodes: nvim_tree.api.Node[]): nvim_tree.config.sort.Sorter?)
---
---Sort folders before files. Has no effect when {sorter} is a function.
---(default: `true`)
---@field folders_first? boolean
---
---Sort files before folders. Has no effect when {sorter} is a function. Overrides {folders_first}.
---(default: `false`)
---@field files_first? boolean

View File

@@ -0,0 +1,26 @@
---@meta
error("Cannot require a meta file")
---@class nvim_tree.config.tab
---
---[nvim_tree.config.tab.sync]
---@field sync? nvim_tree.config.tab.sync
---@class nvim_tree.config.tab.sync
---
---Opens the tree automatically when switching tabpage or opening a new tabpage if the tree was previously open.
---(default: `false`)
---@field open? boolean
---
---Closes the tree across all tabpages when the tree is closed.
---(default: `false`)
---@field close? boolean
---
---
---List of filetypes or buffer names on new tab that will prevent `open` and `close`
---(default: `{}`)
---@field ignore? string[]

View File

@@ -0,0 +1,14 @@
---@meta
error("Cannot require a meta file")
---Files may be trashed via an external command that must be installed on your system.
--- - linux: `gio trash`, from linux package `glib2`
--- - macOS: `trash`, from homebrew package `trash`
--- - windows: `trash`, requires `trash-cli` or similar
---
---@class nvim_tree.config.trash
---
---(default: `"gio trash"` or `"trash"`)
---@field cmd? string

View File

@@ -0,0 +1,26 @@
---@meta
error("Cannot require a meta file")
---@class nvim_tree.config.ui
---
---[nvim_tree.config.ui.confirm]
---@field confirm? nvim_tree.config.ui.confirm
---Confirmation prompts.
---@class nvim_tree.config.ui.confirm
---
---Prompt before removing.
---(default: `true`)
---@field remove? boolean
---
---Prompt before trashing.
---(default: `true`)
---@field trash? boolean
---
---If `true` the prompt will be `Y/n`, otherwise `y/N`
---(default: `false`)
---@field default_yes? boolean

View File

@@ -0,0 +1,35 @@
---@meta
error("Cannot require a meta file")
---Update the focused file on [BufEnter], uncollapsing folders recursively.
---
---@class nvim_tree.config.update_focused_file
---
---(default: `false`)
---@field enable? boolean
---
---[nvim_tree.config.update_focused_file.update_root]
---@field update_root? nvim_tree.config.update_focused_file.update_root
---
---A function called on [BufEnter] that returns true if the file should not be focused when opening.
---(default: `false`)
---@field exclude? boolean|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean)
---Update the root directory of the tree if the file is not under the current root directory.
---
---Prefers vim's cwd and [nvim_tree.config] {root_dirs}, falling back to the directory containing the file.
---
---Requires [nvim_tree.config.update_focused_file]
---
---@class nvim_tree.config.update_focused_file.update_root
---
---(default: `false`)
---@field enable? boolean
---
---List of buffer names and filetypes that will not update the root dir of the tree if the file isn't found under the current root directory.
---(default: `{}`)
---@field ignore_list? string[]

View File

@@ -0,0 +1,107 @@
---@meta
error("Cannot require a meta file")
---Configures the dimensions and appearance of the nvim-tree window.
---
---The window is "docked" at the left by default, however may be configured to float: [nvim_tree.config.view.float]
---
---{width} can be a [nvim_tree.config.view.width.spec] for static control or a [nvim_tree.config.view.width] for fully dynamic control based on longest line.
---
---[nvim_tree.config.view.width.spec]()
---- `string`: `x%` string e.g. `30%`
---- `integer`: number of columns
---- `function`: returns one of the above
---@alias nvim_tree.config.view.width.spec string|integer|(fun(): integer|string)
---
---@class nvim_tree.config.view
---
---When entering nvim-tree, reposition the view so that the current node is initially centralized, see [zz].
---(default: `false`)
---@field centralize_selection? boolean
---
---['cursorline']
---(default: `true`)
---@field cursorline? boolean
---
---['cursorlineopt']
---(default: `both`)
---@field cursorlineopt? string
---
---Idle milliseconds before some reload / refresh operations. Increase if you experience performance issues around screen refresh.
---(default: `15`)
---@field debounce_delay? integer
---
---(default: `"left"`)
---@field side? "left"|"right"
---
---Preserves window proportions when opening a file. If `false`, the height and width of windows other than nvim-tree will be equalized.
---(default: `false`)
---@field preserve_window_proportions? boolean
---
---['number']
---(default: `false`)
---@field number? boolean
---
---['relativenumber']
---(default: `false`)
---@field relativenumber? boolean
---
---['signcolumn']
---(default: `"yes"`)
---@field signcolumn? "yes"|"auto"|"no"
---
---(default: `30`)
---@field width? nvim_tree.config.view.width.spec|nvim_tree.config.view.width
---
---[nvim_tree.config.view.float]
---@field float? nvim_tree.config.view.float
---Configure dynamic width based on longest line.
---
---@class nvim_tree.config.view.width
---
---(default: `30`)
---@field min? nvim_tree.config.view.width.spec
---
----1 for unbounded.
---(default: `-1`)
---@field max? nvim_tree.config.view.width.spec
---
---Exclude these lines when computing width.
---(default: `{ "root" }`)
---@field lines_excluded? ("root")[]
---
---Extra padding to the right.
---(default: `1`)
---@field padding? nvim_tree.config.view.width.spec
---Configure floating window behaviour
---
---{open_win_config} is passed to [nvim_open_win()], default:
---```lua
--- {
--- relative = "editor",
--- border = "rounded",
--- width = 30,
--- height = 30,
--- row = 1,
--- col = 1,
--- }
---```
---@class nvim_tree.config.view.float
---
---(default: `false`)
---@field enable? boolean
---
---Close the floating window when it loses focus.
---(default: `true`)
---@field quit_on_focus_loss? boolean
---
---(default: `{ relative = "editor", border = "rounded", width = 30, height = 30, row = 1, col = 1, }`)
---@field open_win_config? vim.api.keyset.win_config|(fun(): vim.api.keyset.win_config)

View File

@@ -1,8 +1,10 @@
local log = require "nvim-tree.log"
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local Iterator = require "nvim-tree.iterators.node-iterator"
local log = require("nvim-tree.log")
local view = require("nvim-tree.view")
local utils = require("nvim-tree.utils")
local core = require("nvim-tree.core")
local DirectoryNode = require("nvim-tree.node.directory")
local Iterator = require("nvim-tree.iterators.node-iterator")
local M = {}
@@ -17,7 +19,7 @@ function M.fn(path)
end
-- always match against the real path
local path_real = vim.loop.fs_realpath(path)
local path_real = vim.uv.fs_realpath(path)
if not path_real then
return
end
@@ -30,7 +32,7 @@ function M.fn(path)
local profile = log.profile_start("find file %s", path_real)
-- refresh the contents of all parents, expanding groups as needed
if utils.get_node_from_path(path_real) == nil then
if explorer:get_node_from_path(path_real) == nil then
explorer:refresh_parent_nodes_for_path(vim.fn.fnamemodify(path_real, ":h"))
end
@@ -58,25 +60,33 @@ function M.fn(path)
local link_match = node.link_to and vim.startswith(path_real, node.link_to .. utils.path_separator)
if abs_match or link_match then
if not node.group_next then
node.open = true
end
if #node.nodes == 0 then
core.get_explorer():expand(node)
if node.group_next and incremented_line then
line = line - 1
local dir = node:as(DirectoryNode)
if dir then
if not dir.group_next then
dir.open = true
end
if #dir.nodes == 0 then
core.get_explorer():expand_dir_node(dir)
if dir.group_next and incremented_line then
line = line - 1
end
end
end
end
end)
:recursor(function(node)
return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes)
node = node and node:as(DirectoryNode)
if node then
return node.group_next and { node.group_next } or (node.open and #node.nodes > 0 and node.nodes)
else
return nil
end
end)
:iterate()
if found and view.is_visible() then
explorer.renderer:draw()
view.set_cursor { line, 0 }
view.set_cursor({ line, 0 })
end
running[path_real] = false

View File

@@ -1,6 +0,0 @@
local M = {}
M.find_file = require "nvim-tree.actions.finders.find-file"
M.search_node = require "nvim-tree.actions.finders.search-node"
return M

View File

@@ -1,4 +1,4 @@
local core = require "nvim-tree.core"
local core = require("nvim-tree.core")
local find_file = require("nvim-tree.actions.finders.find-file").fn
local M = {}
@@ -25,23 +25,23 @@ local function search(search_dir, input_path)
local filter_status = explorer.filters:prepare()
handle, _ = vim.loop.fs_scandir(dir)
handle, _ = vim.uv.fs_scandir(dir)
if not handle then
return
end
realpath, _ = vim.loop.fs_realpath(dir)
realpath, _ = vim.uv.fs_realpath(dir)
if not realpath or vim.tbl_contains(realpaths_searched, realpath) then
return
end
table.insert(realpaths_searched, realpath)
name, _ = vim.loop.fs_scandir_next(handle)
name, _ = vim.uv.fs_scandir_next(handle)
while name do
path = dir .. "/" .. name
---@type uv.fs_stat.result|nil
stat, _ = vim.loop.fs_stat(path)
stat, _ = vim.uv.fs_stat(path)
if not stat then
break
end
@@ -59,7 +59,7 @@ local function search(search_dir, input_path)
end
end
name, _ = vim.loop.fs_scandir_next(handle)
name, _ = vim.uv.fs_scandir_next(handle)
end
end
@@ -74,14 +74,8 @@ function M.fn()
-- temporarily set &path
local bufnr = vim.api.nvim_get_current_buf()
local path_existed, path_opt
if vim.fn.has "nvim-0.10" == 1 then
path_existed, path_opt = pcall(vim.api.nvim_get_option_value, "path", { buf = bufnr })
vim.api.nvim_set_option_value("path", core.get_cwd() .. "/**", { buf = bufnr })
else
path_existed, path_opt = pcall(vim.api.nvim_buf_get_option, bufnr, "path") ---@diagnostic disable-line: deprecated
vim.api.nvim_buf_set_option(bufnr, "path", core.get_cwd() .. "/**") ---@diagnostic disable-line: deprecated
end
local path_existed, path_opt = pcall(vim.api.nvim_get_option_value, "path", { buf = bufnr })
vim.api.nvim_set_option_value("path", core.get_cwd() .. "/**", { buf = bufnr })
vim.ui.input({ prompt = "Search: ", completion = "file_in_path" }, function(input_path)
if not input_path or input_path == "" then
@@ -89,17 +83,9 @@ function M.fn()
end
-- reset &path
if path_existed then
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value("path", path_opt, { buf = bufnr })
else
vim.api.nvim_buf_set_option(bufnr, "path", path_opt) ---@diagnostic disable-line: deprecated
end
vim.api.nvim_set_option_value("path", path_opt, { buf = bufnr })
else
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value("path", nil, { buf = bufnr })
else
vim.api.nvim_buf_set_option(bufnr, "path", nil) ---@diagnostic disable-line: deprecated
end
vim.api.nvim_set_option_value("path", nil, { buf = bufnr })
end
-- strip trailing slash

View File

@@ -1,43 +1,47 @@
local lib = require "nvim-tree.lib"
local log = require "nvim-tree.log"
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local events = require "nvim-tree.events"
local notify = require "nvim-tree.notify"
local lib = require("nvim-tree.lib")
local log = require("nvim-tree.log")
local utils = require("nvim-tree.utils")
local core = require("nvim-tree.core")
local events = require("nvim-tree.events")
local notify = require("nvim-tree.notify")
local find_file = require("nvim-tree.actions.finders.find-file").fn
---@enum ACTION
local ACTION = {
copy = "copy",
cut = "cut",
}
local Class = require("nvim-tree.classic")
local DirectoryNode = require("nvim-tree.node.directory")
local FileNode = require("nvim-tree.node.file")
local Node = require("nvim-tree.node")
---@class Clipboard to handle all actions.fs clipboard API
---@field config table hydrated user opts.filters
---@alias ClipboardAction "copy" | "cut"
---@alias ClipboardData table<ClipboardAction, Node[]>
---@alias ClipboardActionFn fun(source: string, dest: string): boolean, string?
---@class (exact) Clipboard: nvim_tree.Class
---@field private explorer Explorer
---@field private data table<ACTION, Node[]>
local Clipboard = {}
---@field private data ClipboardData
---@field private clipboard_name string
---@field private reg string
local Clipboard = Class:extend()
---@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,
},
---@class Clipboard
---@overload fun(args: ClipboardArgs): Clipboard
---@class (exact) ClipboardArgs
---@field explorer Explorer
---@protected
---@param args ClipboardArgs
function Clipboard:new(args)
self.explorer = args.explorer
self.data = {
copy = {},
cut = {},
}
setmetatable(o, self)
self.__index = self
return o
self.clipboard_name = self.explorer.opts.actions.use_system_clipboard and "system" or "neovim"
self.reg = self.explorer.opts.actions.use_system_clipboard and "+" or "1"
end
---@param source string
@@ -45,13 +49,11 @@ end
---@return boolean
---@return string|nil
local function do_copy(source, destination)
local source_stats, handle
local success, errmsg
local source_stats, err = vim.uv.fs_stat(source)
source_stats, errmsg = vim.loop.fs_stat(source)
if not source_stats then
log.line("copy_paste", "do_copy fs_stat '%s' failed '%s'", source, errmsg)
return false, errmsg
log.line("copy_paste", "do_copy fs_stat '%s' failed '%s'", source, err)
return false, err
end
log.line("copy_paste", "do_copy %s '%s' -> '%s'", source_stats.type, source, destination)
@@ -62,116 +64,68 @@ local function do_copy(source, destination)
end
if source_stats.type == "file" then
success, errmsg = vim.loop.fs_copyfile(source, destination)
local success
success, err = vim.uv.fs_copyfile(source, destination)
if not success then
log.line("copy_paste", "do_copy fs_copyfile failed '%s'", errmsg)
return false, errmsg
log.line("copy_paste", "do_copy fs_copyfile failed '%s'", err)
return false, err
end
return true
elseif source_stats.type == "directory" then
handle, errmsg = vim.loop.fs_scandir(source)
local handle
handle, err = vim.uv.fs_scandir(source)
if type(handle) == "string" then
return false, handle
elseif not handle then
log.line("copy_paste", "do_copy fs_scandir '%s' failed '%s'", source, errmsg)
return false, errmsg
log.line("copy_paste", "do_copy fs_scandir '%s' failed '%s'", source, err)
return false, err
end
success, errmsg = vim.loop.fs_mkdir(destination, source_stats.mode)
local success
success, err = vim.uv.fs_mkdir(destination, source_stats.mode)
if not success then
log.line("copy_paste", "do_copy fs_mkdir '%s' failed '%s'", destination, errmsg)
return false, errmsg
log.line("copy_paste", "do_copy fs_mkdir '%s' failed '%s'", destination, err)
return false, err
end
while true do
local name, _ = vim.loop.fs_scandir_next(handle)
local name, _ = vim.uv.fs_scandir_next(handle)
if not name then
break
end
local new_name = utils.path_join { source, name }
local new_destination = utils.path_join { destination, name }
success, errmsg = do_copy(new_name, new_destination)
local new_name = utils.path_join({ source, name })
local new_destination = utils.path_join({ destination, name })
success, err = do_copy(new_name, new_destination)
if not success then
return false, errmsg
return false, err
end
end
else
errmsg = string.format("'%s' illegal file type '%s'", source, source_stats.type)
log.line("copy_paste", "do_copy %s", errmsg)
return false, errmsg
err = string.format("'%s' illegal file type '%s'", source, source_stats.type)
log.line("copy_paste", "do_copy %s", err)
return false, err
end
return true
end
---Paste a single item with no conflict handling.
---@param source string
---@param dest string
---@param action ACTION
---@param action_fn fun(source: string, dest: string)
---@return boolean|nil -- success
---@return string|nil -- error message
local function do_single_paste(source, dest, action, action_fn)
local dest_stats
local success, errmsg, errcode
local notify_source = notify.render_path(source)
log.line("copy_paste", "do_single_paste '%s' -> '%s'", source, dest)
dest_stats, errmsg, errcode = vim.loop.fs_stat(dest)
if not dest_stats and errcode ~= "ENOENT" then
notify.error("Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or "???"))
return false, errmsg
end
local function on_process()
success, errmsg = action_fn(source, dest)
if not success then
notify.error("Could not " .. action .. " " .. notify_source .. " - " .. (errmsg or "???"))
return false, errmsg
end
find_file(utils.path_remove_trailing(dest))
end
if dest_stats then
local input_opts = {
prompt = "Rename to ",
default = dest,
completion = "dir",
}
if source == dest then
vim.ui.input(input_opts, function(new_dest)
utils.clear_prompt()
if new_dest then
do_single_paste(source, new_dest, action, action_fn)
end
end)
else
local prompt_select = "Overwrite " .. dest .. " ?"
local prompt_input = prompt_select .. " R(ename)/y/n: "
lib.prompt(prompt_input, prompt_select, { "", "y", "n" }, { "Rename", "Yes", "No" }, "nvimtree_overwrite_rename", function(item_short)
utils.clear_prompt()
if item_short == "y" then
on_process()
elseif item_short == "" or item_short == "r" then
vim.ui.input(input_opts, function(new_dest)
utils.clear_prompt()
if new_dest then
do_single_paste(source, new_dest, action, action_fn)
end
end)
end
end)
end
else
on_process()
---@param action ClipboardAction
---@param action_fn ClipboardActionFn
local function do_paste_one(source, dest, action, action_fn)
log.line("copy_paste", "do_paste_one '%s' -> '%s'", source, dest)
local success, err = action_fn(source, dest)
if not success then
notify.error("Could not " .. action .. " " .. notify.render_path(source) .. " - " .. (err or "???"))
end
find_file(utils.path_remove_trailing(dest))
end
---@param node Node
---@param clip table
---@param clip ClipboardData
local function toggle(node, clip)
if node.name == ".." then
return
@@ -189,49 +143,243 @@ end
---Clear copied and cut
function Clipboard:clear_clipboard()
self.data[ACTION.copy] = {}
self.data[ACTION.cut] = {}
notify.info "Clipboard has been emptied."
self.data.copy = {}
self.data.cut = {}
notify.info("Clipboard has been emptied.")
self.explorer.renderer:draw()
end
---Copy one node
---@param node Node
function Clipboard:copy(node)
utils.array_remove(self.data[ACTION.cut], node)
toggle(node, self.data[ACTION.copy])
---Bulk add/remove nodes to/from a clipboard list.
---@private
---@param nodes Node[] filtered nodes to operate on
---@param from Node[] list to remove from (the opposite clipboard)
---@param to Node[] list to add to
---@param verb string notification verb ("added to" or "cut to")
function Clipboard:bulk_clipboard(nodes, from, to, verb)
local added = 0
local removed = 0
for _, node in ipairs(nodes) do
if node.name ~= ".." then
utils.array_remove(from, node)
if utils.array_remove(to, node) then
removed = removed + 1
else
table.insert(to, node)
added = added + 1
end
end
end
if added > 0 then
notify.info(string.format("%d nodes %s clipboard.", added, verb))
elseif removed > 0 then
notify.info(string.format("%d nodes removed from clipboard.", removed))
end
self.explorer.renderer:draw()
end
---Cut one node
---@param node Node
function Clipboard:cut(node)
utils.array_remove(self.data[ACTION.copy], node)
toggle(node, self.data[ACTION.cut])
self.explorer.renderer:draw()
---@private
---@param node_or_nodes Node|Node[]
---@return boolean
function Clipboard:is_nodes_array(node_or_nodes)
if type(node_or_nodes) == "table" and node_or_nodes.is and node_or_nodes:is(Node) then
return false
end
return true
end
---Paste cut or cop
---Copy one or more nodes
---@param node_or_nodes Node|Node[]
function Clipboard:copy(node_or_nodes)
if self:is_nodes_array(node_or_nodes) == false then
utils.array_remove(self.data.cut, node_or_nodes)
toggle(node_or_nodes, self.data.copy)
self.explorer.renderer:draw()
else
self:bulk_clipboard(utils.filter_descendant_nodes(node_or_nodes), self.data.cut, self.data.copy, "added to")
end
self:copy_absolute_path(self.data.copy, { notify = false })
end
---Cut one or more nodes
---@param node_or_nodes Node|Node[]
function Clipboard:cut(node_or_nodes)
if self:is_nodes_array(node_or_nodes) == false then
utils.array_remove(self.data.copy, node_or_nodes)
toggle(node_or_nodes, self.data.cut)
self.explorer.renderer:draw()
else
self:bulk_clipboard(utils.filter_descendant_nodes(node_or_nodes), self.data.copy, self.data.cut, "cut to")
end
self:copy_absolute_path(self.data.cut, { notify = false })
end
---Clear clipboard for action and reload to reflect filesystem changes from paste.
---@private
---@param action ClipboardAction
function Clipboard:finish_paste(action)
self.data[action] = {}
self.explorer:reload_explorer()
end
---Resolve conflicting paste items.
---Single conflict: per-file prompt with full-path rename (pre-visual-mode behavior).
---Multiple conflicts: batch prompt with suffix rename.
---@private
---@param conflict {node: Node, dest: string}[]
---@param destination string
---@param action ClipboardAction
---@param action_fn ClipboardActionFn
function Clipboard:resolve_conflicts(conflict, destination, action, action_fn)
if #conflict == 1 then
local source = conflict[1].node.absolute_path
local dest = conflict[1].dest
local function rename_prompt(default_dest)
vim.ui.input({ prompt = "Rename to ", default = default_dest, completion = "dir" }, function(new_dest)
utils.clear_prompt()
if not new_dest or new_dest == "" then
self:finish_paste(action)
return
end
if vim.uv.fs_stat(new_dest) then
self:resolve_conflicts({ { node = conflict[1].node, dest = new_dest } }, destination, action, action_fn)
else
do_paste_one(source, new_dest, action, action_fn)
self:finish_paste(action)
end
end)
end
if source == dest then
rename_prompt(dest)
else
local prompt_select = "Overwrite " .. dest .. " ?"
lib.prompt(prompt_select .. " R(ename)/y/n: ", prompt_select,
{ "", "y", "n" }, { "Rename", "Yes", "No" },
"nvimtree_overwrite_rename",
function(item_short)
utils.clear_prompt()
if item_short == "y" then
do_paste_one(source, dest, action, action_fn)
self:finish_paste(action)
elseif item_short == "" or item_short == "r" then
rename_prompt(dest)
else
self:finish_paste(action)
end
end)
end
return
end
local prompt_select = #conflict .. " file(s) already exist"
local prompt_input = prompt_select .. ". R(ename suffix)/y/n: "
lib.prompt(prompt_input, prompt_select,
{ "", "y", "n" },
{ "Rename (suffix)", "Overwrite all", "Skip all" },
"nvimtree_paste_conflict",
function(item_short)
utils.clear_prompt()
if item_short == "y" then
for _, item in ipairs(conflict) do
do_paste_one(item.node.absolute_path, item.dest, action, action_fn)
end
self:finish_paste(action)
elseif item_short == "" or item_short == "r" then
vim.ui.input({ prompt = "Suffix: " }, function(suffix)
utils.clear_prompt()
if not suffix or suffix == "" then
return
end
local still_conflict = {}
for _, item in ipairs(conflict) do
local basename = vim.fn.fnamemodify(item.node.name, ":r")
local extension = vim.fn.fnamemodify(item.node.name, ":e")
local new_name = extension ~= "" and (basename .. suffix .. "." .. extension) or (item.node.name .. suffix)
local new_dest = utils.path_join({ destination, new_name })
local stats = vim.uv.fs_stat(new_dest)
if stats then
table.insert(still_conflict, { node = item.node, dest = new_dest })
else
do_paste_one(item.node.absolute_path, new_dest, action, action_fn)
end
end
if #still_conflict > 0 then
self:resolve_conflicts(still_conflict, destination, action, action_fn)
else
self:finish_paste(action)
end
end)
else
self:finish_paste(action)
end
end)
end
--- Transforms the copied absolute paths on register to node
---@private
function Clipboard:get_nodes_from_reg()
local content = vim.fn.getreg(self.reg)
if #content == 0 then
return {}
end
local nodes = {}
local absolute_paths = vim.split(content:sub(1, #content), "\n")
for _, absolute_path in ipairs(absolute_paths) do
if absolute_path:match("^%s*(.-)%s*s") then
local node_args = { absolute_path = absolute_path, name = vim.fn.fnamemodify(absolute_path, ":t"), explorer = self.explorer }
if absolute_path:sub(-1) == "/" then
node_args.name = vim.fn.fnamemodify(absolute_path:sub(1, -2), ":t")
table.insert(nodes, DirectoryNode(node_args))
else
table.insert(nodes, FileNode(node_args))
end
end
end
return nodes
end
---@param nodes Node[]
---@private
function Clipboard:destroy_nodes(nodes)
for _, n in ipairs(nodes) do
n:destroy()
end
end
---Paste cut or copy with batch conflict resolution.
---@private
---@param node Node
---@param action ACTION
---@param action_fn fun(source: string, dest: string)
---@param action ClipboardAction
---@param action_fn ClipboardActionFn
function Clipboard:do_paste(node, action, action_fn)
node = lib.get_last_group_node(node)
local explorer = core.get_explorer()
if node.name == ".." and explorer then
node = explorer
if node.name == ".." then
node = self.explorer
else
local dir = node:as(DirectoryNode)
if dir then
node = dir:last_group_node()
end
end
local clip = self.data[action]
local is_local = #self.data[action] > 0
local clip = is_local and self.data[action] or self:get_nodes_from_reg()
if #clip == 0 then
return
end
local destination = node.absolute_path
local stats, errmsg, errcode = vim.loop.fs_stat(destination)
if not stats and errcode ~= "ENOENT" then
log.line("copy_paste", "do_paste fs_stat '%s' failed '%s'", destination, errmsg)
notify.error("Could not " .. action .. " " .. notify.render_path(destination) .. " - " .. (errmsg or "???"))
local stats, err, err_name = vim.uv.fs_stat(destination)
if not stats and err_name ~= "ENOENT" then
log.line("copy_paste", "do_paste fs_stat '%s' failed '%s'", destination, err)
notify.error("Could not " .. action .. " " .. notify.render_path(destination) .. " - " .. (err or "???"))
if is_local == false then
self:destroy_nodes(clip)
end
return
end
local is_dir = stats and stats.type == "directory"
@@ -239,14 +387,33 @@ function Clipboard:do_paste(node, action, action_fn)
destination = vim.fn.fnamemodify(destination, ":p:h")
end
-- Partition into conflict / no-conflict
local no_conflict = {}
local conflict = {}
for _, _node in ipairs(clip) do
local dest = utils.path_join { destination, _node.name }
do_single_paste(_node.absolute_path, dest, action, action_fn)
local dest = utils.path_join({ destination, _node.name })
local dest_stats = vim.uv.fs_stat(dest)
if dest_stats then
table.insert(conflict, { node = _node, dest = dest })
else
table.insert(no_conflict, { node = _node, dest = dest })
end
end
self.data[action] = {}
if not self.config.filesystem_watchers.enable then
self.explorer:reload_explorer()
-- Paste non-conflicting items immediately
for _, item in ipairs(no_conflict) do
do_paste_one(item.node.absolute_path, item.dest, action, action_fn)
end
-- Resolve conflicts in batch
if #conflict > 0 then
self:resolve_conflicts(conflict, destination, action, action_fn)
else
self:finish_paste(action)
end
if is_local == false then
self:destroy_nodes(clip)
end
end
@@ -263,7 +430,7 @@ local function do_cut(source, destination)
end
events._dispatch_will_rename_node(source, destination)
local success, errmsg = vim.loop.fs_rename(source, destination)
local success, errmsg = vim.uv.fs_rename(source, destination)
if not success then
log.line("copy_paste", "do_cut fs_rename failed '%s'", errmsg)
return false, errmsg
@@ -275,25 +442,27 @@ end
---Paste cut (if present) or copy (if present)
---@param node Node
function Clipboard:paste(node)
if self.data[ACTION.cut][1] ~= nil then
self:do_paste(node, ACTION.cut, do_cut)
elseif self.data[ACTION.copy][1] ~= nil then
self:do_paste(node, ACTION.copy, do_copy)
---@param opts? { cut?: boolean }
function Clipboard:paste(node, opts)
opts = opts and opts or {}
if self.data.cut[1] ~= nil or opts.cut == true then
self:do_paste(node, "cut", do_cut)
else
self:do_paste(node, "copy", do_copy)
end
end
function Clipboard:print_clipboard()
local content = {}
if #self.data[ACTION.cut] > 0 then
if #self.data.cut > 0 then
table.insert(content, "Cut")
for _, node in pairs(self.data[ACTION.cut]) do
for _, node in pairs(self.data.cut) do
table.insert(content, " * " .. (notify.render_path(node.absolute_path)))
end
end
if #self.data[ACTION.copy] > 0 then
if #self.data.copy > 0 then
table.insert(content, "Copy")
for _, node in pairs(self.data[ACTION.copy]) do
for _, node in pairs(self.data.copy) do
table.insert(content, " * " .. (notify.render_path(node.absolute_path)))
end
end
@@ -302,66 +471,46 @@ function Clipboard:print_clipboard()
end
---@param content string
function Clipboard:copy_to_reg(content)
local clipboard_name
local reg
if self.config.actions.use_system_clipboard == true then
clipboard_name = "system"
reg = "+"
else
clipboard_name = "neovim"
reg = "1"
---@param message? string
---@param opts? { notify?: boolean }
function Clipboard:copy_to_reg(content, message, opts)
opts = opts and opts or {}
vim.fn.setreg(self.reg, type(content) == "table" and content or { content }, "v")
if opts.notify ~= false then
notify.info(message or string.format("Copied %s to %s clipboard!", content, self.clipboard_name))
end
-- manually firing TextYankPost does not set vim.v.event
-- workaround: create a scratch buffer with the clipboard contents and send a yank command
local temp_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_text(temp_buf, 0, 0, 0, 0, { content })
vim.api.nvim_buf_call(temp_buf, function()
vim.cmd(string.format('normal! "%sy$', reg))
end)
vim.api.nvim_buf_delete(temp_buf, {})
notify.info(string.format("Copied %s to %s clipboard!", content, clipboard_name))
end
---@param node Node
function Clipboard:copy_filename(node)
local content
if node.name == ".." then
-- root
content = vim.fn.fnamemodify(self.explorer.absolute_path, ":t")
self:copy_to_reg(vim.fn.fnamemodify(self.explorer.absolute_path, ":t"))
else
-- node
content = node.name
self:copy_to_reg(node.name)
end
end
---@private
---@param node Node
---@return string
function Clipboard:get_absolute_path(node)
if node.name == ".." then
node = self.explorer
end
self:copy_to_reg(content)
end
---@param node Node
function Clipboard:copy_basename(node)
local content
if node.name == ".." then
-- root
content = vim.fn.fnamemodify(self.explorer.absolute_path, ":t:r")
else
-- node
content = vim.fn.fnamemodify(node.name, ":r")
end
self:copy_to_reg(content)
local absolute_path = node.absolute_path
return node.nodes ~= nil and utils.path_add_trailing(absolute_path) or absolute_path
end
---@param node Node
---@return string|nil
function Clipboard:copy_path(node)
local content
if node.name == ".." then
-- root
content = utils.path_add_trailing ""
self:copy_to_reg(utils.path_add_trailing(""))
else
-- node
local absolute_path = node.absolute_path
@@ -371,35 +520,66 @@ function Clipboard:copy_path(node)
end
local relative_path = utils.path_relative(absolute_path, cwd)
content = node.nodes ~= nil and utils.path_add_trailing(relative_path) or relative_path
if node:is(DirectoryNode) then
self:copy_to_reg(utils.path_add_trailing(relative_path))
else
self:copy_to_reg(relative_path)
end
end
end
---@param node_or_nodes Node|Node[]
---@param opts? { notify?: boolean }
function Clipboard:copy_absolute_path(node_or_nodes, opts)
opts = opts and opts or {}
local content
local is_single = self:is_nodes_array(node_or_nodes) == false
if is_single then
local node = #node_or_nodes == 1 and node_or_nodes[0] or node_or_nodes
content = self:get_absolute_path(node)
else
node_or_nodes = utils.filter_descendant_nodes(node_or_nodes)
content = {}
for _, node in ipairs(node_or_nodes) do
table.insert(content, self:get_absolute_path(node))
end
end
self:copy_to_reg(content)
if content ~= nil then
local message = nil
if not is_single then
message = string.format("%s %s copied to register", #content, #content > 1 and "absolute paths" or "absolute path")
end
self:copy_to_reg(content, message, opts)
end
end
---@param node Node
function Clipboard:copy_absolute_path(node)
function Clipboard:copy_basename(node)
if node.name == ".." then
node = self.explorer
-- root
self:copy_to_reg(vim.fn.fnamemodify(node.explorer.absolute_path, ":t:r"))
else
-- node
self:copy_to_reg(vim.fn.fnamemodify(node.name, ":r"))
end
local absolute_path = node.absolute_path
local content = node.nodes ~= nil and utils.path_add_trailing(absolute_path) or absolute_path
self:copy_to_reg(content)
end
---Node is cut. Will not be copied.
---@param node Node
---@return boolean
function Clipboard:is_cut(node)
return vim.tbl_contains(self.data[ACTION.cut], node)
return vim.tbl_contains(self.data.cut, node)
end
---Node is copied. Will not be cut.
---@param node Node
---@return boolean
function Clipboard:is_copied(node)
return vim.tbl_contains(self.data[ACTION.copy], node)
return vim.tbl_contains(self.data.copy, node)
end
return Clipboard

View File

@@ -1,22 +1,24 @@
local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events"
local lib = require "nvim-tree.lib"
local core = require "nvim-tree.core"
local notify = require "nvim-tree.notify"
local utils = require("nvim-tree.utils")
local events = require("nvim-tree.events")
local core = require("nvim-tree.core")
local notify = require("nvim-tree.notify")
local find_file = require("nvim-tree.actions.finders.find-file").fn
local FileNode = require("nvim-tree.node.file")
local DirectoryNode = require("nvim-tree.node.directory")
local M = {}
---@param file string
local function create_and_notify(file)
events._dispatch_will_create_file(file)
local ok, fd = pcall(vim.loop.fs_open, file, "w", 420)
local ok, fd = pcall(vim.uv.fs_open, file, "w", 420)
if not ok or type(fd) ~= "number" then
notify.error("Couldn't create file " .. notify.render_path(file))
return
end
vim.loop.fs_close(fd)
vim.uv.fs_close(fd)
events._dispatch_file_created(file)
end
@@ -30,34 +32,21 @@ local function get_num_nodes(iter)
return i
end
---@param node Node
---@return string
local function get_containing_folder(node)
if node.nodes ~= nil then
return utils.path_add_trailing(node.absolute_path)
end
local node_name_size = #(node.name or "")
return node.absolute_path:sub(0, -node_name_size - 1)
end
---@param node Node|nil
---@param node Node?
function M.fn(node)
local cwd = core.get_cwd()
if cwd == nil then
node = node or core.get_explorer()
if not node then
return
end
node = node and lib.get_last_group_node(node)
if not node or node.name == ".." then
node = {
absolute_path = cwd,
name = "",
nodes = core.get_explorer().nodes,
open = true,
}
local dir = node:is(FileNode) and node.parent or node:as(DirectoryNode)
if not dir then
return
end
local containing_folder = get_containing_folder(node)
dir = dir:last_group_node()
local containing_folder = utils.path_add_trailing(dir.absolute_path)
local input_opts = {
prompt = "Create file ",
@@ -72,7 +61,7 @@ function M.fn(node)
end
if utils.file_exists(new_file_path) then
notify.warn "Cannot create: file already exists"
notify.warn("Cannot create: file already exists")
return
end
@@ -87,15 +76,15 @@ function M.fn(node)
for path in utils.path_split(new_file_path) do
idx = idx + 1
local p = utils.path_remove_trailing(path)
if #path_to_create == 0 and vim.fn.has "win32" == 1 then
path_to_create = utils.path_join { p, path_to_create }
if #path_to_create == 0 and vim.fn.has("win32") == 1 then
path_to_create = utils.path_join({ p, path_to_create })
else
path_to_create = utils.path_join { path_to_create, p }
path_to_create = utils.path_join({ path_to_create, p })
end
if is_last_path_file and idx == num_nodes then
create_and_notify(path_to_create)
elseif not utils.file_exists(path_to_create) then
local success = vim.loop.fs_mkdir(path_to_create, 493)
local success = vim.uv.fs_mkdir(path_to_create, 493)
if not success then
notify.error("Could not create folder " .. notify.render_path(path_to_create))
is_error = true

View File

@@ -1,14 +0,0 @@
local M = {}
M.create_file = require "nvim-tree.actions.fs.create-file"
M.remove_file = require "nvim-tree.actions.fs.remove-file"
M.rename_file = require "nvim-tree.actions.fs.rename-file"
M.trash = require "nvim-tree.actions.fs.trash"
function M.setup(opts)
M.remove_file.setup(opts)
M.rename_file.setup(opts)
M.trash.setup(opts)
end
return M

View File

@@ -1,9 +1,14 @@
local core = require "nvim-tree.core"
local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events"
local view = require "nvim-tree.view"
local lib = require "nvim-tree.lib"
local notify = require "nvim-tree.notify"
local core = require("nvim-tree.core")
local utils = require("nvim-tree.utils")
local events = require("nvim-tree.events")
local lib = require("nvim-tree.lib")
local notify = require("nvim-tree.notify")
local config = require("nvim-tree.config")
local DirectoryLinkNode = require("nvim-tree.node.directory-link")
local DirectoryNode = require("nvim-tree.node.directory")
local Node = require("nvim-tree.node")
local RootNode = require("nvim-tree.node.root")
local M = {
config = {},
@@ -11,11 +16,18 @@ local M = {
---@param windows integer[]
local function close_windows(windows)
-- Prevent from closing when the win count equals 1 or 2,
-- where the win to remove could be the last opened.
-- For details see #2503.
if view.View.float.enable and #vim.api.nvim_list_wins() < 3 then
return
-- When floating, prevent closing the last non-floating window.
-- For details see #2503, #3187.
if config.g.view.float.enable then
local non_float_count = 0
for _, win in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_get_config(win).relative == "" then
non_float_count = non_float_count + 1
end
end
if non_float_count <= 1 then
return
end
end
for _, window in ipairs(windows) do
@@ -27,19 +39,19 @@ end
---@param absolute_path string
local function clear_buffer(absolute_path)
local bufs = vim.fn.getbufinfo { bufloaded = 1, buflisted = 1 }
local bufs = vim.fn.getbufinfo({ bufloaded = 1, buflisted = 1 })
for _, buf in pairs(bufs) do
if buf.name == absolute_path then
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 config.g.view.float.enable) then
vim.api.nvim_set_current_win(buf.windows[1])
vim.cmd ":bn"
vim.cmd(":bn")
end
vim.api.nvim_buf_delete(buf.bufnr, { force = true })
if not view.View.float.quit_on_focus_loss then
if not config.g.view.float.quit_on_focus_loss then
vim.api.nvim_set_current_win(tree_winnr)
end
if M.config.actions.remove_file.close_window then
if config.g.actions.remove_file.close_window then
close_windows(buf.windows)
end
return
@@ -50,31 +62,38 @@ end
---@param cwd string
---@return boolean|nil
local function remove_dir(cwd)
local handle, err = vim.loop.fs_scandir(cwd)
local handle, err = vim.uv.fs_scandir(cwd)
if not handle then
notify.error(err)
return
end
while true do
local name, _ = vim.loop.fs_scandir_next(handle)
local name, _ = vim.uv.fs_scandir_next(handle)
if not name then
break
end
local new_cwd = utils.path_join { cwd, name }
local new_cwd = utils.path_join({ cwd, name })
-- 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
local stat = vim.uv.fs_stat(new_cwd)
-- TODO remove once 0.12 is the minimum neovim version
-- path incorrectly specified as an integer, fixed upstream for neovim 0.12 https://github.com/neovim/neovim/pull/33872
---@diagnostic disable-next-line: param-type-mismatch
local lstat = vim.uv.fs_lstat(new_cwd)
if type == "directory" then
local type = stat and stat.type or nil
-- Checks if file is a link file to ensure deletion of the symlink instead of the file it points to
local ltype = lstat and lstat.type or nil
if type == "directory" and ltype ~= "link" then
local success = remove_dir(new_cwd)
if not success then
return false
end
else
local success = vim.loop.fs_unlink(new_cwd)
local success = vim.uv.fs_unlink(new_cwd)
if not success then
return false
end
@@ -82,64 +101,58 @@ local function remove_dir(cwd)
end
end
return vim.loop.fs_rmdir(cwd)
return vim.uv.fs_rmdir(cwd)
end
--- Remove a node, notify errors, dispatch events
---@param node Node
---@return boolean success
function M.remove(node)
local notify_node = notify.render_path(node.absolute_path)
if node.nodes ~= nil and not node.link_to then
if node:is(DirectoryNode) and not node:is(DirectoryLinkNode) then
local success = remove_dir(node.absolute_path)
if not success then
notify.error("Could not remove " .. notify_node)
return
return false
end
events._dispatch_folder_removed(node.absolute_path)
else
events._dispatch_will_remove_file(node.absolute_path)
local success = vim.loop.fs_unlink(node.absolute_path)
local success = vim.uv.fs_unlink(node.absolute_path)
if not success then
notify.error("Could not remove " .. notify_node)
return
return false
end
events._dispatch_file_removed(node.absolute_path)
clear_buffer(node.absolute_path)
end
notify.info(notify_node .. " was properly removed.")
return true
end
---Remove a single node with confirmation.
---@param node Node
function M.fn(node)
if node.name == ".." then
local function remove_one(node)
if node:is(RootNode) then
return
end
local function do_remove()
M.remove(node)
if M.remove(node) then
notify.info(notify.render_path(node.absolute_path) .. " was properly removed.")
end
local explorer = core.get_explorer()
if not M.config.filesystem_watchers.enable and explorer then
if not config.g.filesystem_watchers.enable and explorer then
explorer:reload_explorer()
end
end
if M.config.ui.confirm.remove then
if config.g.ui.confirm.remove then
local prompt_select = "Remove " .. node.name .. "?"
local prompt_input, items_short, items_long
if M.config.ui.confirm.default_yes then
prompt_input = prompt_select .. " Y/n: "
items_short = { "", "n" }
items_long = { "Yes", "No" }
else
prompt_input = prompt_select .. " y/N: "
items_short = { "", "y" }
items_long = { "No", "Yes" }
end
local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, config.g.ui.confirm.default_yes)
lib.prompt(prompt_input, prompt_select, items_short, items_long, "nvimtree_remove", function(item_short)
utils.clear_prompt()
if item_short == "y" or item_short == (M.config.ui.confirm.default_yes and "") then
if item_short == "y" or item_short == (config.g.ui.confirm.default_yes and "") then
do_remove()
end
end)
@@ -148,10 +161,53 @@ function M.fn(node)
end
end
function M.setup(opts)
M.config.ui = opts.ui
M.config.actions = opts.actions
M.config.filesystem_watchers = opts.filesystem_watchers
---Remove multiple nodes with a single confirmation prompt.
---@param nodes Node[]
local function remove_many(nodes)
if #nodes == 0 then
return
end
nodes = utils.filter_descendant_nodes(nodes)
local function execute()
local removed = 0
for _, node in ipairs(nodes) do
if not node:is(RootNode) and M.remove(node) then
removed = removed + 1
end
end
if removed > 0 then
notify.info(string.format("%d nodes properly removed.", removed))
end
local explorer = core.get_explorer()
if not config.g.filesystem_watchers.enable and explorer then
explorer:reload_explorer()
end
end
if config.g.ui.confirm.remove then
local prompt_select = string.format("Remove %d selected?", #nodes)
local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, config.g.ui.confirm.default_yes)
lib.prompt(prompt_input, prompt_select, items_short, items_long, "nvimtree_remove", function(item_short)
utils.clear_prompt()
if item_short == "y" or item_short == (config.g.ui.confirm.default_yes and "") then
execute()
end
end)
else
execute()
end
end
---@param node_or_nodes Node|Node[]
function M.fn(node_or_nodes)
if type(node_or_nodes) == "table" and node_or_nodes.is and node_or_nodes:is(Node) then
remove_one(node_or_nodes)
else
remove_many(node_or_nodes)
end
end
return M

View File

@@ -1,11 +1,14 @@
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events"
local notify = require "nvim-tree.notify"
local core = require("nvim-tree.core")
local utils = require("nvim-tree.utils")
local events = require("nvim-tree.events")
local notify = require("nvim-tree.notify")
local config = require("nvim-tree.config")
local lib = require("nvim-tree.lib")
local find_file = require("nvim-tree.actions.finders.find-file").fn
local DirectoryNode = require("nvim-tree.node.directory")
local M = {
config = {},
}
@@ -32,7 +35,7 @@ local function err_fmt(from, to, reason)
end
local function rename_file_exists(node, to)
if not utils.is_macos then
if not config.os.macos then
return utils.file_exists(to)
end
@@ -45,7 +48,9 @@ end
---@param node Node
---@param to string
function M.rename(node, to)
---@param opts? { notify?: boolean }
function M.rename(node, to, opts)
opts = opts and opts or {}
local notify_from = notify.render_path(node.absolute_path)
local notify_to = notify.render_path(to)
@@ -64,22 +69,22 @@ function M.rename(node, to)
idx = idx + 1
local p = utils.path_remove_trailing(path)
if #path_to_create == 0 and vim.fn.has "win32" == 1 then
path_to_create = utils.path_join { p, path_to_create }
if #path_to_create == 0 and vim.fn.has("win32") == 1 then
path_to_create = utils.path_join({ p, path_to_create })
else
path_to_create = utils.path_join { path_to_create, p }
path_to_create = utils.path_join({ path_to_create, p })
end
if idx == num_nodes then
events._dispatch_will_rename_node(node.absolute_path, to)
local success, err = vim.loop.fs_rename(node.absolute_path, to)
local success, err = vim.uv.fs_rename(node.absolute_path, to)
if not success then
notify.warn(err_fmt(notify_from, notify_to, err))
return
end
elseif not rename_file_exists(notify_from, path_to_create) then
local success = vim.loop.fs_mkdir(path_to_create, 493)
local success = vim.uv.fs_mkdir(path_to_create, 493)
if not success then
notify.error("Could not create folder " .. notify.render_path(path_to_create))
is_error = true
@@ -90,85 +95,126 @@ function M.rename(node, to)
end
if not is_error then
notify.info(string.format("%s -> %s", notify_from, notify_to))
if opts.notify ~= false then
notify.info(string.format("%s -> %s", notify_from, notify_to))
end
utils.rename_loaded_buffers(node.absolute_path, to)
events._dispatch_node_renamed(node.absolute_path, to)
end
end
---@param default_modifier string|nil
---@return fun(node: Node, modifier: string)
function M.fn(default_modifier)
default_modifier = default_modifier or ":t"
return function(node, modifier)
if type(node) ~= "table" then
node = lib.get_node_at_cursor()
end
if node == nil then
return
end
if type(modifier) ~= "string" then
modifier = default_modifier
end
-- support for only specific modifiers have been implemented
if not ALLOWED_MODIFIERS[modifier] then
notify.warn("Modifier " .. vim.inspect(modifier) .. " is not in allowed list : " .. table.concat(ALLOWED_MODIFIERS, ","))
return
end
node = lib.get_last_group_node(node)
if node.name == ".." then
return
end
local namelen = node.name:len()
local directory = node.absolute_path:sub(0, namelen * -1 - 1)
local default_path
local prepend = ""
local append = ""
default_path = vim.fn.fnamemodify(node.absolute_path, modifier)
if modifier:sub(0, 2) == ":t" then
prepend = directory
end
if modifier == ":t:r" then
local extension = vim.fn.fnamemodify(node.name, ":e")
append = extension:len() == 0 and "" or "." .. extension
end
if modifier == ":p:h" then
default_path = default_path .. "/"
end
local input_opts = {
prompt = "Rename to ",
default = default_path,
completion = "file",
}
vim.ui.input(input_opts, function(new_file_path)
utils.clear_prompt()
if not new_file_path then
return
end
M.rename(node, prepend .. new_file_path .. append)
if not M.config.filesystem_watchers.enable then
local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end
find_file(utils.path_remove_trailing(new_file_path))
end)
---@param node Node
---@param modifier? string
local function prompt_to_rename(node, modifier)
if not modifier or type(modifier) ~= "string" then
modifier = ":t"
end
local explorer = core.get_explorer()
if not explorer then
return
end
if type(node) ~= "table" then
local node_at_cursor = explorer:get_node_at_cursor()
if not node_at_cursor then
return
end
node = node_at_cursor
end
-- support for only specific modifiers have been implemented
if not ALLOWED_MODIFIERS[modifier] then
notify.warn("Modifier " .. vim.inspect(modifier) .. " is not in allowed list : " .. table.concat(ALLOWED_MODIFIERS, ","))
return
end
local dir = node:as(DirectoryNode)
if dir then
node = dir:last_group_node()
end
if node.name == ".." then
return
end
local namelen = node.name:len()
local directory = node.absolute_path:sub(0, namelen * -1 - 1)
local default_path
local prepend = ""
local append = ""
default_path = vim.fn.fnamemodify(node.absolute_path, modifier)
if modifier:sub(0, 2) == ":t" then
prepend = directory
end
if modifier == ":t:r" then
local extension = vim.fn.fnamemodify(node.name, ":e")
append = extension:len() == 0 and "" or "." .. extension
end
if modifier == ":p:h" then
default_path = default_path .. "/"
end
local input_opts = {
prompt = "Rename to ",
default = default_path,
completion = "file",
}
vim.ui.input(input_opts, function(new_file_path)
utils.clear_prompt()
if not new_file_path then
return
end
local full_new_path = prepend .. new_file_path .. append
M.rename(node, full_new_path)
if not config.g.filesystem_watchers.enable then
explorer:reload_explorer()
end
find_file(utils.path_remove_trailing(full_new_path))
end)
end
function M.setup(opts)
M.config.filesystem_watchers = opts.filesystem_watchers
---@param node Node
function M.rename_node(node)
prompt_to_rename(node, ":t")
end
---@param node Node
function M.rename_sub(node)
prompt_to_rename(node, ":p:h")
end
---@param node Node
function M.rename_basename(node)
prompt_to_rename(node, ":t:r")
end
---@param node Node
function M.rename_full(node)
prompt_to_rename(node, ":p")
end
---@param nodes Node[]
---@param old_part string
---@param new_part string
function M.bulk_rename(nodes, old_part, new_part)
local prompt_select = string.format("Substitute '%s' for '%s' in %s files?", old_part, new_part, #nodes)
lib.prompt(prompt_select .. " Y/n: ", prompt_select, ({ "", "y" }), { "No", "Yes" }, "nvimtree_bulk_rename", function(answer)
utils.clear_prompt()
if answer == "n" then return end
for _, node in ipairs(nodes) do
local new_name = node.name:gsub(old_part, new_part)
local new_full_path = vim.fn.fnamemodify(node.absolute_path, ":h") .. "/" .. new_name
M.rename(node, new_full_path, { notify = false })
end
notify.info(string.format("%s nodes substituted from %s -> %s", #nodes, old_part, new_part))
end)
end
return M

View File

@@ -1,23 +1,28 @@
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local notify = require "nvim-tree.notify"
local core = require("nvim-tree.core")
local lib = require("nvim-tree.lib")
local notify = require("nvim-tree.notify")
local utils = require("nvim-tree.utils")
local events = require("nvim-tree.events")
local config = require("nvim-tree.config")
local DirectoryLinkNode = require("nvim-tree.node.directory-link")
local DirectoryNode = require("nvim-tree.node.directory")
local Node = require("nvim-tree.node")
local RootNode = require("nvim-tree.node.root")
local M = {
config = {},
}
local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events"
---@param absolute_path string
local function clear_buffer(absolute_path)
local bufs = vim.fn.getbufinfo { bufloaded = 1, buflisted = 1 }
local bufs = vim.fn.getbufinfo({ bufloaded = 1, buflisted = 1 })
for _, buf in pairs(bufs) do
if buf.name == absolute_path then
if buf.hidden == 0 and #bufs > 1 then
local winnr = vim.api.nvim_get_current_win()
vim.api.nvim_set_current_win(buf.windows[1])
vim.cmd ":bn"
vim.cmd(":bn")
vim.api.nvim_set_current_win(winnr)
end
vim.api.nvim_buf_delete(buf.bufnr, {})
@@ -28,9 +33,9 @@ end
---@param node Node
function M.remove(node)
local binary = M.config.trash.cmd:gsub(" .*$", "")
local binary = config.g.trash.cmd:gsub(" .*$", "")
if vim.fn.executable(binary) == 0 then
notify.warn(string.format("trash.cmd '%s' is not an executable.", M.config.trash.cmd))
notify.warn(string.format("trash.cmd '%s' is not an executable.", config.g.trash.cmd))
return
end
@@ -41,27 +46,27 @@ function M.remove(node)
-- trashes a path (file or folder)
local function trash_path(on_exit)
local need_sync_wait = utils.is_windows
local job = vim.fn.jobstart(M.config.trash.cmd .. " " .. vim.fn.shellescape(node.absolute_path), {
local need_sync_wait = config.os.windows
local job = vim.fn.jobstart(config.g.trash.cmd .. " " .. vim.fn.shellescape(node.absolute_path), {
detach = not need_sync_wait,
on_exit = on_exit,
on_stderr = on_stderr,
})
if need_sync_wait then
vim.fn.jobwait { job }
vim.fn.jobwait({ job })
end
end
local explorer = core.get_explorer()
if node.nodes ~= nil and not node.link_to then
if node:is(DirectoryNode) and not node:is(DirectoryLinkNode) then
trash_path(function(_, rc)
if rc ~= 0 then
notify.warn("trash failed: " .. err_msg .. "; please see :help nvim-tree.trash")
return
end
events._dispatch_folder_removed(node.absolute_path)
if not M.config.filesystem_watchers.enable and explorer then
if not config.g.filesystem_watchers.enable and explorer then
explorer:reload_explorer()
end
end)
@@ -74,16 +79,17 @@ function M.remove(node)
end
events._dispatch_file_removed(node.absolute_path)
clear_buffer(node.absolute_path)
if not M.config.filesystem_watchers.enable and explorer then
if not config.g.filesystem_watchers.enable and explorer then
explorer:reload_explorer()
end
end)
end
end
---Trash a single node with confirmation.
---@param node Node
function M.fn(node)
if node.name == ".." then
local function trash_one(node)
if node:is(RootNode) then
return
end
@@ -91,23 +97,13 @@ function M.fn(node)
M.remove(node)
end
if M.config.ui.confirm.trash then
if config.g.ui.confirm.trash then
local prompt_select = "Trash " .. node.name .. "?"
local prompt_input, items_short, items_long
if M.config.ui.confirm.default_yes then
prompt_input = prompt_select .. " Y/n: "
items_short = { "", "n" }
items_long = { "Yes", "No" }
else
prompt_input = prompt_select .. " y/N: "
items_short = { "", "y" }
items_long = { "No", "Yes" }
end
local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, config.g.ui.confirm.default_yes)
lib.prompt(prompt_input, prompt_select, items_short, items_long, "nvimtree_trash", function(item_short)
utils.clear_prompt()
if item_short == "y" or item_short == (M.config.ui.confirm.default_yes and "") then
if item_short == "y" or item_short == (config.g.ui.confirm.default_yes and "") then
do_trash()
end
end)
@@ -116,10 +112,45 @@ function M.fn(node)
end
end
function M.setup(opts)
M.config.ui = opts.ui
M.config.trash = opts.trash
M.config.filesystem_watchers = opts.filesystem_watchers
---Trash multiple nodes with a single confirmation prompt.
---@param nodes Node[]
local function trash_many(nodes)
if #nodes == 0 then
return
end
nodes = utils.filter_descendant_nodes(nodes)
local function execute()
for _, node in ipairs(nodes) do
if not node:is(RootNode) then
M.remove(node)
end
end
end
if config.g.ui.confirm.trash then
local prompt_select = string.format("Trash %d selected?", #nodes)
local prompt_input, items_short, items_long = utils.confirm_prompt(prompt_select, config.g.ui.confirm.default_yes)
lib.prompt(prompt_input, prompt_select, items_short, items_long, "nvimtree_trash", function(item_short)
utils.clear_prompt()
if item_short == "y" or item_short == (config.g.ui.confirm.default_yes and "") then
execute()
end
end)
else
execute()
end
end
---@param node_or_nodes Node|Node[]
function M.fn(node_or_nodes)
if type(node_or_nodes) == "table" and node_or_nodes.is and node_or_nodes:is(Node) then
trash_one(node_or_nodes)
else
trash_many(node_or_nodes)
end
end
return M

View File

@@ -1,17 +0,0 @@
local M = {}
M.finders = require "nvim-tree.actions.finders"
M.fs = require "nvim-tree.actions.fs"
M.moves = require "nvim-tree.actions.moves"
M.node = require "nvim-tree.actions.node"
M.root = require "nvim-tree.actions.root"
M.tree = require "nvim-tree.actions.tree"
function M.setup(opts)
M.fs.setup(opts)
M.node.setup(opts)
M.root.setup(opts)
M.tree.setup(opts)
end
return M

View File

@@ -1,7 +0,0 @@
local M = {}
M.item = require "nvim-tree.actions.moves.item"
M.parent = require "nvim-tree.actions.moves.parent"
M.sibling = require "nvim-tree.actions.moves.sibling"
return M

View File

@@ -1,23 +1,23 @@
local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local explorer_node = require "nvim-tree.explorer.node"
local diagnostics = require "nvim-tree.diagnostics"
local view = require("nvim-tree.view")
local core = require("nvim-tree.core")
local diagnostics = require("nvim-tree.diagnostics")
local FileNode = require("nvim-tree.node.file")
local DirectoryNode = require("nvim-tree.node.directory")
local M = {}
local MAX_DEPTH = 100
---Return the status of the node or nil if no status, depending on the type of
---status.
---@param node table node to inspect
---@param what string type of status
---@param skip_gitignored boolean default false
---@param node Node to inspect
---@param what string? type of status
---@param skip_gitignored boolean? default false
---@return boolean
local function status_is_valid(node, what, skip_gitignored)
if what == "git" then
local git_status = explorer_node.get_git_status(node)
return git_status ~= nil and (not skip_gitignored or git_status[1] ~= "!!")
local git_xy = node:get_git_xy()
return git_xy ~= nil and (not skip_gitignored or git_xy[1] ~= "!!")
elseif what == "diag" then
local diag_status = diagnostics.get_diag_status(node)
return diag_status ~= nil and diag_status.value ~= nil
@@ -29,15 +29,16 @@ local function status_is_valid(node, what, skip_gitignored)
end
---Move to the next node that has a valid status. If none found, don't move.
---@param where string where to move (forwards or backwards)
---@param what string type of status
---@param skip_gitignored boolean default false
local function move(where, what, skip_gitignored)
---@param explorer Explorer
---@param where string? where to move (forwards or backwards)
---@param what string? type of status
---@param skip_gitignored boolean? default false
local function move(explorer, where, what, skip_gitignored)
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 = explorer:get_nodes_by_line(first_node_line)
local iter_start, iter_end, iter_step, cur, first, nex
local cursor = lib.get_cursor_position()
local cursor = explorer:get_cursor_position()
if cursor and cursor[1] < first_node_line then
cur = cursor[1]
end
@@ -65,31 +66,33 @@ local function move(where, what, skip_gitignored)
end
if nex then
view.set_cursor { nex, 0 }
view.set_cursor({ nex, 0 })
elseif vim.o.wrapscan and first then
view.set_cursor { first, 0 }
view.set_cursor({ first, 0 })
end
end
---@param node DirectoryNode
local function expand_node(node)
if not node.open then
-- Expand the node.
-- Should never collapse since we checked open.
lib.expand_or_collapse(node)
node:expand_or_collapse(false)
end
end
--- Move to the next node recursively.
---@param what string type of status
---@param skip_gitignored boolean default false
local function move_next_recursive(what, skip_gitignored)
---@param explorer Explorer
---@param what string? type of status
---@param skip_gitignored? boolean default false
local function move_next_recursive(explorer, what, skip_gitignored)
-- If the current node:
-- * is a directory
-- * and is not the root node
-- * and has a git/diag status
-- * and is not opened
-- expand it.
local node_init = lib.get_node_at_cursor()
local node_init = explorer:get_node_at_cursor()
if not node_init then
return
end
@@ -97,13 +100,14 @@ local function move_next_recursive(what, skip_gitignored)
if node_init.name ~= ".." then -- root node cannot have a status
valid = status_is_valid(node_init, what, skip_gitignored)
end
if node_init.nodes ~= nil and valid and not node_init.open then
lib.expand_or_collapse(node_init)
local node_dir = node_init:as(DirectoryNode)
if node_dir and valid and not node_dir.open then
node_dir:expand_or_collapse(false)
end
move("next", what, skip_gitignored)
move(explorer, "next", what, skip_gitignored)
local node_cur = lib.get_node_at_cursor()
local node_cur = explorer:get_node_at_cursor()
if not node_cur then
return
end
@@ -115,20 +119,15 @@ local function move_next_recursive(what, skip_gitignored)
-- i is used to limit iterations.
local i = 0
local is_dir = node_cur.nodes ~= nil
while is_dir and i < MAX_DEPTH do
expand_node(node_cur)
local dir_cur = node_cur:as(DirectoryNode)
while dir_cur and i < MAX_DEPTH do
expand_node(dir_cur)
move("next", what, skip_gitignored)
move(explorer, "next", what, skip_gitignored)
-- Save current node.
node_cur = lib.get_node_at_cursor()
-- Update is_dir.
if node_cur then
is_dir = node_cur.nodes ~= nil
else
is_dir = false
end
node_cur = explorer:get_node_at_cursor()
dir_cur = node_cur and node_cur:as(DirectoryNode)
i = i + 1
end
@@ -149,24 +148,25 @@ end
--- 4.4) Call a non-recursive prev.
--- 4.5) Save the current node and start back from 4.1.
---
---@param what string type of status
---@param skip_gitignored boolean default false
local function move_prev_recursive(what, skip_gitignored)
---@param explorer Explorer
---@param what string? type of status
---@param skip_gitignored boolean? default false
local function move_prev_recursive(explorer, what, skip_gitignored)
local node_init, node_cur
-- 1)
node_init = lib.get_node_at_cursor()
node_init = explorer:get_node_at_cursor()
if node_init == nil then
return
end
-- 2)
move("prev", what, skip_gitignored)
move(explorer, "prev", what, skip_gitignored)
node_cur = lib.get_node_at_cursor()
node_cur = explorer:get_node_at_cursor()
if node_cur == node_init.parent then
-- 3)
move_prev_recursive(what, skip_gitignored)
move_prev_recursive(explorer, what, skip_gitignored)
else
-- i is used to limit iterations.
local i = 0
@@ -175,31 +175,33 @@ local function move_prev_recursive(what, skip_gitignored)
if
node_cur == nil
or node_cur == node_init -- we didn't move
or not node_cur.nodes -- node is a file
or node_cur:is(FileNode) -- node is a file
then
return
end
-- 4.2)
local node_dir = node_cur
expand_node(node_dir)
local node_dir = node_cur:as(DirectoryNode)
if node_dir then
expand_node(node_dir)
end
-- 4.3)
if node_init.name == ".." then -- root node
view.set_cursor { 1, 0 } -- move to root node (position 1)
view.set_cursor({ 1, 0 }) -- move to root node (position 1)
else
local node_init_line = utils.find_node_line(node_init)
local node_init_line = explorer:find_node_line(node_init)
if node_init_line < 0 then
return
end
view.set_cursor { node_init_line, 0 }
view.set_cursor({ node_init_line, 0 })
end
-- 4.4)
move("prev", what, skip_gitignored)
move(explorer, "prev", what, skip_gitignored)
-- 4.5)
node_cur = lib.get_node_at_cursor()
node_cur = explorer:get_node_at_cursor()
i = i + 1
end
@@ -207,36 +209,83 @@ local function move_prev_recursive(what, skip_gitignored)
end
---@class NavigationItemOpts
---@field where string
---@field what string
---@field where string?
---@field what string?
---@field skip_gitignored boolean?
---@field recurse boolean?
---@param opts NavigationItemOpts
---@return fun()
function M.fn(opts)
return function()
local recurse = false
local skip_gitignored = false
local function item(opts)
local explorer = core.get_explorer()
if not explorer then
return
end
-- recurse only valid for git and diag moves.
if (opts.what == "git" or opts.what == "diag") and opts.recurse ~= nil then
recurse = opts.recurse
end
local recurse = false
if opts.skip_gitignored ~= nil then
skip_gitignored = opts.skip_gitignored
end
-- recurse only valid for git and diag moves.
if (opts.what == "git" or opts.what == "diag") and opts.recurse ~= nil then
recurse = opts.recurse
end
if not recurse then
move(opts.where, opts.what, skip_gitignored)
return
end
if not recurse then
move(explorer, opts.where, opts.what, opts.skip_gitignored)
return
end
if opts.where == "next" then
move_next_recursive(opts.what, skip_gitignored)
elseif opts.where == "prev" then
move_prev_recursive(opts.what, skip_gitignored)
end
if opts.where == "next" then
move_next_recursive(explorer, opts.what, opts.skip_gitignored)
elseif opts.where == "prev" then
move_prev_recursive(explorer, opts.what, opts.skip_gitignored)
end
end
function M.git_next()
item({ where = "next", what = "git" })
end
function M.git_next_skip_gitignored()
item({ where = "next", what = "git", skip_gitignored = true })
end
function M.git_next_recursive()
item({ where = "next", what = "git", recurse = true })
end
function M.git_prev()
item({ where = "prev", what = "git" })
end
function M.git_prev_skip_gitignored()
item({ where = "prev", what = "git", skip_gitignored = true })
end
function M.git_prev_recursive()
item({ where = "prev", what = "git", recurse = true })
end
function M.diagnostics_next()
item({ where = "next", what = "diag" })
end
function M.diagnostics_next_recursive()
item({ where = "next", what = "diag", recurse = true })
end
function M.diagnostics_prev()
item({ where = "prev", what = "diag" })
end
function M.diagnostics_prev_recursive()
item({ where = "prev", what = "diag", recurse = true })
end
function M.opened_next()
item({ where = "next", what = "opened" })
end
function M.opened_prev()
item({ where = "prev", what = "opened" })
end
return M

View File

@@ -1,45 +1,47 @@
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local view = require("nvim-tree.view")
local DirectoryNode = require("nvim-tree.node.directory")
local M = {}
---@param should_close boolean|nil
---@return fun(node: Node): boolean|nil
function M.fn(should_close)
should_close = should_close or false
local explorer = core.get_explorer()
return function(node)
node = lib.get_last_group_node(node)
if should_close and node.open then
node.open = false
if explorer then
explorer.renderer:draw()
end
---@param node Node
---@param should_close boolean
local function move(node, should_close)
local dir = node:as(DirectoryNode)
if dir then
dir = dir:last_group_node()
if should_close and dir.open then
dir.open = false
dir.explorer.renderer:draw()
return
end
end
local parent = utils.get_parent_of_group(node).parent
local parent = (node:get_parent_of_group() or node).parent
if not parent or not parent.parent then
return view.set_cursor { 1, 0 }
end
if not parent or not parent.parent then
view.set_cursor({ 1, 0 })
return
end
local _, line = utils.find_node(core.get_explorer().nodes, function(n)
return n.absolute_path == parent.absolute_path
end)
local _, line = parent.explorer:find_node(function(n)
return n.absolute_path == parent.absolute_path
end)
view.set_cursor { line + 1, 0 }
if should_close then
parent.open = false
if explorer then
explorer.renderer:draw()
end
end
view.set_cursor({ line + 1, 0 })
if should_close then
parent.open = false
parent.explorer.renderer:draw()
end
end
---@param node Node
function M.move(node)
move(node, false)
end
---@param node Node
function M.move_close(node)
move(node, true)
end
return M

View File

@@ -1,53 +1,75 @@
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local Iterator = require "nvim-tree.iterators.node-iterator"
local core = require("nvim-tree.core")
local Iterator = require("nvim-tree.iterators.node-iterator")
local M = {}
---@param direction string
---@return fun(node: Node): nil
function M.fn(direction)
return function(node)
if node.name == ".." or not direction then
return
end
---@param node Node
---@param direction "next"|"prev"|"first"|"last"
local function move(node, direction)
if node.name == ".." or not direction then
return
end
local first, last, next, prev = nil, nil, nil, nil
local found = false
local parent = node.parent or core.get_explorer()
Iterator.builder(parent.nodes)
:recursor(function()
return nil
end)
:applier(function(n)
first = first or n
last = n
if n.absolute_path == node.absolute_path then
found = true
return
end
prev = not found and n or prev
if found and not next then
next = n
end
end)
:iterate()
local explorer = core.get_explorer()
if not explorer then
return
end
local target_node
if direction == "first" then
target_node = first
elseif direction == "last" then
target_node = last
elseif direction == "next" then
target_node = next or first
else
target_node = prev or last
end
local first, last, next, prev = nil, nil, nil, nil
local found = false
local parent = node.parent or explorer
Iterator.builder(parent and parent.nodes or {})
:recursor(function()
return nil
end)
:applier(function(n)
first = first or n
last = n
if n.absolute_path == node.absolute_path then
found = true
return
end
prev = not found and n or prev
if found and not next then
next = n
end
end)
:iterate()
if target_node then
utils.focus_file(target_node.absolute_path)
end
local target_node
if direction == "first" then
target_node = first
elseif direction == "last" then
target_node = last
elseif direction == "next" then
target_node = next or first
else
target_node = prev or last
end
if target_node then
explorer:focus_node_or_parent(target_node)
end
end
---@param node Node
function M.next(node)
move(node, "next")
end
---@param node Node
function M.prev(node)
move(node, "prev")
end
---@param node Node
function M.first(node)
move(node, "first")
end
---@param node Node
function M.last(node)
move(node, "last")
end
return M

View File

@@ -0,0 +1,55 @@
-- Copyright 2019 Yazdani Kiyan under MIT License
local notify = require("nvim-tree.notify")
local M = {}
---@param node Node
---@param opts? nvim_tree.api.node.buffer.RemoveOpts
function M.delete(node, opts)
M.delete_buffer("delete", node.absolute_path, opts)
end
---@param node Node
---@param opts? nvim_tree.api.node.buffer.RemoveOpts
function M.wipe(node, opts)
M.delete_buffer("wipe", node.absolute_path, opts)
end
---@alias ApiNodeDeleteWipeBufferMode '"delete"'|'"wipe"'
---@param mode ApiNodeDeleteWipeBufferMode
---@param filename string
---@param opts? nvim_tree.api.node.buffer.RemoveOpts
function M.delete_buffer(mode, filename, opts)
if type(mode) ~= "string" then
mode = "delete"
end
local buf_fn = vim.cmd.bdelete
if mode == "wipe" then
buf_fn = vim.cmd.bwipe
end
opts = opts or { force = false }
local notify_node = notify.render_path(filename)
-- check if buffer for file at cursor exists and if it is loaded
local bufnr_at_filename = vim.fn.bufnr(filename)
if bufnr_at_filename == -1 or vim.fn.getbufinfo(bufnr_at_filename)[1].loaded == 0 then
notify.info("No loaded buffer coincides with " .. notify_node)
return
end
local force = opts.force
-- check if buffer is modified
local buf_modified = vim.fn.getbufinfo(bufnr_at_filename)[1].changed
if not force and buf_modified == 1 then
notify.error("Buffer for file " .. notify_node .. " is modified")
return
end
buf_fn({ filename, bang = force })
end
return M

View File

@@ -1,4 +1,5 @@
local utils = require "nvim-tree.utils"
local utils = require("nvim-tree.utils")
local config = require("nvim-tree.config")
local M = {}
@@ -38,7 +39,7 @@ local function setup_window(node)
local max_width = vim.fn.max(vim.tbl_map(function(n)
return #n
end, lines))
local open_win_config = vim.tbl_extend("force", M.open_win_config, {
local open_win_config = vim.tbl_extend("force", config.g.actions.file_popup.open_win_config, {
width = max_width + 1,
height = #lines,
noautocmd = true,
@@ -50,14 +51,17 @@ local function setup_window(node)
file_path = node.absolute_path,
}
local bufnr = vim.api.nvim_create_buf(false, true)
vim.bo[bufnr].bufhidden = "wipe"
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
vim.api.nvim_win_set_buf(winnr, bufnr)
end
function M.close_popup()
if current_popup ~= nil then
vim.api.nvim_win_close(current_popup.winnr, true)
vim.cmd "augroup NvimTreeRemoveFilePopup | au! CursorMoved | augroup END"
if vim.api.nvim_win_is_valid(current_popup.winnr) then
vim.api.nvim_win_close(current_popup.winnr, true)
end
vim.cmd("augroup NvimTreeRemoveFilePopup | au! CursorMoved | augroup END")
current_popup = nil
end
@@ -86,8 +90,4 @@ function M.toggle_file_info(node)
})
end
function M.setup(opts)
M.open_win_config = opts.actions.file_popup.open_win_config
end
return M

View File

@@ -1,14 +0,0 @@
local M = {}
M.file_popup = require "nvim-tree.actions.node.file-popup"
M.open_file = require "nvim-tree.actions.node.open-file"
M.run_command = require "nvim-tree.actions.node.run-command"
M.system_open = require "nvim-tree.actions.node.system-open"
function M.setup(opts)
require("nvim-tree.actions.node.system-open").setup(opts)
require("nvim-tree.actions.node.file-popup").setup(opts)
require("nvim-tree.actions.node.open-file").setup(opts)
end
return M

View File

@@ -1,8 +1,16 @@
-- Copyright 2019 Yazdani Kiyan under MIT License
local lib = require "nvim-tree.lib"
local notify = require "nvim-tree.notify"
local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local lib = require("nvim-tree.lib")
local notify = require("nvim-tree.notify")
local utils = require("nvim-tree.utils")
local full_name = require("nvim-tree.renderer.components.full-name")
local view = require("nvim-tree.view")
local config = require("nvim-tree.config")
local DirectoryNode = require("nvim-tree.node.directory")
local FileLinkNode = require("nvim-tree.node.file-link")
local RootNode = require("nvim-tree.node.root")
---@alias NodeOpenFileMode ""|"change_dir"|"drop"|"edit"|"edit_in_place"|"edit_no_picker"|"preview"|"preview_no_picker"|"split"|"split_no_picker"|"tab_drop"|"tabnew"|"toggle_group_empty"|"vsplit"|"vsplit_no_picker"
local M = {}
@@ -25,13 +33,8 @@ local function usable_win_ids()
return vim.tbl_filter(function(id)
local bufid = vim.api.nvim_win_get_buf(id)
for option, v in pairs(M.window_picker.exclude) do
local ok, option_value
if vim.fn.has "nvim-0.10" == 1 then
ok, option_value = pcall(vim.api.nvim_get_option_value, option, { buf = bufid })
else
ok, option_value = pcall(vim.api.nvim_buf_get_option, bufid, option) ---@diagnostic disable-line: deprecated
end
for option, v in pairs(config.g.actions.open_file.window_picker.exclude) do
local ok, option_value = pcall(vim.api.nvim_get_option_value, option, { buf = bufid })
if ok and vim.tbl_contains(v, option_value) then
return false
@@ -39,21 +42,15 @@ local function usable_win_ids()
end
local win_config = vim.api.nvim_win_get_config(id)
return id ~= tree_winid and win_config.focusable and not win_config.external or false
return id ~= tree_winid
and id ~= full_name.popup_win
and win_config.focusable
and not win_config.hide
and not win_config.external
or false
end, win_ids)
end
---Find the first window in the tab that is not NvimTree.
---@return integer -1 if none available
local function first_win_id()
local selectable = usable_win_ids()
if #selectable > 0 then
return selectable[1]
else
return -1
end
end
---Get user to pick a window in the tab that is not NvimTree.
---@return integer|nil -- If a valid window was picked, return its id. If an
--- invalid window was picked / user canceled, return nil. If there are
@@ -69,16 +66,26 @@ local function pick_win_id()
return selectable[1]
end
if #M.window_picker.chars < #selectable then
notify.error(string.format("More windows (%d) than actions.open_file.window_picker.chars (%d).", #selectable, #M.window_picker.chars))
if #config.g.actions.open_file.window_picker.chars < #selectable then
notify.error(string.format("More windows (%d) than actions.open_file.window_picker.chars (%d).", #selectable,
#config.g.actions.open_file.window_picker.chars))
return nil
end
local i = 1
local win_opts = {}
local win_opts_selectable = {}
local win_opts_unselectable = {}
local win_map = {}
local laststatus = vim.o.laststatus
vim.o.laststatus = 2
local fillchars = vim.opt.fillchars:get()
local stl = fillchars.stl
local stlnc = fillchars.stlnc
fillchars.stl = nil
fillchars.stlnc = nil
vim.opt.fillchars = fillchars
fillchars.stl = stl
fillchars.stlnc = stlnc
local tabpage = vim.api.nvim_get_current_tabpage()
local win_ids = vim.api.nvim_tabpage_list_wins(tabpage)
@@ -89,66 +96,42 @@ local function pick_win_id()
if laststatus == 3 then
for _, win_id in ipairs(not_selectable) do
local ok_status, statusline, ok_hl, winhl
local ok_status, statusline = pcall(vim.api.nvim_get_option_value, "statusline", { win = win_id })
if vim.fn.has "nvim-0.10" == 1 then
ok_status, statusline = pcall(vim.api.nvim_get_option_value, "statusline", { win = win_id })
ok_hl, winhl = pcall(vim.api.nvim_get_option_value, "winhl", { win = win_id })
else
ok_status, statusline = pcall(vim.api.nvim_win_get_option, win_id, "statusline") ---@diagnostic disable-line: deprecated
ok_hl, winhl = pcall(vim.api.nvim_win_get_option, win_id, "winhl") ---@diagnostic disable-line: deprecated
end
win_opts[win_id] = {
win_opts_unselectable[win_id] = {
statusline = ok_status and statusline or "",
winhl = ok_hl and winhl or "",
}
-- Clear statusline for windows not selectable
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value("statusline", " ", { win = win_id })
else
vim.api.nvim_win_set_option(win_id, "statusline", " ") ---@diagnostic disable-line: deprecated
end
vim.api.nvim_set_option_value("statusline", " ", { win = win_id })
end
end
-- Setup UI
for _, id in ipairs(selectable) do
local char = M.window_picker.chars:sub(i, i)
local char = config.g.actions.open_file.window_picker.chars:sub(i, i)
local ok_status, statusline, ok_hl, winhl
if vim.fn.has "nvim-0.10" == 1 then
ok_status, statusline = pcall(vim.api.nvim_get_option_value, "statusline", { win = id })
ok_hl, winhl = pcall(vim.api.nvim_get_option_value, "winhl", { win = id })
else
ok_status, statusline = pcall(vim.api.nvim_win_get_option, id, "statusline") ---@diagnostic disable-line: deprecated
ok_hl, winhl = pcall(vim.api.nvim_win_get_option, id, "winhl") ---@diagnostic disable-line: deprecated
end
local ok_status, statusline = pcall(vim.api.nvim_get_option_value, "statusline", { win = id })
local ok_hl, winhl = pcall(vim.api.nvim_get_option_value, "winhl", { win = id })
win_opts[id] = {
win_opts_selectable[id] = {
statusline = ok_status and statusline or "",
winhl = ok_hl and winhl or "",
}
win_map[char] = id
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value("statusline", "%=" .. char .. "%=", { win = id })
vim.api.nvim_set_option_value("winhl", "StatusLine:NvimTreeWindowPicker,StatusLineNC:NvimTreeWindowPicker", { win = id })
else
vim.api.nvim_win_set_option(id, "statusline", "%=" .. char .. "%=") ---@diagnostic disable-line: deprecated
vim.api.nvim_win_set_option(id, "winhl", "StatusLine:NvimTreeWindowPicker,StatusLineNC:NvimTreeWindowPicker") ---@diagnostic disable-line: deprecated
end
vim.api.nvim_set_option_value("statusline", "%=" .. char .. "%=", { win = id })
vim.api.nvim_set_option_value("winhl", "StatusLine:NvimTreeWindowPicker,StatusLineNC:NvimTreeWindowPicker", { win = id })
i = i + 1
if i > #M.window_picker.chars then
if i > #config.g.actions.open_file.window_picker.chars then
break
end
end
vim.cmd "redraw"
vim.cmd("redraw")
if vim.opt.cmdheight._value ~= 0 then
print "Pick window: "
print("Pick window: ")
end
local _, resp = pcall(get_user_input_char)
resp = (resp or ""):upper()
@@ -156,12 +139,8 @@ local function pick_win_id()
-- Restore window options
for _, id in ipairs(selectable) do
for opt, value in pairs(win_opts[id]) do
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value(opt, value, { win = id })
else
vim.api.nvim_win_set_option(id, opt, value) ---@diagnostic disable-line: deprecated
end
for opt, value in pairs(win_opts_selectable[id]) do
vim.api.nvim_set_option_value(opt, value, { win = id })
end
end
@@ -169,20 +148,17 @@ local function pick_win_id()
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
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value(opt, value, { win = id })
else
vim.api.nvim_win_set_option(id, opt, value) ---@diagnostic disable-line: deprecated
end
for opt, value in pairs(win_opts_unselectable[id]) do
vim.api.nvim_set_option_value(opt, value, { win = id })
end
end
end
end
vim.o.laststatus = laststatus
vim.opt.fillchars = fillchars
if not vim.tbl_contains(vim.split(M.window_picker.chars, ""), resp) then
if not vim.tbl_contains(vim.split(config.g.actions.open_file.window_picker.chars, ""), resp) then
return
end
@@ -190,30 +166,38 @@ local function pick_win_id()
end
local function open_file_in_tab(filename)
if M.quit_on_open then
if config.g.actions.open_file.quit_on_open then
view.close()
end
if M.relative_path then
if config.g.actions.open_file.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("tabe " .. vim.fn.fnameescape(filename))
vim.cmd.tabnew()
vim.bo.bufhidden = "wipe"
-- Following vim.fn.tabnew the # buffer may be set to the tree buffer. There is no way to clear the # buffer via vim.fn.setreg as it requires a valid buffer. Clear # by setting it to a new temporary scratch buffer.
if utils.is_nvim_tree_buf(vim.fn.bufnr("#")) then
local tmpbuf = vim.api.nvim_create_buf(false, true)
vim.fn.setreg("#", tmpbuf)
vim.api.nvim_buf_delete(tmpbuf, { force = true })
end
vim.cmd.edit(vim.fn.fnameescape(filename))
end
local function drop(filename)
if M.quit_on_open then
if config.g.actions.open_file.quit_on_open then
view.close()
end
if M.relative_path then
if config.g.actions.open_file.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("drop " .. vim.fn.fnameescape(filename))
end
local function tab_drop(filename)
if M.quit_on_open then
if config.g.actions.open_file.quit_on_open then
view.close()
end
if M.relative_path then
if config.g.actions.open_file.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("tab :drop " .. vim.fn.fnameescape(filename))
@@ -237,17 +221,21 @@ end
local function get_target_winid(mode)
local target_winid
if not M.window_picker.enable or mode == "edit_no_picker" or mode == "preview_no_picker" then
if not config.g.actions.open_file.window_picker.enable or string.find(mode, "no_picker") then
target_winid = lib.target_winid
-- first available window
if not vim.tbl_contains(vim.api.nvim_tabpage_list_wins(0), target_winid) then
target_winid = first_win_id()
local usable_wins = usable_win_ids()
-- first available usable window
if not vim.tbl_contains(usable_wins, target_winid) then
if #usable_wins > 0 then
target_winid = usable_wins[1]
else
target_winid = -1
end
end
else
-- pick a window
if type(M.window_picker.picker) == "function" then
target_winid = M.window_picker.picker()
if type(config.g.actions.open_file.window_picker.picker) == "function" then
target_winid = config.g.actions.open_file.window_picker.picker()
else
target_winid = pick_win_id()
end
@@ -272,6 +260,8 @@ local function set_current_win_no_autocmd(winid, autocmd)
vim.opt.eventignore = eventignore
end
---@param filename string
---@param mode NodeOpenFileMode
local function open_in_new_window(filename, mode)
if type(mode) ~= "string" then
mode = ""
@@ -282,15 +272,20 @@ local function open_in_new_window(filename, mode)
return
end
local position = string.find(mode, "no_picker")
if position then
mode = string.sub(mode, 0, position - 2)
end
-- non-floating, non-nvim-tree windows
local win_ids = vim.tbl_filter(function(id)
local config = vim.api.nvim_win_get_config(id)
local win_config = vim.api.nvim_win_get_config(id)
local bufnr = vim.api.nvim_win_get_buf(id)
return config and config.relative == "" or utils.is_nvim_tree_buf(bufnr)
return win_config and win_config.relative == "" or utils.is_nvim_tree_buf(bufnr)
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 new_window_side = (view.View.side == "right") and "aboveleft" or "belowright"
local new_window_side = (config.g.view.side == "right") and "aboveleft" or "belowright"
-- Target is invalid: create new window
if not vim.tbl_contains(win_ids, target_winid) then
@@ -300,7 +295,7 @@ local function open_in_new_window(filename, mode)
-- No need to split, as we created a new window.
create_new_window = false
if mode:match "split$" then
if mode:match("split$") then
mode = "edit"
end
elseif not vim.o.hidden then
@@ -308,21 +303,16 @@ local function open_in_new_window(filename, mode)
-- modified, and create new split if it is.
local target_bufid = vim.api.nvim_win_get_buf(target_winid)
local modified
if vim.fn.has "nvim-0.10" == 1 then
modified = vim.api.nvim_get_option_value("modified", { buf = target_bufid })
else
modified = vim.api.nvim_buf_get_option(target_bufid, "modified") ---@diagnostic disable-line: deprecated
end
local modified = vim.api.nvim_get_option_value("modified", { buf = target_bufid })
if modified then
if not mode:match "split$" then
if not mode:match("split$") then
mode = "vsplit"
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 config.g.view.float.enable then
-- ignore "WinLeave" autocmd on preview
-- because the registered "WinLeave"
-- will kill the floating window immediately
@@ -332,17 +322,17 @@ local function open_in_new_window(filename, mode)
end
local fname
if M.relative_path then
fname = vim.fn.fnameescape(utils.path_relative(filename, vim.fn.getcwd()))
if config.g.actions.open_file.relative_path then
fname = utils.escape_special_chars(vim.fn.fnameescape(utils.path_relative(filename, vim.fn.getcwd())))
else
fname = vim.fn.fnameescape(filename)
fname = utils.escape_special_chars(vim.fn.fnameescape(filename))
end
local command
if create_new_window then
-- generated from vim.api.nvim_parse_cmd("belowright vsplit foo", {})
command = { cmd = "vsplit", mods = { split = new_window_side }, args = { fname } }
elseif mode:match "split$" then
elseif mode:match("split$") then
command = { cmd = mode, args = { fname } }
else
command = { cmd = "edit", args = { fname } }
@@ -363,37 +353,37 @@ end
local function edit_in_current_buf(filename)
require("nvim-tree.view").abandon_current_window()
if M.relative_path then
if config.g.actions.open_file.relative_path then
filename = utils.path_relative(filename, vim.fn.getcwd())
end
vim.cmd("keepalt keepjumps edit " .. vim.fn.fnameescape(filename))
end
---@param mode string
---@param mode NodeOpenFileMode
---@param filename string
---@return nil
function M.fn(mode, filename)
local fname = utils.escape_special_chars(filename)
if type(mode) ~= "string" then
mode = ""
end
if mode == "tabnew" then
return open_file_in_tab(fname)
return open_file_in_tab(filename)
end
if mode == "drop" then
return drop(fname)
return drop(filename)
end
if mode == "tab_drop" then
return tab_drop(fname)
return tab_drop(filename)
end
if mode == "edit_in_place" then
return edit_in_current_buf(fname)
return edit_in_current_buf(filename)
end
local buf_loaded = is_already_loaded(fname)
local buf_loaded = is_already_loaded(filename)
local found_win = utils.get_win_buf_from_path(filename)
if found_win and (mode == "preview" or mode == "preview_no_picker") then
@@ -401,13 +391,13 @@ function M.fn(mode, filename)
end
if not found_win then
open_in_new_window(fname, mode)
open_in_new_window(filename, mode)
else
vim.api.nvim_set_current_win(found_win)
vim.bo.bufhidden = ""
end
if M.resize_window then
if config.g.actions.open_file.resize_window then
view.resize()
end
@@ -415,19 +405,131 @@ function M.fn(mode, filename)
return on_preview(buf_loaded)
end
if M.quit_on_open then
if config.g.actions.open_file.quit_on_open then
view.close()
end
end
function M.setup(opts)
M.quit_on_open = opts.actions.open_file.quit_on_open
M.resize_window = opts.actions.open_file.resize_window
M.relative_path = opts.experimental.actions.open_file.relative_path
if opts.actions.open_file.window_picker.chars then
opts.actions.open_file.window_picker.chars = tostring(opts.actions.open_file.window_picker.chars):upper()
---@param mode string
---@param node Node
---@param edit_opts nvim_tree.api.node.open.Opts?
local function edit(mode, node, edit_opts)
local file_link = node:as(FileLinkNode)
local path = file_link and file_link.link_to or node.absolute_path
local cur_tabpage = vim.api.nvim_get_current_tabpage()
M.fn(mode, path)
edit_opts = edit_opts or {}
local mode_unsupported_quit_on_open = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
if not mode_unsupported_quit_on_open and edit_opts.quit_on_open then
view.close(cur_tabpage)
end
M.window_picker = opts.actions.open_file.window_picker
local mode_unsupported_focus = mode == "drop" or mode == "tab_drop" or mode == "edit_in_place"
local focus = edit_opts.focus == nil or edit_opts.focus == false
if not mode_unsupported_focus and not focus then
-- if mode == "tabnew" a new tab will be opened and we need to focus back to the previous tab
if mode == "tabnew" then
vim.cmd(":tabprev")
end
view.focus()
end
end
---@param node Node
---@param mode NodeOpenFileMode
---@param toggle_group boolean?
---@param edit_opts nvim_tree.api.node.open.Opts?
local function open_or_expand_or_dir_up(node, mode, toggle_group, edit_opts)
local root = node:as(RootNode)
local dir = node:as(DirectoryNode)
if root or node.name == ".." then
local explorer = require("nvim-tree.core").get_explorer()
if explorer then
explorer:change_dir("..")
end
elseif dir then
dir:expand_or_collapse(toggle_group)
elseif not toggle_group then
edit(mode, node, edit_opts)
end
end
---@param node Node
function M.toggle_group_empty(node)
open_or_expand_or_dir_up(node, "toggle_group_empty", true)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.preview(node, opts)
open_or_expand_or_dir_up(node, "preview", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.preview_no_picker(node, opts)
open_or_expand_or_dir_up(node, "preview_no_picker", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.edit(node, opts)
open_or_expand_or_dir_up(node, "edit", false, opts)
end
---@param node Node
function M.drop(node)
open_or_expand_or_dir_up(node, "drop")
end
---@param node Node
function M.tab_drop(node)
open_or_expand_or_dir_up(node, "tab_drop")
end
---@param node Node
function M.replace_tree_buffer(node)
open_or_expand_or_dir_up(node, "edit_in_place")
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.no_window_picker(node, opts)
open_or_expand_or_dir_up(node, "edit_no_picker", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.vertical(node, opts)
open_or_expand_or_dir_up(node, "vsplit", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.vertical_no_picker(node, opts)
open_or_expand_or_dir_up(node, "vsplit_no_picker", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.horizontal(node, opts)
open_or_expand_or_dir_up(node, "split", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.horizontal_no_picker(node, opts)
open_or_expand_or_dir_up(node, "split_no_picker", false, opts)
end
---@param node Node
---@param opts nvim_tree.api.node.open.Opts?
function M.tab(node, opts)
open_or_expand_or_dir_up(node, "tabnew", false, opts)
end
return M

View File

@@ -1,5 +1,5 @@
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local utils = require("nvim-tree.utils")
local core = require("nvim-tree.core")
local M = {}

View File

@@ -1,56 +1,9 @@
local notify = require "nvim-tree.notify"
local utils = require "nvim-tree.utils"
local notify = require("nvim-tree.notify")
local M = {}
---@param node Node
local function user(node)
if #M.config.system_open.cmd == 0 then
require("nvim-tree.utils").notify.warn "Cannot open file with system application. Unrecognized platform."
return
end
local process = {
cmd = M.config.system_open.cmd,
args = M.config.system_open.args,
errors = "\n",
stderr = vim.loop.new_pipe(false),
}
table.insert(process.args, node.link_to or node.absolute_path)
local opts = {
args = process.args,
stdio = { nil, nil, process.stderr },
detached = true,
}
process.handle, process.pid = vim.loop.spawn(process.cmd, opts, function(code)
process.stderr:read_stop()
process.stderr:close()
process.handle:close()
if code ~= 0 then
notify.warn(string.format("system_open failed with return code %d: %s", code, process.errors))
end
end)
table.remove(process.args)
if not process.handle then
notify.warn(string.format("system_open failed to spawn command '%s': %s", process.cmd, process.pid))
return
end
vim.loop.read_start(process.stderr, function(err, data)
if err then
return
end
if data then
process.errors = process.errors .. data
end
end)
vim.loop.unref(process.handle)
end
---@param node Node
local function native(node)
function M.fn(node)
local _, err = vim.ui.open(node.link_to or node.absolute_path)
-- err only provided on opener executable not found hence logging path is not useful
@@ -59,33 +12,4 @@ local function native(node)
end
end
---@param node Node
function M.fn(node)
M.open(node)
end
-- TODO always use native once 0.10 is the minimum neovim version
function M.setup(opts)
M.config = {}
M.config.system_open = opts.system_open or {}
if vim.fn.has "nvim-0.10" == 1 and #M.config.system_open.cmd == 0 then
M.open = native
else
M.open = user
if #M.config.system_open.cmd == 0 then
if utils.is_windows then
M.config.system_open = {
cmd = "cmd",
args = { "/c", "start", '""' },
}
elseif utils.is_macos then
M.config.system_open.cmd = "open"
elseif utils.is_unix then
M.config.system_open.cmd = "xdg-open"
end
end
end
end
return M

View File

@@ -1,105 +0,0 @@
local log = require "nvim-tree.log"
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local M = {
current_tab = vim.api.nvim_get_current_tabpage(),
}
---@param name string
---@return string|nil
local function clean_input_cwd(name)
name = vim.fn.fnameescape(name)
local cwd = core.get_cwd()
if cwd == nil then
return
end
local root_parent_cwd = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h")
if name == ".." and root_parent_cwd then
return vim.fn.expand(root_parent_cwd)
else
return vim.fn.expand(name)
end
end
---@param new_tabpage integer
---@return boolean
local function is_window_event(new_tabpage)
local is_event_scope_window = vim.v.event.scope == "window" or vim.v.event.changed_window
return is_event_scope_window and new_tabpage == M.current_tab
end
---@param foldername string
---@return boolean
local function prevent_cwd_change(foldername)
local is_same_cwd = foldername == core.get_cwd()
local is_restricted_above = M.options.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1)
return is_same_cwd or is_restricted_above
end
---@param input_cwd string
---@param with_open boolean|nil
function M.fn(input_cwd, with_open)
if not core.get_explorer() then
return
end
local new_tabpage = vim.api.nvim_get_current_tabpage()
if is_window_event(new_tabpage) then
return
end
local foldername = clean_input_cwd(input_cwd)
if foldername == nil or prevent_cwd_change(foldername) then
return
end
M.current_tab = new_tabpage
M.force_dirchange(foldername, with_open)
end
---@param global boolean
---@param path string
local function cd(global, path)
vim.cmd((global and "cd " or "lcd ") .. vim.fn.fnameescape(path))
end
---@return boolean
local function should_change_dir()
return M.options.enable and vim.tbl_isempty(vim.v.event)
end
---@param f function
---@return fun(foldername: string, should_open_view: boolean|nil)
local function add_profiling_to(f)
return function(foldername, should_open_view)
local profile = log.profile_start("change dir %s", foldername)
f(foldername, should_open_view)
log.profile_end(profile)
end
end
M.force_dirchange = add_profiling_to(function(foldername, should_open_view)
local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs
if valid_dir then
if should_change_dir() then
cd(M.options.global, foldername)
end
core.init(foldername)
end
if should_open_view then
require("nvim-tree.lib").open()
else
local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end
end
end)
function M.setup(options)
M.options = options.actions.change_dir
end
return M

View File

@@ -1,22 +0,0 @@
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local M = {}
---@param node Node
function M.fn(node)
if not node or node.name == ".." then
require("nvim-tree.actions.root.change-dir").fn ".."
else
local cwd = core.get_cwd()
if cwd == nil then
return
end
local newdir = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h")
require("nvim-tree.actions.root.change-dir").fn(newdir)
require("nvim-tree.actions.finders.find-file").fn(node.absolute_path)
end
end
return M

View File

@@ -1,10 +0,0 @@
local M = {}
M.change_dir = require "nvim-tree.actions.root.change-dir"
M.dir_up = require "nvim-tree.actions.root.dir-up"
function M.setup(opts)
M.change_dir.setup(opts)
end
return M

View File

@@ -0,0 +1,19 @@
local core = require("nvim-tree.core")
local config = require("nvim-tree.config")
local find_file = require("nvim-tree.actions.tree.find-file")
local M = {}
---@param name? string
function M.fn(name)
local explorer = core.get_explorer()
if name and explorer then
explorer:change_dir(name)
end
if config.g.update_focused_file.update_root.enable then
find_file.fn()
end
end
return M

View File

@@ -0,0 +1,68 @@
local utils = require("nvim-tree.utils")
local core = require("nvim-tree.core")
local config = require("nvim-tree.config")
local M = {}
--- Update the tree root to a directory or the directory containing
---@param path string relative or absolute
---@param bufnr number|nil
function M.fn(path, bufnr)
-- skip if current file is in ignore_list
if type(bufnr) == "number" then
local ft = vim.api.nvim_get_option_value("filetype", { buf = bufnr }) or ""
for _, value in pairs(config.g.update_focused_file.update_root.ignore_list) do
if utils.str_find(path, value) or utils.str_find(ft, value) then
return
end
end
end
-- don't find inexistent
if vim.fn.filereadable(path) == 0 then
return
end
local cwd = core.get_cwd()
if cwd == nil then
return
end
local vim_cwd = vim.fn.getcwd()
local explorer = core.get_explorer()
if not explorer then
return
end
-- test if in vim_cwd
if utils.path_relative(path, vim_cwd) ~= path then
if vim_cwd ~= cwd then
explorer:change_dir(vim_cwd)
end
return
end
-- test if in cwd
if utils.path_relative(path, cwd) ~= path then
return
end
-- otherwise test init_root
if config.g.prefer_startup_root and utils.path_relative(path, config.init_root) ~= path then
explorer:change_dir(config.init_root)
return
end
-- otherwise root_dirs
for _, dir in pairs(config.g.root_dirs) do
dir = vim.fn.fnamemodify(dir, ":p")
if utils.path_relative(path, dir) ~= path then
explorer:change_dir(dir)
return
end
end
-- finally fall back to the folder containing the file
explorer:change_dir(vim.fn.fnamemodify(path, ":p:h"))
end
return M

View File

@@ -0,0 +1,81 @@
local utils = require("nvim-tree.utils")
local core = require("nvim-tree.core")
local Iterator = require("nvim-tree.iterators.node-iterator")
local FileNode = require("nvim-tree.node.file")
local DirectoryNode = require("nvim-tree.node.directory")
local M = {}
---@return fun(path: string): boolean
local function buf_match()
local buffer_paths = vim.tbl_map(function(buffer)
return vim.api.nvim_buf_get_name(buffer)
end, vim.api.nvim_list_bufs())
return function(path)
for _, buffer_path in ipairs(buffer_paths) do
local matches = utils.str_find(buffer_path, path)
if matches then
return true
end
end
return false
end
end
---Collapse a node, root if nil
---@param node? Node
---@param opts nvim_tree.api.node.collapse.Opts
local function collapse(node, opts)
local explorer = core.get_explorer()
if not explorer then
return
end
node = node or explorer
local node_at_cursor = explorer:get_node_at_cursor()
if not node_at_cursor then
return
end
local matches = buf_match()
Iterator.builder({ node:is(FileNode) and node.parent or node:as(DirectoryNode) })
:hidden()
:applier(function(n)
local dir = n:as(DirectoryNode)
if dir then
dir.open = opts.keep_buffers == true and matches(dir.absolute_path)
end
end)
:recursor(function(n)
return n.group_next and { n.group_next } or n.nodes
end)
:iterate()
explorer.renderer:draw()
explorer:focus_node_or_parent(node_at_cursor)
end
---@param opts? nvim_tree.api.node.collapse.Opts|boolean legacy -> opts.keep_buffers
function M.all(opts)
-- legacy arguments
if type(opts) == "boolean" then
opts = {
keep_buffers = opts,
}
end
collapse(nil, opts or {})
end
---@param node? Node
---@param opts? nvim_tree.api.node.collapse.Opts
function M.node(node, opts)
collapse(node, opts or {})
end
return M

View File

@@ -1,12 +1,14 @@
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local view = require "nvim-tree.view"
local finders_find_file = require "nvim-tree.actions.finders.find-file"
local core = require("nvim-tree.core")
local lib = require("nvim-tree.lib")
local view = require("nvim-tree.view")
local config = require("nvim-tree.config")
local finders_find_file = require("nvim-tree.actions.finders.find-file")
local change_root = require("nvim-tree.actions.tree.change-root")
local M = {}
--- Find file or buffer
---@param opts ApiTreeFindFileOpts|nil|boolean legacy -> opts.buf
---@param opts? nvim_tree.api.tree.find_file.Opts|boolean legacy -> opts.buf
function M.fn(opts)
-- legacy arguments
if type(opts) == "string" then
@@ -49,23 +51,19 @@ function M.fn(opts)
end
elseif opts.open then
-- open
lib.open { current_window = opts.current_window, winid = opts.winid }
lib.open({ current_window = opts.current_window, winid = opts.winid })
if not opts.focus then
vim.cmd "noautocmd wincmd p"
vim.cmd("noautocmd wincmd p")
end
end
-- update root
if opts.update_root or M.config.update_focused_file.update_root.enable then
require("nvim-tree").change_root(path, bufnr)
if opts.update_root or config.g.update_focused_file.update_root.enable then
change_root.fn(path, bufnr)
end
-- find
finders_find_file.fn(path)
end
function M.setup(opts)
M.config = opts or {}
end
return M

View File

@@ -1,17 +0,0 @@
local M = {}
M.find_file = require "nvim-tree.actions.tree.find-file"
M.modifiers = require "nvim-tree.actions.tree.modifiers"
M.open = require "nvim-tree.actions.tree.open"
M.toggle = require "nvim-tree.actions.tree.toggle"
M.resize = require "nvim-tree.actions.tree.resize"
function M.setup(opts)
M.find_file.setup(opts)
M.modifiers.setup(opts)
M.open.setup(opts)
M.toggle.setup(opts)
M.resize.setup(opts)
end
return M

View File

@@ -1,52 +0,0 @@
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib"
local Iterator = require "nvim-tree.iterators.node-iterator"
local M = {}
---@return fun(path: string): boolean
local function buf_match()
local buffer_paths = vim.tbl_map(function(buffer)
return vim.api.nvim_buf_get_name(buffer)
end, vim.api.nvim_list_bufs())
return function(path)
for _, buffer_path in ipairs(buffer_paths) do
local matches = utils.str_find(buffer_path, path)
if matches then
return true
end
end
return false
end
end
---@param keep_buffers boolean
function M.fn(keep_buffers)
local node = lib.get_node_at_cursor()
local explorer = core.get_explorer()
if explorer == nil then
return
end
local matches = buf_match()
Iterator.builder(explorer.nodes)
:hidden()
:applier(function(n)
if n.nodes ~= nil then
n.open = keep_buffers == true and matches(n.absolute_path)
end
end)
:recursor(function(n)
return n.group_next and { n.group_next } or n.nodes
end)
:iterate()
explorer.renderer:draw()
utils.focus_node_or_parent(node)
end
return M

View File

@@ -1,82 +0,0 @@
local core = require "nvim-tree.core"
local Iterator = require "nvim-tree.iterators.node-iterator"
local notify = require "nvim-tree.notify"
local lib = require "nvim-tree.lib"
local M = {}
---@param list string[]
---@return table
local function to_lookup_table(list)
local table = {}
for _, element in ipairs(list) do
table[element] = true
end
return table
end
---@param node Node
local function expand(node)
node = lib.get_last_group_node(node)
node.open = true
if #node.nodes == 0 then
core.get_explorer():expand(node)
end
end
---@param expansion_count integer
---@param node Node
---@return boolean
local function should_expand(expansion_count, node)
local should_halt = expansion_count >= M.MAX_FOLDER_DISCOVERY
local should_exclude = M.EXCLUDE[node.name]
return not should_halt and node.nodes and not node.open and not should_exclude
end
local function gen_iterator()
local expansion_count = 0
return function(parent)
if parent.parent and parent.nodes and not parent.open then
expansion_count = expansion_count + 1
expand(parent)
end
Iterator.builder(parent.nodes)
:hidden()
:applier(function(node)
if should_expand(expansion_count, node) then
expansion_count = expansion_count + 1
expand(node)
end
end)
:recursor(function(node)
return expansion_count < M.MAX_FOLDER_DISCOVERY and (node.group_next and { node.group_next } or (node.open and node.nodes))
end)
:iterate()
if expansion_count >= M.MAX_FOLDER_DISCOVERY then
return true
end
end
end
---@param base_node table
function M.fn(base_node)
local explorer = core.get_explorer()
local node = base_node.nodes and base_node or explorer
if gen_iterator()(node) then
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
end
if explorer then
explorer.renderer:draw()
end
end
function M.setup(opts)
M.MAX_FOLDER_DISCOVERY = opts.actions.expand_all.max_folder_discovery
M.EXCLUDE = to_lookup_table(opts.actions.expand_all.exclude)
end
return M

View File

@@ -1,11 +0,0 @@
local M = {}
M.collapse_all = require "nvim-tree.actions.tree.modifiers.collapse-all"
M.expand_all = require "nvim-tree.actions.tree.modifiers.expand-all"
M.toggles = require "nvim-tree.actions.tree.modifiers.toggles"
function M.setup(opts)
M.expand_all.setup(opts)
end
return M

View File

@@ -1,72 +0,0 @@
local lib = require "nvim-tree.lib"
local utils = require "nvim-tree.utils"
local core = require "nvim-tree.core"
local M = {}
---@param explorer Explorer
local function reload(explorer)
local node = lib.get_node_at_cursor()
explorer:reload_explorer()
utils.focus_node_or_parent(node)
end
local function wrap_explorer(fn)
return function(...)
local explorer = core.get_explorer()
if explorer then
return fn(explorer, ...)
end
end
end
---@param explorer Explorer
local function custom(explorer)
explorer.filters.config.filter_custom = not explorer.filters.config.filter_custom
reload(explorer)
end
---@param explorer Explorer
local function git_ignored(explorer)
explorer.filters.config.filter_git_ignored = not explorer.filters.config.filter_git_ignored
reload(explorer)
end
---@param explorer Explorer
local function git_clean(explorer)
explorer.filters.config.filter_git_clean = not explorer.filters.config.filter_git_clean
reload(explorer)
end
---@param explorer Explorer
local function no_buffer(explorer)
explorer.filters.config.filter_no_buffer = not explorer.filters.config.filter_no_buffer
reload(explorer)
end
---@param explorer Explorer
local function no_bookmark(explorer)
explorer.filters.config.filter_no_bookmark = not explorer.filters.config.filter_no_bookmark
reload(explorer)
end
---@param explorer Explorer
local function dotfiles(explorer)
explorer.filters.config.filter_dotfiles = not explorer.filters.config.filter_dotfiles
reload(explorer)
end
---@param explorer Explorer
local function enable(explorer)
explorer.filters.config.enable = not explorer.filters.config.enable
reload(explorer)
end
M.custom = wrap_explorer(custom)
M.git_ignored = wrap_explorer(git_ignored)
M.git_clean = wrap_explorer(git_clean)
M.no_buffer = wrap_explorer(no_buffer)
M.no_bookmark = wrap_explorer(no_bookmark)
M.dotfiles = wrap_explorer(dotfiles)
M.enable = wrap_explorer(enable)
return M

View File

@@ -1,11 +1,14 @@
local lib = require "nvim-tree.lib"
local view = require "nvim-tree.view"
local finders_find_file = require "nvim-tree.actions.finders.find-file"
local lib = require("nvim-tree.lib")
local view = require("nvim-tree.view")
local core = require("nvim-tree.core")
local config = require("nvim-tree.config")
local finders_find_file = require("nvim-tree.actions.finders.find-file")
local change_root = require("nvim-tree.actions.tree.change-root")
local M = {}
---Open the tree, focusing if already open.
---@param opts ApiTreeOpenOpts|nil|string legacy -> opts.path
---@param opts? nvim_tree.api.tree.open.Opts|string legacy -> opts.path
function M.fn(opts)
-- legacy arguments
if type(opts) == "string" then
@@ -29,18 +32,18 @@ function M.fn(opts)
view.focus()
else
-- open
lib.open {
lib.open({
path = opts.path,
current_window = opts.current_window,
winid = opts.winid,
}
})
end
-- find file
if M.config.update_focused_file.enable or opts.find_file then
if config.g.update_focused_file.enable or opts.find_file then
-- update root
if opts.update_root then
require("nvim-tree").change_root(previous_path, previous_buf)
change_root.fn(previous_path, previous_buf)
end
-- find
@@ -48,8 +51,42 @@ function M.fn(opts)
end
end
function M.setup(opts)
M.config = opts or {}
---@param dirname string absolute directory path
function M.open_on_directory(dirname)
local should_proceed = config.g.hijack_directories.auto_open or view.is_visible()
if not should_proceed then
return
end
-- instantiate an explorer if there is not one
if not core.get_explorer() then
core.init(dirname)
end
local explorer = core.get_explorer()
if explorer then
explorer:force_dirchange(dirname, true, false)
end
end
function M.tab_enter()
if view.is_visible({ any_tabpage = true }) then
local bufname = vim.api.nvim_buf_get_name(0)
local ft = vim.api.nvim_get_option_value("filetype", { buf = 0 }) or ""
for _, filter in ipairs(config.g.tab.sync.ignore) do
if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then
return
end
end
view.open({ focus_tree = false })
local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end
end
end
return M

View File

@@ -1,13 +1,14 @@
local view = require "nvim-tree.view"
local view = require("nvim-tree.view")
local view_state = require("nvim-tree.view-state")
local M = {}
---Resize the tree, persisting the new size.
---@param opts ApiTreeResizeOpts|nil
---@param opts? nvim_tree.api.tree.resize.Opts
function M.fn(opts)
if opts == nil then
-- reset to config values
view.configure_width()
view_state.configure_width()
view.resize()
return
end
@@ -16,7 +17,7 @@ function M.fn(opts)
local width_cfg = options.width
if width_cfg ~= nil then
view.configure_width(width_cfg)
view_state.configure_width(width_cfg)
view.resize()
return
end
@@ -44,8 +45,4 @@ function M.fn(opts)
end
end
function M.setup(opts)
M.config = opts or {}
end
return M

View File

@@ -1,14 +1,16 @@
local lib = require "nvim-tree.lib"
local view = require "nvim-tree.view"
local finders_find_file = require "nvim-tree.actions.finders.find-file"
local lib = require("nvim-tree.lib")
local view = require("nvim-tree.view")
local config = require("nvim-tree.config")
local finders_find_file = require("nvim-tree.actions.finders.find-file")
local change_root = require("nvim-tree.actions.tree.change-root")
local M = {}
---Toggle the tree.
---@param opts ApiTreeToggleOpts|nil|boolean legacy -> opts.find_file
---@param no_focus string|nil legacy -> opts.focus
---@param cwd boolean|nil legacy -> opts.path
---@param bang boolean|nil legacy -> opts.update_root
---@param opts? nvim_tree.api.tree.toggle.Opts|boolean legacy -> opts.find_file
---@param no_focus? string legacy -> opts.focus
---@param cwd? boolean legacy -> opts.path
---@param bang? boolean legacy -> opts.update_root
function M.fn(opts, no_focus, cwd, bang)
-- legacy arguments
if type(opts) == "boolean" then
@@ -45,17 +47,17 @@ function M.fn(opts, no_focus, cwd, bang)
view.close()
else
-- open
lib.open {
lib.open({
path = opts.path,
current_window = opts.current_window,
winid = opts.winid,
}
})
-- find file
if M.config.update_focused_file.enable or opts.find_file then
if config.g.update_focused_file.enable or opts.find_file then
-- update root
if opts.update_root then
require("nvim-tree").change_root(previous_path, previous_buf)
change_root.fn(previous_path, previous_buf)
end
-- find
@@ -64,13 +66,9 @@ function M.fn(opts, no_focus, cwd, bang)
-- restore focus
if not opts.focus then
vim.cmd "noautocmd wincmd p"
vim.cmd("noautocmd wincmd p")
end
end
end
function M.setup(opts)
M.config = opts or {}
end
return M

View File

@@ -1,288 +1,101 @@
local lib = require "nvim-tree.lib"
local core = require "nvim-tree.core"
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils"
local actions = require "nvim-tree.actions"
local appearance_diagnostics = require "nvim-tree.appearance.diagnostics"
local events = require "nvim-tree.events"
local help = require "nvim-tree.help"
local keymap = require "nvim-tree.keymap"
local notify = require "nvim-tree.notify"
---@brief
---nvim-tree exposes a public API. This is non breaking, with additions made as necessary.
---
---Please do not require or use modules other than `nvim-tree.api`, as internal modules will change without notice.
---
---The API is separated into multiple modules:
---
---- [nvim-tree-api-appearance]
---- [nvim-tree-api-commands]
---- [nvim-tree-api-events]
---- [nvim-tree-api-filter]
---- [nvim-tree-api-fs]
---- [nvim-tree-api-git]
---- [nvim-tree-api-map]
---- [nvim-tree-api-marks]
---- [nvim-tree-api-node]
---- [nvim-tree-api-tree]
---
---Modules are accessed via `api.<module>.<function>`
---
---Example invocation of the `reload` function in the `tree` module:
---```lua
---
---local api = require("nvim-tree.api")
---api.tree.reload()
---```
---Generally, functions accepting a [nvim_tree.api.Node] as their first argument will use the node under the cursor when that argument is not present or nil. Some functions are mode-dependent: when invoked in visual mode they will operate on all nodes in the visual selection instead of a single node. See |nvim-tree-mappings-default| for which mappings support visual mode.
---
---e.g. the following are functionally identical:
---```lua
---
---api.node.open.edit(nil, { focus = true })
---
---api.node.open.edit(api.tree.get_node_under_cursor(), { focus = true })
---```
local Api = {
tree = {},
node = {
navigate = {
sibling = {},
git = {},
diagnostics = {},
opened = {},
},
run = {},
open = {},
},
events = {},
marks = {
bulk = {},
navigate = {},
},
fs = {
copy = {},
},
git = {},
live_filter = {},
config = {
mappings = {},
},
commands = {},
diagnostics = {},
---The Node class is a data class. Instances may be provided by API functions for use as a:
---- handle to pass back to API functions e.g. [nvim_tree.api.node.run.cmd()]
---- reference in callbacks e.g. [nvim_tree.config.sort.Sorter] {sorter}
---
---Please do not mutate the contents of any Node object.
---
---@class nvim_tree.api.Node
---@field absolute_path string of the file or directory
---@field name string file or directory name
---@field parent? nvim_tree.api.DirectoryNode parent directory, nil for root
---@field type "file" | "directory" | "link" [uv.fs_stat()] {type}
---@field executable boolean file is executable
---@field fs_stat? uv.fs_stat.result at time of last tree display, see [uv.fs_stat()]
---@field git_status nvim_tree.git.Status? for files and directories
---@field diag_severity? lsp.DiagnosticSeverity diagnostic status
---@field hidden boolean node is not visible in the tree
---
---Git statuses for a single node.
---
---`nvim_tree.git.XY`: 2 character string, see `man 1 git-status` "Short Format"
---@alias nvim_tree.git.XY string
---
---{dir} status is derived from its contents:
---- `direct`: inherited from child files
---- `indirect`: inherited from child directories
---
---@class nvim_tree.git.Status
---@field file? nvim_tree.git.XY status of a file node
---@field dir? table<"direct" | "indirect", nvim_tree.git.XY[]> direct inclusive-or indirect status
---
---nvim-tree Public API
---
---@class nvim_tree.api
---@nodoc
local api = {
appearance = require("nvim-tree._meta.api.appearance"),
commands = require("nvim-tree._meta.api.commands"),
config = require("nvim-tree._meta.api.config"),
events = require("nvim-tree._meta.api.events"),
filter = require("nvim-tree._meta.api.filter"),
fs = require("nvim-tree._meta.api.fs"),
git = require("nvim-tree._meta.api.git"),
map = require("nvim-tree._meta.api.map"),
marks = require("nvim-tree._meta.api.marks"),
node = require("nvim-tree._meta.api.node"),
tree = require("nvim-tree._meta.api.tree"),
Decorator = require("nvim-tree._meta.api.decorator"),
decorator = require("nvim-tree._meta.api.deprecated").decorator, ---@deprecated
diagnostics = require("nvim-tree._meta.api.deprecated").diagnostics, ---@deprecated
live_filter = require("nvim-tree._meta.api.deprecated").live_filter, ---@deprecated
}
--- Print error when setup not called.
--- f function to invoke
---@param f function
---@return fun(...) : any
local function wrap(f)
return function(...)
if vim.g.NvimTreeSetup == 1 then
return f(...)
else
notify.error "nvim-tree setup not called"
end
end
end
require("nvim-tree.api.impl").hydrate_pre_setup(api)
---Inject the node as the first argument if present otherwise do nothing.
---@param fn function function to invoke
local function wrap_node(fn)
return function(node, ...)
node = node or lib.get_node_at_cursor()
if node then
return fn(node, ...)
end
end
end
---Inject the node or nil as the first argument if absent.
---@param fn function function to invoke
local function wrap_node_or_nil(fn)
return function(node, ...)
node = node or lib.get_node_at_cursor()
return fn(node, ...)
end
end
---Invoke a method on the singleton explorer.
---Print error when setup not called.
---@param explorer_method string explorer method name
---@return fun(...) : any
local function wrap_explorer(explorer_method)
return wrap(function(...)
local explorer = core.get_explorer()
if explorer then
return explorer[explorer_method](explorer, ...)
end
end)
end
---Invoke a member's method on the singleton explorer.
---Print error when setup not called.
---@param explorer_member string explorer member name
---@param member_method string method name to invoke on member
---@return fun(...) : any
local function wrap_explorer_member(explorer_member, member_method)
return wrap(function(...)
local explorer = core.get_explorer()
if explorer then
return explorer[explorer_member][member_method](explorer[explorer_member], ...)
end
end)
end
---@class ApiTreeOpenOpts
---@field path string|nil path
---@field current_window boolean|nil default false
---@field winid number|nil
---@field find_file boolean|nil default false
---@field update_root boolean|nil default false
Api.tree.open = wrap(actions.tree.open.fn)
Api.tree.focus = Api.tree.open
---@class ApiTreeToggleOpts
---@field path string|nil
---@field current_window boolean|nil default false
---@field winid number|nil
---@field find_file boolean|nil default false
---@field update_root boolean|nil default false
---@field focus boolean|nil default true
Api.tree.toggle = wrap(actions.tree.toggle.fn)
Api.tree.close = wrap(view.close)
Api.tree.close_in_this_tab = wrap(view.close_this_tab_only)
Api.tree.close_in_all_tabs = wrap(view.close_all_tabs)
Api.tree.reload = wrap_explorer "reload_explorer"
---@class ApiTreeResizeOpts
---@field width string|function|number|table|nil
---@field absolute number|nil
---@field relative number|nil
Api.tree.resize = wrap(actions.tree.resize.fn)
Api.tree.change_root = wrap(function(...)
require("nvim-tree").change_dir(...)
end)
Api.tree.change_root_to_node = wrap_node(function(node)
if node.name == ".." then
actions.root.change_dir.fn ".."
elseif node.nodes ~= nil then
actions.root.change_dir.fn(lib.get_last_group_node(node).absolute_path)
end
end)
Api.tree.change_root_to_parent = wrap_node(actions.root.dir_up.fn)
Api.tree.get_node_under_cursor = wrap(lib.get_node_at_cursor)
Api.tree.get_nodes = wrap(lib.get_nodes)
---@class ApiTreeFindFileOpts
---@field buf string|number|nil
---@field open boolean|nil default false
---@field current_window boolean|nil default false
---@field winid number|nil
---@field update_root boolean|nil default false
---@field focus boolean|nil default false
Api.tree.find_file = wrap(actions.tree.find_file.fn)
Api.tree.search_node = wrap(actions.finders.search_node.fn)
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse_all.fn)
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand_all.fn)
Api.tree.toggle_enable_filters = wrap(actions.tree.modifiers.toggles.enable)
Api.tree.toggle_gitignore_filter = wrap(actions.tree.modifiers.toggles.git_ignored)
Api.tree.toggle_git_clean_filter = wrap(actions.tree.modifiers.toggles.git_clean)
Api.tree.toggle_no_buffer_filter = wrap(actions.tree.modifiers.toggles.no_buffer)
Api.tree.toggle_custom_filter = wrap(actions.tree.modifiers.toggles.custom)
Api.tree.toggle_hidden_filter = wrap(actions.tree.modifiers.toggles.dotfiles)
Api.tree.toggle_no_bookmark_filter = wrap(actions.tree.modifiers.toggles.no_bookmark)
Api.tree.toggle_help = wrap(help.toggle)
Api.tree.is_tree_buf = wrap(utils.is_nvim_tree_buf)
---@class ApiTreeIsVisibleOpts
---@field tabpage number|nil
---@field any_tabpage boolean|nil default false
Api.tree.is_visible = wrap(view.is_visible)
---@class ApiTreeWinIdOpts
---@field tabpage number|nil default nil
Api.tree.winid = wrap(view.winid)
Api.fs.create = wrap_node_or_nil(actions.fs.create_file.fn)
Api.fs.remove = wrap_node(actions.fs.remove_file.fn)
Api.fs.trash = wrap_node(actions.fs.trash.fn)
Api.fs.rename_node = wrap_node(actions.fs.rename_file.fn ":t")
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_basename = wrap_node(actions.fs.rename_file.fn ":t:r")
Api.fs.rename_full = wrap_node(actions.fs.rename_file.fn ":p")
Api.fs.cut = wrap_node(wrap_explorer_member("clipboard", "cut"))
Api.fs.paste = wrap_node(wrap_explorer_member("clipboard", "paste"))
Api.fs.clear_clipboard = wrap_explorer_member("clipboard", "clear_clipboard")
Api.fs.print_clipboard = wrap_explorer_member("clipboard", "print_clipboard")
Api.fs.copy.node = wrap_node(wrap_explorer_member("clipboard", "copy"))
Api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_absolute_path"))
Api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename"))
Api.fs.copy.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename"))
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
---@param mode string
---@param node table
local function edit(mode, node)
local path = node.absolute_path
if node.link_to and not node.nodes then
path = node.link_to
end
actions.node.open_file.fn(mode, path)
end
---@param mode string
---@return fun(node: table)
local function open_or_expand_or_dir_up(mode, toggle_group)
return function(node)
if node.name == ".." then
actions.root.change_dir.fn ".."
elseif node.nodes then
lib.expand_or_collapse(node, toggle_group)
elseif not toggle_group then
edit(mode, node)
end
end
end
Api.node.open.edit = wrap_node(open_or_expand_or_dir_up "edit")
Api.node.open.drop = wrap_node(open_or_expand_or_dir_up "drop")
Api.node.open.tab_drop = wrap_node(open_or_expand_or_dir_up "tab_drop")
Api.node.open.replace_tree_buffer = wrap_node(open_or_expand_or_dir_up "edit_in_place")
Api.node.open.no_window_picker = wrap_node(open_or_expand_or_dir_up "edit_no_picker")
Api.node.open.vertical = wrap_node(open_or_expand_or_dir_up "vsplit")
Api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up "split")
Api.node.open.tab = wrap_node(open_or_expand_or_dir_up "tabnew")
Api.node.open.toggle_group_empty = wrap_node(open_or_expand_or_dir_up("toggle_group_empty", true))
Api.node.open.preview = wrap_node(open_or_expand_or_dir_up "preview")
Api.node.open.preview_no_picker = wrap_node(open_or_expand_or_dir_up "preview_no_picker")
Api.node.show_info_popup = wrap_node(actions.node.file_popup.toggle_file_info)
Api.node.run.cmd = wrap_node(actions.node.run_command.run_file_command)
Api.node.run.system = wrap_node(actions.node.system_open.fn)
Api.node.navigate.sibling.next = wrap_node(actions.moves.sibling.fn "next")
Api.node.navigate.sibling.prev = wrap_node(actions.moves.sibling.fn "prev")
Api.node.navigate.sibling.first = wrap_node(actions.moves.sibling.fn "first")
Api.node.navigate.sibling.last = wrap_node(actions.moves.sibling.fn "last")
Api.node.navigate.parent = wrap_node(actions.moves.parent.fn(false))
Api.node.navigate.parent_close = wrap_node(actions.moves.parent.fn(true))
Api.node.navigate.git.next = wrap_node(actions.moves.item.fn { where = "next", what = "git" })
Api.node.navigate.git.next_skip_gitignored = wrap_node(actions.moves.item.fn { where = "next", what = "git", skip_gitignored = true })
Api.node.navigate.git.next_recursive = wrap_node(actions.moves.item.fn { where = "next", what = "git", recurse = true })
Api.node.navigate.git.prev = wrap_node(actions.moves.item.fn { where = "prev", what = "git" })
Api.node.navigate.git.prev_skip_gitignored = wrap_node(actions.moves.item.fn { where = "prev", what = "git", skip_gitignored = true })
Api.node.navigate.git.prev_recursive = wrap_node(actions.moves.item.fn { where = "prev", what = "git", recurse = true })
Api.node.navigate.diagnostics.next = wrap_node(actions.moves.item.fn { where = "next", what = "diag" })
Api.node.navigate.diagnostics.next_recursive = wrap_node(actions.moves.item.fn { where = "next", what = "diag", recurse = true })
Api.node.navigate.diagnostics.prev = wrap_node(actions.moves.item.fn { where = "prev", what = "diag" })
Api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn { where = "prev", what = "diag", recurse = true })
Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn { where = "next", what = "opened" })
Api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn { where = "prev", what = "opened" })
Api.git.reload = wrap_explorer "reload_git"
Api.events.subscribe = events.subscribe
Api.events.Event = events.Event
Api.live_filter.start = wrap_explorer_member("live_filter", "start_filtering")
Api.live_filter.clear = wrap_explorer_member("live_filter", "clear_filter")
Api.marks.get = wrap_node(wrap_explorer_member("marks", "get"))
Api.marks.list = wrap_explorer_member("marks", "list")
Api.marks.toggle = wrap_node(wrap_explorer_member("marks", "toggle"))
Api.marks.clear = wrap_explorer_member("marks", "clear")
Api.marks.bulk.delete = wrap_explorer_member("marks", "bulk_delete")
Api.marks.bulk.trash = wrap_explorer_member("marks", "bulk_trash")
Api.marks.bulk.move = wrap_explorer_member("marks", "bulk_move")
Api.marks.navigate.next = wrap_explorer_member("marks", "navigate_next")
Api.marks.navigate.prev = wrap_explorer_member("marks", "navigate_prev")
Api.marks.navigate.select = wrap_explorer_member("marks", "navigate_select")
Api.config.mappings.get_keymap = wrap(keymap.get_keymap)
Api.config.mappings.get_keymap_default = wrap(keymap.get_keymap_default)
Api.config.mappings.default_on_attach = keymap.default_on_attach
Api.diagnostics.hi_test = wrap(appearance_diagnostics.hi_test)
Api.commands.get = wrap(function()
return require("nvim-tree.commands").get()
end)
return Api
return api

260
lua/nvim-tree/api/impl.lua Normal file
View File

@@ -0,0 +1,260 @@
---Hydrates API meta functions with their implementations.
---For startup performance reasons, all API implementation's requires must be
---done at call time, not when this module is required.
local M = {}
---Walk the entire API, hydrating all functions with the error notification.
---Do not hydrate classes i.e. anything with a metatable.
---@param api table not properly typed to prevent LSP from referencing implementations
local function hydrate_not_setup(api)
for k, v in pairs(api) do
if type(v) == "function" then
api[k] = function()
require("nvim-tree.notify").error("nvim-tree setup not called")
end
elseif type(v) == "table" and not getmetatable(v) then
hydrate_not_setup(v)
end
end
end
---Returns the node under the cursor.
---@return Node?
local function node_at_cursor()
local e = require("nvim-tree.core").get_explorer()
return e and e:get_node_at_cursor() or nil
end
---Returns the visually selected nodes, if we are in visual mode.
---@return Node[]?
local function visual_nodes()
local utils = require("nvim-tree.utils")
return utils.is_visual_mode() and utils.get_visual_nodes() or nil
end
---Injects:
---- n: n or node at cursor
---@param fn fun(n?: Node, ...): any
---@return fun(n?: Node, ...): any
local function _n(fn)
return function(n, ...)
return fn(n or node_at_cursor(), ...)
end
end
---Injects:
---- n: visual nodes or n or node at cursor
---@param fn fun(n?: Node|Node[], ...): any
---@return fun(n?: Node|Node[], ...): any
local function _v(fn)
return function(n, ...)
return fn(visual_nodes() or n or node_at_cursor(), ...)
end
end
---Injects:
---- e: Explorer instance
---Does nothing when no explorer instance.
---@param fn fun(e: Explorer, ...): any
---@return fun(e: Explorer, ...): any
local function e_(fn)
return function(...)
local e = require("nvim-tree.core").get_explorer()
if e then
return fn(e, ...)
end
end
end
---Injects:
---- e: Explorer instance
---- n: n or node at cursor
---Does nothing when no explorer instance.
---@param fn fun(e: Explorer, n?: Node, ...): any
---@return fun(e: Explorer, n?: Node, ...): any
local function en(fn)
return function(n, ...)
local e = require("nvim-tree.core").get_explorer()
if e then
return fn(e, n or node_at_cursor(), ...)
end
end
end
---Injects:
---- e: Explorer instance
---- n: visual nodes or n or node at cursor
---Does nothing when no explorer instance.
---@param fn fun(e: Explorer, n?: Node|Node[], ...): any
---@return fun(e: Explorer, n?: Node|Node[], ...): any
local function ev(fn)
return function(n, ...)
local e = require("nvim-tree.core").get_explorer()
if e then
return fn(e, visual_nodes() or n or node_at_cursor(), ...)
end
end
end
---NOP function wrapper, exists for formatting purposes only.
---@param fn fun(...): any
---@return fun(...): any
local function __(fn)
return function(...)
return fn(...)
end
end
---Hydrates API meta functions pre-setup:
--- Pre-setup functions will be hydrated with their implementation.
--- Post-setup functions will notify error: "nvim-tree setup not called"
--- All classes will be hydrated with their implementations.
---Called once when api is first required
---@param api table not properly typed to prevent LSP from referencing implementations
function M.hydrate_pre_setup(api)
hydrate_not_setup(api)
api.appearance.hi_test = __(function() require("nvim-tree.appearance.hi-test")() end)
api.commands.get = __(function() return require("nvim-tree.commands").get() end)
api.config.default = __(function() return require("nvim-tree.config").d_clone() end)
api.events.subscribe = __(function(event_name, handler) require("nvim-tree.events").subscribe(event_name, handler) end)
api.map.keymap.default = __(function() return require("nvim-tree.keymap").get_keymap_default() end)
api.map.on_attach.default = __(function(bufnr) require("nvim-tree.keymap").on_attach_default(bufnr) end)
api.Decorator = require("nvim-tree.renderer.decorator")
-- Map any legacy functions to implementations above or to meta
require("nvim-tree.legacy").map_api(api)
end
---Re-hydrates all API functions with implementations, replacing any "nvim-tree setup not called" error functions.
---Called explicitly after nvim-tree setup
---@param api table not properly typed to prevent LSP from referencing implementations
function M.hydrate_post_setup(api)
-- Parameter naming conventions:
-- `e` explorer
-- `n` node
-- `o` opts
api.config.global = __(function() return require("nvim-tree.config").g_clone() end)
api.config.user = __(function() return require("nvim-tree.config").u_clone() end)
api.filter.custom.toggle = e_(function(e) e.filters:toggle("custom") end)
api.filter.dotfiles.toggle = e_(function(e) e.filters:toggle("dotfiles") end)
api.filter.git.clean.toggle = e_(function(e) e.filters:toggle("git_clean") end)
api.filter.git.ignored.toggle = e_(function(e) e.filters:toggle("git_ignored") end)
api.filter.live.clear = e_(function(e) e.live_filter:clear_filter() end)
api.filter.live.start = e_(function(e) e.live_filter:start_filtering() end)
api.filter.no_bookmark.toggle = e_(function(e) e.filters:toggle("no_bookmark") end)
api.filter.no_buffer.toggle = e_(function(e) e.filters:toggle("no_buffer") end)
api.filter.toggle = e_(function(e) e.filters:toggle() end)
api.fs.clear_clipboard = e_(function(e) e.clipboard:clear_clipboard() end)
api.fs.copy.absolute_path = ev(function(e, n) e.clipboard:copy_absolute_path(n) end)
api.fs.copy.basename = en(function(e, n) e.clipboard:copy_basename(n) end)
api.fs.copy.filename = en(function(e, n) e.clipboard:copy_filename(n) end)
api.fs.copy.relative_path = en(function(e, n) e.clipboard:copy_path(n) end)
api.fs.copy.node = ev(function(e, n) e.clipboard:copy(n) end)
api.fs.create = _n(function(n) require("nvim-tree.actions.fs.create-file").fn(n) end)
api.fs.cut = ev(function(e, n) e.clipboard:cut(n) end)
api.fs.paste = en(function(e, n) e.clipboard:paste(n) end)
api.fs.move = en(function(e, n) e.clipboard:paste(n, { cut = true }) end)
api.fs.print_clipboard = e_(function(e) e.clipboard:print_clipboard() end)
api.fs.remove = _v(function(n) require("nvim-tree.actions.fs.remove-file").fn(n) end)
api.fs.rename = _n(function(n) require("nvim-tree.actions.fs.rename-file").rename_node(n) end)
api.fs.rename_basename = _n(function(n) require("nvim-tree.actions.fs.rename-file").rename_basename(n) end)
api.fs.rename_full = _n(function(n) require("nvim-tree.actions.fs.rename-file").rename_full(n) end)
api.fs.rename_node = _n(function(n) require("nvim-tree.actions.fs.rename-file").rename_node(n) end)
api.fs.rename_sub = _n(function(n) require("nvim-tree.actions.fs.rename-file").rename_sub(n) end)
api.fs.trash = _v(function(n) require("nvim-tree.actions.fs.trash").fn(n) end)
api.git.reload = e_(function(e) e:reload_git() end)
api.map.keymap.current = __(function() return require("nvim-tree.keymap").get_keymap() end)
api.marks.bulk.delete = e_(function(e) e.marks:bulk_delete() end)
api.marks.bulk.move = e_(function(e) e.marks:bulk_move() end)
api.marks.bulk.trash = e_(function(e) e.marks:bulk_trash() end)
api.marks.clear = e_(function(e) e.marks:clear() end)
api.marks.get = en(function(e, n) return e.marks:get(n) end)
api.marks.list = e_(function(e) return e.marks:list() end)
api.marks.navigate.next = e_(function(e) e.marks:navigate_next() end)
api.marks.navigate.prev = e_(function(e) e.marks:navigate_prev() end)
api.marks.navigate.select = e_(function(e) e.marks:navigate_select() end)
api.marks.toggle = ev(function(e, n) e.marks:toggle(n) end)
api.node.buffer.delete = _n(function(n, o) require("nvim-tree.actions.node.buffer").delete(n, o) end)
api.node.buffer.wipe = _n(function(n, o) require("nvim-tree.actions.node.buffer").wipe(n, o) end)
api.node.collapse = _n(function(n, o) require("nvim-tree.actions.tree.collapse").node(n, o) end)
api.node.expand = en(function(e, n, o) e:expand_node(n, o) end)
api.node.navigate.diagnostics.next = __(function() require("nvim-tree.actions.moves.item").diagnostics_next() end)
api.node.navigate.diagnostics.next_recursive = __(function() require("nvim-tree.actions.moves.item").diagnostics_next_recursive() end)
api.node.navigate.diagnostics.prev = __(function() require("nvim-tree.actions.moves.item").diagnostics_prev() end)
api.node.navigate.diagnostics.prev_recursive = __(function() require("nvim-tree.actions.moves.item").diagnostics_prev_recursive() end)
api.node.navigate.git.next = __(function() require("nvim-tree.actions.moves.item").git_next() end)
api.node.navigate.git.next_recursive = __(function() require("nvim-tree.actions.moves.item").git_next_recursive() end)
api.node.navigate.git.next_skip_gitignored = __(function() require("nvim-tree.actions.moves.item").git_next_skip_gitignored() end)
api.node.navigate.git.prev = __(function() require("nvim-tree.actions.moves.item").git_prev() end)
api.node.navigate.git.prev_recursive = __(function() require("nvim-tree.actions.moves.item").git_prev_recursive() end)
api.node.navigate.git.prev_skip_gitignored = __(function() require("nvim-tree.actions.moves.item").git_prev_skip_gitignored() end)
api.node.navigate.opened.next = __(function() require("nvim-tree.actions.moves.item").opened_next() end)
api.node.navigate.opened.prev = __(function() require("nvim-tree.actions.moves.item").opened_prev() end)
api.node.navigate.parent = _n(function(n) require("nvim-tree.actions.moves.parent").move(n) end)
api.node.navigate.parent_close = _n(function(n) require("nvim-tree.actions.moves.parent").move_close(n) end)
api.node.navigate.sibling.first = _n(function(n) require("nvim-tree.actions.moves.sibling").first(n) end)
api.node.navigate.sibling.last = _n(function(n) require("nvim-tree.actions.moves.sibling").last(n) end)
api.node.navigate.sibling.next = _n(function(n) require("nvim-tree.actions.moves.sibling").next(n) end)
api.node.navigate.sibling.prev = _n(function(n) require("nvim-tree.actions.moves.sibling").prev(n) end)
api.node.open.drop = _n(function(n) require("nvim-tree.actions.node.open-file").drop(n) end)
api.node.open.edit = _n(function(n, o) require("nvim-tree.actions.node.open-file").edit(n, o) end)
api.node.open.horizontal = _n(function(n, o) require("nvim-tree.actions.node.open-file").horizontal(n, o) end)
api.node.open.horizontal_no_picker = _n(function(n, o)
require("nvim-tree.actions.node.open-file").horizontal_no_picker(n,
o)
end)
api.node.open.no_window_picker = _n(function(n, o) require("nvim-tree.actions.node.open-file").no_window_picker(n, o) end)
api.node.open.preview = _n(function(n, o) require("nvim-tree.actions.node.open-file").preview(n, o) end)
api.node.open.preview_no_picker = _n(function(n, o) require("nvim-tree.actions.node.open-file").preview_no_picker(n, o) end)
api.node.open.replace_tree_buffer = _n(function(n) require("nvim-tree.actions.node.open-file").replace_tree_buffer(n) end)
api.node.open.tab = _n(function(n, o) require("nvim-tree.actions.node.open-file").tab(n, o) end)
api.node.open.tab_drop = _n(function(n) require("nvim-tree.actions.node.open-file").tab_drop(n) end)
api.node.open.toggle_group_empty = _n(function(n) require("nvim-tree.actions.node.open-file").toggle_group_empty(n) end)
api.node.open.vertical = _n(function(n, o) require("nvim-tree.actions.node.open-file").vertical(n, o) end)
api.node.open.vertical_no_picker = _n(function(n, o) require("nvim-tree.actions.node.open-file").vertical_no_picker(n, o) end)
api.node.run.cmd = _n(function(n) require("nvim-tree.actions.node.run-command").run_file_command(n) end)
api.node.run.system = _n(function(n) require("nvim-tree.actions.node.system-open").fn(n) end)
api.node.show_info_popup = _n(function(n) require("nvim-tree.actions.node.file-popup").toggle_file_info(n) end)
api.tree.change_root = __(function(path) require("nvim-tree.actions.tree.change-dir").fn(path) end)
api.tree.change_root_to_node = en(function(e, n) e:change_dir_to_node(n) end)
api.tree.change_root_to_parent = en(function(e, n) e:dir_up(n) end)
api.tree.close = __(function() require("nvim-tree.view").close() end)
api.tree.close_in_all_tabs = __(function() require("nvim-tree.view").close_all_tabs() end)
api.tree.close_in_this_tab = __(function() require("nvim-tree.view").close_this_tab_only() end)
api.tree.collapse_all = __(function(o) require("nvim-tree.actions.tree.collapse").all(o) end)
api.tree.expand_all = en(function(e, n, o) e:expand_all(n, o) end)
api.tree.find_file = __(function(o) require("nvim-tree.actions.tree.find-file").fn(o) end)
api.tree.focus = __(function(o) require("nvim-tree.actions.tree.open").fn(o) end)
api.tree.get_node_under_cursor = en(function(e) return e:get_node_at_cursor() end)
api.tree.get_nodes = en(function(e) return e:get_nodes() end)
api.tree.is_tree_buf = __(function(bufnr) return require("nvim-tree.utils").is_nvim_tree_buf(bufnr) end)
api.tree.is_visible = __(function(o) return require("nvim-tree.view").is_visible(o) end)
api.tree.open = __(function(o) require("nvim-tree.actions.tree.open").fn(o) end)
api.tree.reload = e_(function(e) e:reload_explorer() end)
api.tree.reload_git = e_(function(e) e:reload_git() end)
api.tree.resize = __(function(o) require("nvim-tree.actions.tree.resize").fn(o) end)
api.tree.search_node = __(function() require("nvim-tree.actions.finders.search-node").fn() end)
api.tree.toggle = __(function(o) require("nvim-tree.actions.tree.toggle").fn(o) end)
api.tree.toggle_help = __(function() require("nvim-tree.help").toggle() end)
api.tree.winid = __(function(o) return require("nvim-tree.view").winid(o) end)
-- Map all legacy functions to implementations
require("nvim-tree.legacy").map_api(api)
end
return M

View File

@@ -1,45 +1,49 @@
local appearance = require "nvim-tree.appearance"
local appearance = require("nvim-tree.appearance")
local Class = require("nvim-tree.classic")
-- others with name and links less than this arbitrary value are short
local SHORT_LEN = 50
local M = {}
local namespace_hi_test_id = vim.api.nvim_create_namespace("NvimTreeHiTest")
---@class HighlightDisplay for :NvimTreeHiTest
---For :NvimTreeHiTest
---@class (exact) HighlightDisplay: nvim_tree.Class
---@field group string nvim-tree highlight group name
---@field links string link chain to a concretely defined group
---@field def string :hi concrete definition after following any links
local HighlightDisplay = {}
local HighlightDisplay = Class:extend()
---@param group string nvim-tree highlight group name
---@return HighlightDisplay
function HighlightDisplay:new(group)
local o = {}
setmetatable(o, self)
self.__index = self
---@class HighlightDisplay
---@overload fun(args: HighlightDisplayArgs): HighlightDisplay
o.group = group
local concrete = o.group
---@class (exact) HighlightDisplayArgs
---@field group string nvim-tree highlight group name
---@protected
---@param args HighlightDisplayArgs
function HighlightDisplay:new(args)
self.group = args.group
local concrete = self.group
-- maybe follow links
local links = {}
local link = vim.api.nvim_get_hl(0, { name = o.group }).link
local link = vim.api.nvim_get_hl(0, { name = self.group }).link
while link do
table.insert(links, link)
concrete = link
link = vim.api.nvim_get_hl(0, { name = link }).link
end
o.links = table.concat(links, " ")
self.links = table.concat(links, " ")
-- concrete definition
local ok, res = pcall(vim.api.nvim_cmd, { cmd = "highlight", args = { concrete } }, { output = true })
if ok and type(res) == "string" then
o.def = res:gsub(".*xxx *", "")
self.def = res:gsub(".*xxx *", "")
else
o.def = ""
self.def = ""
end
return o
end
---Render one group.
@@ -51,7 +55,12 @@ function HighlightDisplay:render(bufnr, fmt, l)
local text = string.format(fmt, self.group, self.links, self.def)
vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { text })
vim.api.nvim_buf_add_highlight(bufnr, -1, self.group, l, 0, #self.group)
if vim.fn.has("nvim-0.11") == 1 and vim.hl and vim.hl.range then
vim.hl.range(bufnr, namespace_hi_test_id, self.group, { l, 0 }, { l, #self.group, }, {})
else
vim.api.nvim_buf_add_highlight(bufnr, -1, self.group, l, 0, #self.group) ---@diagnostic disable-line: deprecated
end
return l + 1
end
@@ -87,7 +96,7 @@ end
---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim
---Display all nvim-tree and neovim highlight groups, their link chain and actual definition
function M.hi_test()
return function()
-- create a buffer
local bufnr = vim.api.nvim_create_buf(false, true)
@@ -96,7 +105,7 @@ function M.hi_test()
-- nvim-tree groups, ordered
local displays = {}
for _, highlight_group in ipairs(appearance.HIGHLIGHT_GROUPS) do
local display = HighlightDisplay:new(highlight_group.group)
local display = HighlightDisplay({ group = highlight_group.group })
table.insert(displays, display)
end
l = render_displays("nvim-tree", displays, bufnr, l)
@@ -110,7 +119,7 @@ function M.hi_test()
if ok then
for group in string.gmatch(out, "(%w*)%s+xxx") do
if group:find("NvimTree", 1, true) ~= 1 then
local display = HighlightDisplay:new(group)
local display = HighlightDisplay({ group = group })
if #display.group + #display.links > SHORT_LEN then
table.insert(displays_long, display)
else
@@ -129,13 +138,7 @@ function M.hi_test()
render_displays("other, long", displays_long, bufnr, l)
-- finalise and focus the buffer
if vim.fn.has "nvim-0.10" == 1 then
vim.api.nvim_set_option_value("modifiable", false, { buf = bufnr })
else
vim.api.nvim_buf_set_option(bufnr, "modifiable", false) ---@diagnostic disable-line: deprecated
end
vim.api.nvim_set_option_value("modifiable", false, { buf = bufnr })
vim.cmd.buffer(bufnr)
end
return M

View File

@@ -12,126 +12,126 @@ local M = {}
M.HIGHLIGHT_GROUPS = {
-- Standard
{ group = "NvimTreeNormal", link = "Normal" },
{ group = "NvimTreeNormalFloat", link = "NormalFloat" },
{ group = "NvimTreeNormalFloatBorder", link = "FloatBorder" },
{ group = "NvimTreeNormalNC", link = "NvimTreeNormal" },
{ group = "NvimTreeNormal", link = "Normal" },
{ group = "NvimTreeNormalFloat", link = "NormalFloat" },
{ group = "NvimTreeNormalFloatBorder", link = "FloatBorder" },
{ group = "NvimTreeNormalNC", link = "NvimTreeNormal" },
{ group = "NvimTreeLineNr", link = "LineNr" },
{ group = "NvimTreeWinSeparator", link = "WinSeparator" },
{ group = "NvimTreeEndOfBuffer", link = "EndOfBuffer" },
{ group = "NvimTreePopup", link = "Normal" },
{ group = "NvimTreeSignColumn", link = "NvimTreeNormal" },
{ group = "NvimTreeLineNr", link = "LineNr" },
{ group = "NvimTreeWinSeparator", link = "WinSeparator" },
{ group = "NvimTreeEndOfBuffer", link = "EndOfBuffer" },
{ group = "NvimTreePopup", link = "Normal" },
{ group = "NvimTreeSignColumn", link = "NvimTreeNormal" },
{ group = "NvimTreeCursorColumn", link = "CursorColumn" },
{ group = "NvimTreeCursorLine", link = "CursorLine" },
{ group = "NvimTreeCursorLineNr", link = "CursorLineNr" },
{ group = "NvimTreeCursorColumn", link = "CursorColumn" },
{ group = "NvimTreeCursorLine", link = "CursorLine" },
{ group = "NvimTreeCursorLineNr", link = "CursorLineNr" },
{ group = "NvimTreeStatusLine", link = "StatusLine" },
{ group = "NvimTreeStatusLineNC", link = "StatusLineNC" },
{ group = "NvimTreeStatusLine", link = "StatusLine" },
{ group = "NvimTreeStatusLineNC", link = "StatusLineNC" },
-- File Text
{ group = "NvimTreeExecFile", link = "Question" },
{ group = "NvimTreeImageFile", link = "Question" },
{ group = "NvimTreeSpecialFile", link = "Title" },
{ group = "NvimTreeSymlink", link = "Underlined" },
{ group = "NvimTreeExecFile", link = "Question" },
{ group = "NvimTreeImageFile", link = "Question" },
{ group = "NvimTreeSpecialFile", link = "Title" },
{ group = "NvimTreeSymlink", link = "Underlined" },
-- Folder Text
{ group = "NvimTreeRootFolder", link = "Title" },
{ group = "NvimTreeFolderName", link = "Directory" },
{ group = "NvimTreeEmptyFolderName", link = "Directory" },
{ group = "NvimTreeOpenedFolderName", link = "Directory" },
{ group = "NvimTreeSymlinkFolderName", link = "Directory" },
{ group = "NvimTreeRootFolder", link = "Title" },
{ group = "NvimTreeFolderName", link = "Directory" },
{ group = "NvimTreeEmptyFolderName", link = "Directory" },
{ group = "NvimTreeOpenedFolderName", link = "Directory" },
{ group = "NvimTreeSymlinkFolderName", link = "Directory" },
-- File Icons
{ group = "NvimTreeFileIcon", link = "NvimTreeNormal" },
{ group = "NvimTreeSymlinkIcon", link = "NvimTreeNormal" },
{ group = "NvimTreeFileIcon", link = "NvimTreeNormal" },
{ group = "NvimTreeSymlinkIcon", link = "NvimTreeNormal" },
-- Folder Icons
{ group = "NvimTreeFolderIcon", def = "guifg=#8094b4 ctermfg=Blue" },
{ group = "NvimTreeOpenedFolderIcon", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeClosedFolderIcon", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeFolderArrowClosed", link = "NvimTreeIndentMarker" },
{ group = "NvimTreeFolderArrowOpen", link = "NvimTreeIndentMarker" },
{ group = "NvimTreeFolderIcon", def = "guifg=#8094b4 ctermfg=Blue" },
{ group = "NvimTreeOpenedFolderIcon", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeClosedFolderIcon", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeFolderArrowClosed", link = "NvimTreeIndentMarker" },
{ group = "NvimTreeFolderArrowOpen", link = "NvimTreeIndentMarker" },
-- Indent
{ group = "NvimTreeIndentMarker", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeIndentMarker", link = "NvimTreeFolderIcon" },
-- Picker
{ group = "NvimTreeWindowPicker", def = "guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=DarkBlue" },
{ group = "NvimTreeWindowPicker", def = "guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=DarkBlue" },
-- LiveFilter
{ group = "NvimTreeLiveFilterPrefix", link = "PreProc" },
{ group = "NvimTreeLiveFilterValue", link = "ModeMsg" },
{ group = "NvimTreeLiveFilterPrefix", link = "PreProc" },
{ group = "NvimTreeLiveFilterValue", link = "ModeMsg" },
-- Clipboard
{ group = "NvimTreeCutHL", link = "SpellBad" },
{ group = "NvimTreeCopiedHL", link = "SpellRare" },
{ group = "NvimTreeCutHL", link = "SpellBad" },
{ group = "NvimTreeCopiedHL", link = "SpellRare" },
-- Bookmark
{ group = "NvimTreeBookmarkIcon", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeBookmarkHL", link = "SpellLocal" },
{ group = "NvimTreeBookmarkIcon", link = "NvimTreeFolderIcon" },
{ group = "NvimTreeBookmarkHL", link = "SpellLocal" },
-- Modified
{ group = "NvimTreeModifiedIcon", link = "Type" },
{ group = "NvimTreeModifiedFileHL", link = "NvimTreeModifiedIcon" },
{ group = "NvimTreeModifiedFolderHL", link = "NvimTreeModifiedFileHL" },
{ group = "NvimTreeModifiedIcon", link = "Type" },
{ group = "NvimTreeModifiedFileHL", link = "NvimTreeModifiedIcon" },
{ group = "NvimTreeModifiedFolderHL", link = "NvimTreeModifiedFileHL" },
-- Hidden
{ group = "NvimTreeHiddenIcon", link = "Conceal" },
{ group = "NvimTreeHiddenFileHL", link = "NvimTreeHiddenIcon" },
{ group = "NvimTreeHiddenFolderHL", link = "NvimTreeHiddenFileHL" },
{ group = "NvimTreeHiddenIcon", link = "Conceal" },
{ group = "NvimTreeHiddenFileHL", link = "NvimTreeHiddenIcon" },
{ group = "NvimTreeHiddenFolderHL", link = "NvimTreeHiddenFileHL" },
-- Hidden Display
{ group = "NvimTreeHiddenDisplay", link = "Conceal" },
{ group = "NvimTreeHiddenDisplay", link = "Conceal" },
-- Opened
{ group = "NvimTreeOpenedHL", link = "Special" },
{ group = "NvimTreeOpenedHL", link = "Special" },
-- Git Icon
{ group = "NvimTreeGitDeletedIcon", link = "Statement" },
{ group = "NvimTreeGitDirtyIcon", link = "Statement" },
{ group = "NvimTreeGitIgnoredIcon", link = "Comment" },
{ group = "NvimTreeGitMergeIcon", link = "Constant" },
{ group = "NvimTreeGitNewIcon", link = "PreProc" },
{ group = "NvimTreeGitRenamedIcon", link = "PreProc" },
{ group = "NvimTreeGitStagedIcon", link = "Constant" },
{ group = "NvimTreeGitDeletedIcon", link = "Statement" },
{ group = "NvimTreeGitDirtyIcon", link = "Statement" },
{ group = "NvimTreeGitIgnoredIcon", link = "Comment" },
{ group = "NvimTreeGitMergeIcon", link = "Constant" },
{ group = "NvimTreeGitNewIcon", link = "PreProc" },
{ group = "NvimTreeGitRenamedIcon", link = "PreProc" },
{ group = "NvimTreeGitStagedIcon", link = "Constant" },
-- Git File Highlight
{ group = "NvimTreeGitFileDeletedHL", link = "NvimTreeGitDeletedIcon" },
{ group = "NvimTreeGitFileDirtyHL", link = "NvimTreeGitDirtyIcon" },
{ group = "NvimTreeGitFileIgnoredHL", link = "NvimTreeGitIgnoredIcon" },
{ group = "NvimTreeGitFileMergeHL", link = "NvimTreeGitMergeIcon" },
{ group = "NvimTreeGitFileNewHL", link = "NvimTreeGitNewIcon" },
{ group = "NvimTreeGitFileRenamedHL", link = "NvimTreeGitRenamedIcon" },
{ group = "NvimTreeGitFileStagedHL", link = "NvimTreeGitStagedIcon" },
{ group = "NvimTreeGitFileDeletedHL", link = "NvimTreeGitDeletedIcon" },
{ group = "NvimTreeGitFileDirtyHL", link = "NvimTreeGitDirtyIcon" },
{ group = "NvimTreeGitFileIgnoredHL", link = "NvimTreeGitIgnoredIcon" },
{ group = "NvimTreeGitFileMergeHL", link = "NvimTreeGitMergeIcon" },
{ group = "NvimTreeGitFileNewHL", link = "NvimTreeGitNewIcon" },
{ group = "NvimTreeGitFileRenamedHL", link = "NvimTreeGitRenamedIcon" },
{ group = "NvimTreeGitFileStagedHL", link = "NvimTreeGitStagedIcon" },
-- Git Folder Highlight
{ group = "NvimTreeGitFolderDeletedHL", link = "NvimTreeGitFileDeletedHL" },
{ group = "NvimTreeGitFolderDirtyHL", link = "NvimTreeGitFileDirtyHL" },
{ group = "NvimTreeGitFolderIgnoredHL", link = "NvimTreeGitFileIgnoredHL" },
{ group = "NvimTreeGitFolderMergeHL", link = "NvimTreeGitFileMergeHL" },
{ group = "NvimTreeGitFolderNewHL", link = "NvimTreeGitFileNewHL" },
{ group = "NvimTreeGitFolderRenamedHL", link = "NvimTreeGitFileRenamedHL" },
{ group = "NvimTreeGitFolderStagedHL", link = "NvimTreeGitFileStagedHL" },
{ group = "NvimTreeGitFolderDeletedHL", link = "NvimTreeGitFileDeletedHL" },
{ group = "NvimTreeGitFolderDirtyHL", link = "NvimTreeGitFileDirtyHL" },
{ group = "NvimTreeGitFolderIgnoredHL", link = "NvimTreeGitFileIgnoredHL" },
{ group = "NvimTreeGitFolderMergeHL", link = "NvimTreeGitFileMergeHL" },
{ group = "NvimTreeGitFolderNewHL", link = "NvimTreeGitFileNewHL" },
{ group = "NvimTreeGitFolderRenamedHL", link = "NvimTreeGitFileRenamedHL" },
{ group = "NvimTreeGitFolderStagedHL", link = "NvimTreeGitFileStagedHL" },
-- Diagnostics Icon
{ group = "NvimTreeDiagnosticErrorIcon", link = "DiagnosticError" },
{ group = "NvimTreeDiagnosticWarnIcon", link = "DiagnosticWarn" },
{ group = "NvimTreeDiagnosticInfoIcon", link = "DiagnosticInfo" },
{ group = "NvimTreeDiagnosticHintIcon", link = "DiagnosticHint" },
{ group = "NvimTreeDiagnosticErrorIcon", link = "DiagnosticError" },
{ group = "NvimTreeDiagnosticWarnIcon", link = "DiagnosticWarn" },
{ group = "NvimTreeDiagnosticInfoIcon", link = "DiagnosticInfo" },
{ group = "NvimTreeDiagnosticHintIcon", link = "DiagnosticHint" },
-- Diagnostics File Highlight
{ group = "NvimTreeDiagnosticErrorFileHL", link = "DiagnosticUnderlineError" },
{ group = "NvimTreeDiagnosticWarnFileHL", link = "DiagnosticUnderlineWarn" },
{ group = "NvimTreeDiagnosticInfoFileHL", link = "DiagnosticUnderlineInfo" },
{ group = "NvimTreeDiagnosticHintFileHL", link = "DiagnosticUnderlineHint" },
{ group = "NvimTreeDiagnosticErrorFileHL", link = "DiagnosticUnderlineError" },
{ group = "NvimTreeDiagnosticWarnFileHL", link = "DiagnosticUnderlineWarn" },
{ group = "NvimTreeDiagnosticInfoFileHL", link = "DiagnosticUnderlineInfo" },
{ group = "NvimTreeDiagnosticHintFileHL", link = "DiagnosticUnderlineHint" },
-- Diagnostics Folder Highlight
{ group = "NvimTreeDiagnosticErrorFolderHL", link = "NvimTreeDiagnosticErrorFileHL" },
{ group = "NvimTreeDiagnosticWarnFolderHL", link = "NvimTreeDiagnosticWarnFileHL" },
{ group = "NvimTreeDiagnosticInfoFolderHL", link = "NvimTreeDiagnosticInfoFileHL" },
{ group = "NvimTreeDiagnosticHintFolderHL", link = "NvimTreeDiagnosticHintFileHL" },
{ group = "NvimTreeDiagnosticWarnFolderHL", link = "NvimTreeDiagnosticWarnFileHL" },
{ group = "NvimTreeDiagnosticInfoFolderHL", link = "NvimTreeDiagnosticInfoFileHL" },
{ group = "NvimTreeDiagnosticHintFolderHL", link = "NvimTreeDiagnosticHintFileHL" },
}
-- nvim-tree highlight groups to legacy
@@ -182,7 +182,8 @@ M.LEGACY_LINKS = {
NvimTreeDiagnosticHintFolderHL = "NvimTreeLspDiagnosticsHintFolderText",
}
function M.setup()
---Create all highlight groups and links. Idempotent.
function M.highlight()
-- non-linked
for _, g in ipairs(M.HIGHLIGHT_GROUPS) do
if g.def then

213
lua/nvim-tree/autocmd.lua Normal file
View File

@@ -0,0 +1,213 @@
local config = require("nvim-tree.config")
local M = {}
---Create all global autocommands after setup.
---Idempotent: removes existing autocommands first.
---For startup performance reasons, all requires must be done inline during the callback.
---Some short circuiting logic is done directly inside the callback to prevent unnecessary requires.
function M.global()
local augroup_id = vim.api.nvim_create_augroup("NvimTree", { clear = true })
vim.api.nvim_create_autocmd("BufWipeout", {
group = augroup_id,
pattern = "NvimTree_*",
callback = function()
require("nvim-tree.view").wipeout()
end,
})
if vim.fn.has("nvim-0.13") == 1 and config.g.experimental.session_restore_nvim then
vim.api.nvim_create_autocmd("SessionWritePre", { ---@diagnostic disable-line: param-type-mismatch
group = augroup_id,
callback = function()
-- We must schedule to wait until `vim.v.this_session` is properly updated
-- This prevents overwriting the data if a new session is created with a new `:mksession`
vim.schedule(require("nvim-tree.session").save)
end,
})
vim.api.nvim_create_autocmd("SessionLoadPost", {
group = augroup_id,
-- Workaround: this event fires multiple times (report upstream),
-- failure notifications could show up many times.
once = true,
callback = function()
require("nvim-tree.session").restore()
end,
})
end
if config.g.tab.sync.open then
vim.api.nvim_create_autocmd("TabEnter", {
group = augroup_id,
callback = vim.schedule_wrap(function()
require("nvim-tree.actions.tree.open").tab_enter()
end)
})
end
if config.g.sync_root_with_cwd then
vim.api.nvim_create_autocmd("DirChanged", {
group = augroup_id,
callback = function()
require("nvim-tree.actions.tree.change-dir").fn(vim.uv.cwd())
end,
})
end
if config.g.update_focused_file.enable then
vim.api.nvim_create_autocmd("BufEnter", {
group = augroup_id,
callback = function(event)
if type(config.g.update_focused_file.exclude) == "function" and config.g.update_focused_file.exclude(event) then
return
end
require("nvim-tree.utils").debounce("BufEnter:find_file", config.g.view.debounce_delay, function()
require("nvim-tree.actions.tree.find-file").fn()
end)
end,
})
end
if config.g.hijack_directories.enable and (config.g.disable_netrw or config.g.hijack_netrw) then
vim.api.nvim_create_autocmd({ "BufEnter", "BufNewFile" }, {
group = augroup_id,
nested = true,
callback = function(data)
local bufname = vim.api.nvim_buf_get_name(data.buf)
if vim.fn.isdirectory(bufname) == 1 then
require("nvim-tree.actions.tree.open").open_on_directory(bufname)
end
end,
})
end
if config.g.view.centralize_selection then
vim.api.nvim_create_autocmd("BufEnter", {
group = augroup_id,
pattern = "NvimTree_*",
callback = vim.schedule_wrap(function()
vim.api.nvim_buf_call(0, function()
local is_term_mode = vim.api.nvim_get_mode().mode == "t"
if is_term_mode then
return
end
vim.cmd([[norm! zz]])
end)
end)
})
end
if config.g.diagnostics.enable then
vim.api.nvim_create_autocmd("DiagnosticChanged", {
group = augroup_id,
callback = function(ev)
require("nvim-tree.diagnostics").update_lsp(ev)
end,
})
vim.api.nvim_create_autocmd("User", {
group = augroup_id,
pattern = "CocDiagnosticChange",
callback = function()
require("nvim-tree.diagnostics").update_coc()
end,
})
end
if config.g.view.float.enable and config.g.view.float.quit_on_focus_loss then
vim.api.nvim_create_autocmd("WinLeave", {
group = augroup_id,
pattern = "NvimTree_*",
callback = function()
if require("nvim-tree.utils").is_nvim_tree_buf(0) then
require("nvim-tree.view").close()
end
end,
})
end
-- Handles event dispatch when tree is closed by `:q`
vim.api.nvim_create_autocmd("WinClosed", {
group = augroup_id,
pattern = "*",
---@param ev vim.api.keyset.create_autocmd.callback_args
callback = function(ev)
if not vim.api.nvim_buf_is_valid(ev.buf) then
return
end
if vim.api.nvim_get_option_value("filetype", { buf = ev.buf }) == "NvimTree" then
require("nvim-tree.events")._dispatch_on_tree_close()
end
end,
})
if config.g.renderer.full_name then
local group = vim.api.nvim_create_augroup("nvim_tree_floating_node", { clear = true })
vim.api.nvim_create_autocmd({ "BufLeave", "CursorMoved" }, {
group = group,
pattern = { "NvimTree_*" },
callback = function()
require("nvim-tree.renderer.components.full-name").hide()
end,
})
vim.api.nvim_create_autocmd({ "CursorMoved" }, {
group = group,
pattern = { "NvimTree_*" },
callback = function()
require("nvim-tree.renderer.components.full-name").show()
end,
})
end
vim.api.nvim_create_autocmd("CmdlineLeave", {
group = augroup_id,
pattern = ":",
callback = function(ev)
if require("nvim-tree.view").get_bufnr() == ev.buf then
local cmd = vim.fn.getcmdline()
if cmd:match("s([^%w])") then
local delimiter = cmd:match("s([^%w])")
local escaped_delim = delimiter:gsub("[%%%.%+%-%*%?%^%$%(%)%[%]]", "%%%1")
-- Looks for: s <delimiter> <old> <delimiter> <new> <delimiter or end>
local pattern = "s" .. escaped_delim .. "([^" .. escaped_delim .. "]*)" .. escaped_delim .. "([^" .. escaped_delim .. "]*)"
local old_part, new_part = cmd:match(pattern)
local core = require("nvim-tree.core")
local explorer = core.get_explorer()
local utils = require("nvim-tree.utils")
local bulk_rename = require("nvim-tree.actions.fs.rename-file").bulk_rename
local visual_marker = "'<,'>"
local nodes = cmd:sub(1, #visual_marker) == visual_marker and
utils.get_visual_nodes({ use_native = true, should_exit = false }) or
explorer and explorer:get_nodes_by_line(core.get_nodes_starting_line()) or {}
local matching_nodes = {}
for i = #nodes, 1, -1 do
local node = nodes[i]
if node and node.name:find(old_part) ~= nil then
table.insert(matching_nodes, node)
end
end
if #matching_nodes > 0 then
bulk_rename(matching_nodes, old_part, new_part)
else
require("nvim-tree.notify").notify(string.format("Not matching nodes with '%s'", old_part))
end
vim.fn.setcmdline("")
end
end
end
})
end
return M

View File

@@ -1,3 +1,7 @@
local config = require("nvim-tree.config")
local DirectoryNode = require("nvim-tree.node.directory")
local M = {}
---@type table<string, boolean> record of which file is modified
@@ -6,16 +10,14 @@ M._modified = {}
---refresh M._modified
function M.reload_modified()
M._modified = {}
local bufs = vim.fn.getbufinfo { bufmodified = 1, buflisted = 1 }
local bufs = vim.fn.getbufinfo({ bufmodified = 1, buflisted = 1 })
for _, buf in pairs(bufs) do
local path = buf.name
if path ~= "" then -- not a [No Name] buffer
-- mark all the parent as modified as well
while
M._modified[path] ~= true
while M._modified[path] ~= true do
-- no need to keep going if already recorded
-- This also prevents an infinite loop
do
M._modified[path] = true
path = vim.fn.fnamemodify(path, ":h")
end
@@ -23,28 +25,36 @@ function M.reload_modified()
end
end
---@param node table
---@param node Node
---@return boolean
function M.is_modified(node)
return node
and M.config.modified.enable
and M._modified[node.absolute_path]
and (not node.nodes or M.config.modified.show_on_dirs)
and (not node.open or M.config.modified.show_on_open_dirs)
if not config.g.modified.enable then
return false
end
if not M._modified[node.absolute_path] then
return false
end
local dir = node:as(DirectoryNode)
if dir then
if not config.g.modified.show_on_dirs then
return false
end
if dir.open and not config.g.modified.show_on_open_dirs then
return false
end
end
return true
end
---A buffer exists for the node's absolute path
---@param node table
---@param node Node
---@return boolean
function M.is_opened(node)
return node and vim.fn.bufloaded(node.absolute_path) > 0
end
---@param opts table
function M.setup(opts)
M.config = {
modified = opts.modified,
}
end
return M

197
lua/nvim-tree/classic.lua Normal file
View File

@@ -0,0 +1,197 @@
--
-- classic
--
-- Copyright (c) 2014, rxi
--
-- This module is free software; you can redistribute it and/or modify it under
-- the terms of the MIT license. See LICENSE for details.
--
-- https://github.com/rxi/classic
--
---@brief
---
---nvim-tree uses the https://github.com/rxi/classic class framework adding safe casts, instanceof mixin and conventional destructors.
---
---The key differences between classic and ordinary Lua classes:
---- The constructor [nvim_tree.Class:new()] is not responsible for allocation: `self` is available when the constructor is called.
---- Instances are constructed via the `__call` meta method: `SomeClass(args)`
---
---Classes are conventionally named using camel case e.g. `MyClass`
---
---Classes are created by extending another class:
---```lua
---
--- local Class = require("nvim-tree.classic")
---
--- ---@class (exact) Fruit: nvim_tree.Class
--- ---@field ...
--- local Fruit = Class:extend()
---
--- ---@class (exact) Apple: Fruit
--- ---@field ...
--- local Apple = Fruit:extend()
---```
---
---Implementing a constructor [nvim_tree.Class:new()] is optional, however it must call the `super` constructor:
---```lua
---
--- ---@protected
--- ---@param args AppleArgs
--- function Apple:new(args)
---
--- ---@type FruitArgs
--- local fruit_args = ...
---
--- Apple.super.new(self, fruit_args)
--- ---
---```
---
---Create an instance of a class using the `__call` meta method that will invoke the constructor:
---```lua
---
--- ---@type AppleArgs
--- local args = ...
---
--- local an_apple = Apple(args)
--- -- above will call `Apple:new(args)`
---```
---
---In order to strongly type instantiation, the following pattern is used to type the meta method `__call` with arguments and return:
---```lua
---
--- ---@class (exact) Fruit: nvim_tree.Class
--- ---@field ...
--- local Fruit = Class:extend()
---
--- ---@class (exact) FruitArgs
--- ---@field ...
---
--- ---@class Fruit
--- ---@overload fun(args: FruitArgs): Fruit
---
--- ---@protected
--- ---@param args FruitArgs
--- function Fruit:new(args)
---```
---
---@class nvim_tree.Class
---
---Parent class, `Class` for base classes.
---@field super nvim_tree.Class
---
---mixin classes that are implemented.
---@field private implements table<nvim_tree.Class, boolean>
local Class = {}
Class.__index = Class ---@diagnostic disable-line: inject-field
---
---Constructor: `self` has been allocated and is available.
---
---Super constructor must be called using the form `Child.super.new(self, parent_args)`
---
---@param ... any constructor arguments
function Class:new(...) --luacheck: ignore 212
end
---
---Conventional destructor, optional, must be called by the owner.
---
---Parent destructor must be invoked using the form `Parent.destroy(self)`
---
function Class:destroy()
end
---
---Create a new class by extending another class.
---
---Base classes extend `Class`
---
---@return [nvim_tree.Class] child class
function Class:extend()
local cls = {}
for k, v in pairs(self) do
if k:find("__") == 1 then
cls[k] = v
end
end
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
---
---Add the methods and fields of a mixin using the form `SomeClass:implement(MixinClass)`
---
---If the mixin has fields, it must implement a constructor.
---
---@param mixin nvim_tree.Class
function Class:implement(mixin)
if not rawget(self, "implements") then
-- set on the class itself instead of parents
rawset(self, "implements", {})
end
self.implements[mixin] = true
for k, v in pairs(mixin) do
if self[k] == nil and type(v) == "function" then
self[k] = v
end
end
end
---
---Instance of.
---
---Test whether an object is {class}, inherits {class} or implements mixin {class}.
---
---@generic T
---@param class T `<T>`
---@return boolean
function Class:is(class)
local mt = getmetatable(self)
while mt do
if mt == class then
return true
end
if mt.implements and mt.implements[class] then
return true
end
mt = getmetatable(mt)
end
return false
end
---
---Type safe cast.
---
---If instance [nvim_tree.Class:is()], cast to {class} and return it, otherwise nil.
---
---@generic T
---@param class T `<T>`
---@return T? `<T>`
function Class:as(class)
return self:is(class) and self or nil
end
---
---Constructs an instance: calls [nvim_tree.Class:new()] and returns the new instance.
---
---@param ... any constructor args
---@return nvim_tree.Class
function Class:__call(...)
local obj = setmetatable({}, self)
obj:new(...)
return obj
end
---
---Utility method to bypass unused param warnings in abstract methods.
---
---@param ... any
function Class:nop(...) --luacheck: ignore 212
end
return Class

View File

@@ -1,8 +1,6 @@
local api = require "nvim-tree.api"
local view = require "nvim-tree.view"
local M = {}
---@type nvim_tree.api.commands.Command[]
local CMDS = {
{
name = "NvimTreeOpen",
@@ -12,7 +10,7 @@ local CMDS = {
complete = "dir",
},
command = function(c)
api.tree.open { path = c.args }
require("nvim-tree.api").tree.open({ path = c.args })
end,
},
{
@@ -22,7 +20,7 @@ local CMDS = {
bar = true,
},
command = function()
api.tree.close()
require("nvim-tree.api").tree.close()
end,
},
{
@@ -33,12 +31,12 @@ local CMDS = {
complete = "dir",
},
command = function(c)
api.tree.toggle {
require("nvim-tree.api").tree.toggle({
find_file = false,
focus = true,
path = c.args,
update_root = false,
}
})
end,
},
{
@@ -48,7 +46,7 @@ local CMDS = {
bar = true,
},
command = function()
api.tree.open()
require("nvim-tree.api").tree.open()
end,
},
{
@@ -58,7 +56,7 @@ local CMDS = {
bar = true,
},
command = function()
api.tree.reload()
require("nvim-tree.api").tree.reload()
end,
},
{
@@ -68,7 +66,7 @@ local CMDS = {
bar = true,
},
command = function()
api.fs.print_clipboard()
require("nvim-tree.api").fs.print_clipboard()
end,
},
{
@@ -79,11 +77,11 @@ local CMDS = {
bar = true,
},
command = function(c)
api.tree.find_file {
require("nvim-tree.api").tree.find_file({
open = true,
focus = true,
update_root = c.bang,
}
})
end,
},
{
@@ -95,12 +93,12 @@ local CMDS = {
complete = "dir",
},
command = function(c)
api.tree.toggle {
require("nvim-tree.api").tree.toggle({
find_file = true,
focus = true,
path = c.args,
update_root = c.bang,
}
})
end,
},
{
@@ -111,7 +109,12 @@ local CMDS = {
bar = true,
},
command = function(c)
view.resize(c.args)
local sign = c.args:sub(1, 1)
if sign == "+" or sign == "-" then
require("nvim-tree.api").tree.resize({ relative = tonumber(c.args) })
else
require("nvim-tree.api").tree.resize({ absolute = tonumber(c.args) })
end
end,
},
{
@@ -121,7 +124,7 @@ local CMDS = {
bar = true,
},
command = function()
api.tree.collapse_all(false)
require("nvim-tree.api").tree.collapse_all({ keep_buffers = false })
end,
},
{
@@ -131,7 +134,7 @@ local CMDS = {
bar = true,
},
command = function()
api.tree.collapse_all(true)
require("nvim-tree.api").tree.collapse_all({ keep_buffers = true })
end,
},
{
@@ -139,10 +142,13 @@ local CMDS = {
opts = {
desc = "nvim-tree: highlight test",
},
command = api.diagnostics.hi_test,
command = function()
require("nvim-tree.api").appearance.hi_test()
end,
},
}
---@return nvim_tree.api.commands.Command[]
function M.get()
return vim.deepcopy(CMDS)
end

600
lua/nvim-tree/config.lua Normal file
View File

@@ -0,0 +1,600 @@
--This will be required after api, before setup.
--This file should have minimal requires that are cheap and have no dependencies or are already required.
--Short names like g are used rather than getters to keep code brief
local M = {
---Immutable default config
---@type nvim_tree.config
d = {},
---Global current config, nil until setup called, mutable
---@type nvim_tree.config?
g = nil,
---Immutable raw user config, nil when no user config passed to setup
---@type nvim_tree.config?
u = nil,
---Immutable OS, detected once on first require
---@type table<"unix"|"macos"|"wsl"|"windows", boolean>
os = nil,
---Nvim cwd at setup time
---@type string
init_root = "",
}
M.os = {
unix = vim.fn.has("unix") == 1,
macos = vim.fn.has("mac") == 1 or vim.fn.has("macunix") == 1,
wsl = vim.fn.has("wsl") == 1,
-- false for WSL
windows = vim.fn.has("win32") == 1 or vim.fn.has("win32unix") == 1,
}
M.d = { -- config-default-start
on_attach = "default",
hijack_cursor = false,
auto_reload_on_write = true,
disable_netrw = false,
hijack_netrw = true,
hijack_unnamed_buffer_when_opening = false,
root_dirs = {},
prefer_startup_root = false,
sync_root_with_cwd = false,
reload_on_bufenter = false,
respect_buf_cwd = false,
select_prompts = false,
sort = {
sorter = "name",
folders_first = true,
files_first = false,
},
view = {
centralize_selection = false,
cursorline = true,
cursorlineopt = "both",
debounce_delay = 15,
side = "left",
preserve_window_proportions = false,
number = false,
relativenumber = false,
signcolumn = "yes",
width = 30,
float = {
enable = false,
quit_on_focus_loss = true,
open_win_config = {
relative = "editor",
border = "rounded",
width = 30,
height = 30,
row = 1,
col = 1,
},
},
},
renderer = {
add_trailing = false,
group_empty = false,
full_name = false,
root_folder_label = ":~:s?$?/..?",
indent_width = 2,
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none",
symlink_destination = true,
decorators = { "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", },
highlight_git = "none",
highlight_diagnostics = "none",
highlight_opened_files = "none",
highlight_modified = "none",
highlight_hidden = "none",
highlight_bookmarks = "none",
highlight_clipboard = "name",
indent_markers = {
enable = false,
inline_arrows = true,
icons = {
corner = "",
edge = "",
item = "",
bottom = "",
none = " ",
},
},
icons = {
web_devicons = {
file = {
enable = true,
color = true,
},
folder = {
enable = false,
color = true,
},
},
git_placement = "before",
modified_placement = "after",
hidden_placement = "after",
diagnostics_placement = "signcolumn",
bookmarks_placement = "signcolumn",
padding = {
icon = " ",
folder_arrow = " ",
},
symlink_arrow = "",
show = {
file = true,
folder = true,
folder_arrow = true,
git = true,
modified = true,
hidden = false,
diagnostics = true,
bookmarks = true,
},
glyphs = {
default = "",
symlink = "",
bookmark = "󰆤",
modified = "",
hidden = "󰜌",
folder = {
arrow_closed = "",
arrow_open = "",
default = "",
open = "",
empty = "",
empty_open = "",
symlink = "",
symlink_open = "",
},
git = {
unstaged = "",
staged = "",
unmerged = "",
renamed = "",
untracked = "",
deleted = "",
ignored = "",
},
},
},
},
hijack_directories = {
enable = true,
auto_open = true,
},
update_focused_file = {
enable = false,
update_root = {
enable = false,
ignore_list = {},
},
exclude = false,
},
git = {
enable = true,
show_on_dirs = true,
show_on_open_dirs = true,
disable_for_dirs = {},
timeout = 400,
cygwin_support = false,
},
diagnostics = {
enable = false,
show_on_dirs = false,
show_on_open_dirs = true,
debounce_delay = 500,
severity = {
min = vim.diagnostic.severity.HINT,
max = vim.diagnostic.severity.ERROR,
},
icons = {
hint = "",
info = "",
warning = "",
error = "",
},
diagnostic_opts = false,
},
modified = {
enable = false,
show_on_dirs = true,
show_on_open_dirs = true,
},
filters = {
enable = true,
git_ignored = true,
dotfiles = false,
git_clean = false,
no_buffer = false,
no_bookmark = false,
custom = {},
exclude = {},
},
live_filter = {
prefix = "[FILTER]: ",
always_show_folders = true,
},
filesystem_watchers = {
enable = true,
debounce_delay = 50,
max_events = 0,
ignore_dirs = {
"/.ccls-cache",
"/build",
"/node_modules",
"/target",
"/.zig-cache",
},
whitelist_dirs = {},
},
actions = {
use_system_clipboard = true,
change_dir = {
enable = true,
global = false,
restrict_above_cwd = false,
},
expand_all = {
max_folder_discovery = 300,
exclude = {},
},
file_popup = {
open_win_config = {
col = 1,
row = 1,
relative = "cursor",
border = "shadow",
style = "minimal",
},
},
open_file = {
quit_on_open = false,
eject = true,
resize_window = true,
relative_path = true,
window_picker = {
enable = true,
picker = "default",
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
exclude = {
filetype = { "notify", "packer", "qf", "diff", "fugitive", "fugitiveblame" },
buftype = { "nofile", "terminal", "help" },
},
},
},
remove_file = {
close_window = true,
},
},
trash = {
cmd = "gio trash",
},
tab = {
sync = {
open = false,
close = false,
ignore = {},
},
},
notify = {
threshold = vim.log.levels.INFO,
absolute_path = true,
},
help = {
sort_by = "key",
},
ui = {
confirm = {
remove = true,
trash = true,
default_yes = false,
},
},
bookmarks = {
persist = false,
},
experimental = {
session_restore_nvim = false
},
log = {
enable = false,
truncate = false,
types = {
all = false,
config = false,
copy_paste = false,
dev = false,
diagnostics = false,
git = false,
profile = false,
watcher = false,
},
},
} -- config-default-end
local FIELD_SKIP_VALIDATE = {
open_win_config = true,
}
local ACCEPTED_TYPES = {
on_attach = { "function", "string" },
sort = {
sorter = { "function", "string" },
},
view = {
width = {
"string",
"function",
"number",
"table",
min = { "string", "function", "number" },
max = { "string", "function", "number" },
lines_excluded = { "table" },
padding = { "function", "number" },
},
},
renderer = {
hidden_display = { "function", "string" },
group_empty = { "boolean", "function" },
root_folder_label = { "function", "string", "boolean" },
},
update_focused_file = {
exclude = { "function" },
},
git = {
disable_for_dirs = { "function" },
},
filters = {
custom = { "function" },
},
filesystem_watchers = {
ignore_dirs = { "function" },
whitelist_dirs = { "function" },
},
actions = {
open_file = {
window_picker = {
picker = { "function", "string" },
},
},
},
bookmarks = {
persist = { "boolean", "string" },
},
}
local ACCEPTED_STRINGS = {
sort = {
sorter = { "name", "case_sensitive", "modification_time", "extension", "suffix", "filetype" },
},
view = {
side = { "left", "right" },
signcolumn = { "yes", "no", "auto" },
},
renderer = {
hidden_display = { "none", "simple", "all" },
highlight_git = { "none", "icon", "name", "all" },
highlight_opened_files = { "none", "icon", "name", "all" },
highlight_modified = { "none", "icon", "name", "all" },
highlight_hidden = { "none", "icon", "name", "all" },
highlight_bookmarks = { "none", "icon", "name", "all" },
highlight_diagnostics = { "none", "icon", "name", "all" },
highlight_clipboard = { "none", "icon", "name", "all" },
icons = {
git_placement = { "before", "after", "signcolumn", "right_align" },
modified_placement = { "before", "after", "signcolumn", "right_align" },
hidden_placement = { "before", "after", "signcolumn", "right_align" },
diagnostics_placement = { "before", "after", "signcolumn", "right_align" },
bookmarks_placement = { "before", "after", "signcolumn", "right_align" },
},
},
help = {
sort_by = { "key", "desc" },
},
}
local ACCEPTED_ENUMS = {
view = {
width = {
lines_excluded = { "root", },
},
},
}
---Validate types and values of the user supplied config.
---Warns and removes invalid in place.
---@param u nvim_tree.config
local function validate_config(u)
local msg
---@param user any
---@param def any
---@param strs table
---@param types table
---@param enums table
---@param prefix string
local function validate(user, def, strs, types, enums, prefix)
-- if user's option is not a table there is nothing to do
if type(user) ~= "table" then
return
end
-- we have hit a leaf enum to validate against - it's an integer indexed table
local enum_value = type(enums) == "table" and next(enums) and type(next(enums)) == "number"
-- only compare tables with contents that are not integer indexed nor enums
if not enum_value and (type(def) ~= "table" or not next(def) or type(next(def)) == "number") then
-- unless the field can be a table (and is not a table in default config)
if vim.tbl_contains(types, "table") then
-- use a dummy default to allow all checks
def = {}
else
return
end
end
for k, v in pairs(user) do
if not FIELD_SKIP_VALIDATE[k] then
local invalid
if enum_value then
if not vim.tbl_contains(enums, v) then
invalid = string.format("Invalid value for field %s%s: Expected one of enum '%s', got '%s'", prefix, k,
table.concat(enums, "'|'"), tostring(v))
end
else
if def[k] == nil and types[k] == nil then
-- option does not exist
invalid = string.format("Unknown option: %s%s", prefix, k)
elseif type(v) ~= type(def[k]) then
local expected
if types[k] and #types[k] > 0 then
if not vim.tbl_contains(types[k], type(v)) then
expected = table.concat(types[k], "|")
end
else
expected = type(def[k])
end
if expected then
-- option is of the wrong type
invalid = string.format("Invalid option: %s%s. Expected %s, got %s", prefix, k, expected, type(v))
end
elseif type(v) == "string" and strs[k] and not vim.tbl_contains(strs[k], v) then
-- option has type `string` but value is not accepted
invalid = string.format("Invalid value for field %s%s: '%s'", prefix, k, v)
end
end
if invalid then
if msg then
msg = string.format("%s\n%s", msg, invalid)
else
msg = invalid
end
user[k] = nil
elseif not enum_value then
validate(v, def[k], strs[k] or {}, types[k] or {}, enums[k] or {}, prefix .. k .. ".")
end
end
end
end
validate(u, M.d, ACCEPTED_STRINGS, ACCEPTED_TYPES, ACCEPTED_ENUMS, "")
if msg then
require("nvim-tree.notify").warn(msg .. "\n\nsee :help nvim-tree-config for available configuration options")
end
end
---Localise the (default) config with OS specifics
---@param d nvim_tree.config
local function localise_config(d)
-- Trash
if M.os.macos or M.os.windows then
d.trash.cmd = "trash"
end
-- Watchers
if M.os.windows then
d.filesystem_watchers.max_events = 1000
end
end
---Normalise the (global) config: defaults and user
---@param g nvim_tree.config
local function process_config(g)
--
-- Open
--
g.actions.open_file.window_picker.chars = tostring(g.actions.open_file.window_picker.chars):upper()
--
-- Padding
--
if g.renderer.indent_width < 1 then
g.renderer.indent_width = 1
end
for k, v in pairs(g.renderer.indent_markers.icons) do
if #v == 0 then
g.renderer.indent_markers.icons[k] = " "
else
-- return the first character from the UTF-8 encoded string; we may use utf8.codes from Lua 5.3 when available
g.renderer.indent_markers.icons[k] = v:match("[%z\1-\127\194-\244][\128-\191]*")
end
end
end
---Hijack and disable netrw if (globally) configured
---@param g nvim_tree.config
local function manage_netrw(g)
if g.hijack_netrw then
vim.cmd("silent! autocmd! FileExplorer *")
vim.cmd("autocmd VimEnter * ++once silent! autocmd! FileExplorer *")
end
if g.disable_netrw then
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
end
end
---Validate user config and migrate legacy.
---Merge with M.d and persist as M.g
---When no user config M.g is set to M.d and M.u is set to nil
---@param u? nvim_tree.config user supplied subset of config
function M.setup(u)
if not u or type(u) ~= "table" then
if u then
require("nvim-tree.notify").warn(string.format("invalid config type \"%s\" passed to setup, using defaults", type(u)))
end
M.g = vim.deepcopy(M.d)
M.u = nil
return
end
-- retain user for reference
M.u = vim.deepcopy(u)
require("nvim-tree.legacy").migrate_config(u)
validate_config(u)
-- set global to the validated and populated user config
M.g = vim.tbl_deep_extend("force", M.d, u)
-- process merged config
process_config(M.g)
-- deal with netrw
manage_netrw(M.g)
-- store cwd
M.init_root = vim.fn.getcwd()
end
---Deep clone defaults
---@return nvim_tree.config
function M.d_clone()
return vim.deepcopy(M.d)
end
---Deep clone user
---@return nvim_tree.config? nil when no config passed to setup
function M.u_clone()
return vim.deepcopy(M.u)
end
---Deep clone global
---@return nvim_tree.config? nil when setup not called
function M.g_clone()
return vim.deepcopy(M.g)
end
---Immediately localise the defaults once and once only
localise_config(M.d)
return M

View File

@@ -1,6 +1,9 @@
local events = require "nvim-tree.events"
local view = require "nvim-tree.view"
local log = require "nvim-tree.log"
local events = require("nvim-tree.events")
local notify = require("nvim-tree.notify")
local view = require("nvim-tree.view")
local log = require("nvim-tree.log")
local git = require("nvim-tree.git")
local watcher = require("nvim-tree.watcher")
local M = {}
@@ -15,7 +18,21 @@ function M.init(foldername)
if TreeExplorer then
TreeExplorer:destroy()
end
TreeExplorer = require("nvim-tree.explorer"):new(foldername)
local err, path
if foldername then
path, err = vim.uv.fs_realpath(foldername)
else
path, err = vim.uv.cwd()
end
if path then
TreeExplorer = require("nvim-tree.explorer")({ path = path })
else
notify.error(err)
TreeExplorer = nil
end
if not first_init_done then
events._dispatch_ready()
first_init_done = true
@@ -49,4 +66,16 @@ function M.get_nodes_starting_line()
return offset
end
function M.purge_all_state()
view.close_all_tabs()
view.abandon_all_windows()
if TreeExplorer then
git.purge_state()
TreeExplorer:destroy()
M.reset_explorer()
end
-- purge orphaned that were not destroyed by their nodes
watcher.purge_watchers()
end
return M

View File

@@ -1,7 +1,10 @@
local core = require "nvim-tree.core"
local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local log = require "nvim-tree.log"
local config = require("nvim-tree.config")
local core = require("nvim-tree.core")
local utils = require("nvim-tree.utils")
local view = require("nvim-tree.view")
local log = require("nvim-tree.log")
local DirectoryNode = require("nvim-tree.node.directory")
local M = {}
@@ -15,7 +18,7 @@ local COC_SEVERITY_LEVELS = {
}
---Absolute Node path to LSP severity level
---@alias NodeSeverities table<string, lsp.DiagnosticSeverity>
---@alias NodeSeverities table<string, vim.diagnostic.Severity>
---@class DiagStatus
---@field value lsp.DiagnosticSeverity|nil
@@ -35,39 +38,17 @@ local function uniformize_path(path)
return utils.canonical_path(path:gsub("\\", "/"))
end
---Marshal severities from LSP. Does nothing when LSP disabled.
---@return NodeSeverities
local function from_nvim_lsp()
local buffer_severity = {}
-- is_enabled is not present in all 0.10 builds/releases, see #2781
local is_enabled = false
if vim.fn.has "nvim-0.10" == 1 and type(vim.diagnostic.is_enabled) == "function" then
is_enabled = vim.diagnostic.is_enabled()
elseif type(vim.diagnostic.is_disabled) == "function" then ---@diagnostic disable-line: deprecated
is_enabled = not vim.diagnostic.is_disabled() ---@diagnostic disable-line: deprecated
end
if is_enabled then
for _, diagnostic in ipairs(vim.diagnostic.get(nil, { severity = M.severity })) do
if diagnostic.severity and diagnostic.bufnr and vim.api.nvim_buf_is_valid(diagnostic.bufnr) then
local bufname = uniformize_path(vim.api.nvim_buf_get_name(diagnostic.bufnr))
if not buffer_severity[bufname] or diagnostic.severity < buffer_severity[bufname] then
buffer_severity[bufname] = diagnostic.severity
end
end
end
end
return buffer_severity
end
---Severity is within diagnostics.severity.min, diagnostics.severity.max
---@param severity lsp.DiagnosticSeverity
---@param config table
---Alway in range when using vim.diagnostic.Opts:
---@see nvim_tree.config.diagnostics.diagnostic_opts
---@param severity vim.diagnostic.Severity
---@return boolean
local function is_severity_in_range(severity, config)
return config.max <= severity and severity <= config.min
local function is_severity_in_range(severity)
if config.g.diagnostics.diagnostic_opts then
return true
else
return severity >= config.g.diagnostics.severity.max and severity <= config.g.diagnostics.severity.min
end
end
---Handle any COC exceptions, preventing any propagation
@@ -77,7 +58,7 @@ local function handle_coc_exception(err)
local notify = true
-- avoid distractions on interrupts (CTRL-C)
if err:find "Vim:Interrupt" or err:find "Keyboard interrupt" then
if err:find("Vim:Interrupt") or err:find("Keyboard interrupt") then
notify = false
end
@@ -100,7 +81,7 @@ local function from_coc()
end
local ok, diagnostic_list = xpcall(function()
return vim.fn.CocAction "diagnosticList"
return vim.fn.CocAction("diagnosticList")
end, handle_coc_exception)
if not ok or type(diagnostic_list) ~= "table" or vim.tbl_isempty(diagnostic_list) then
return {}
@@ -111,7 +92,7 @@ local function from_coc()
local bufname = uniformize_path(diagnostic.file)
local coc_severity = COC_SEVERITY_LEVELS[diagnostic.severity]
local highest_severity = buffer_severity[bufname] or coc_severity
if is_severity_in_range(highest_severity, M.severity) then
if is_severity_in_range(highest_severity) then
buffer_severity[bufname] = math.min(highest_severity, coc_severity)
end
end
@@ -125,7 +106,7 @@ end
local function from_cache(node)
local nodepath = uniformize_path(node.absolute_path)
local max_severity = nil
if not node.nodes then
if not node:is(DirectoryNode) then
-- direct cache hit for files
max_severity = NODE_SEVERITIES[nodepath]
else
@@ -133,11 +114,8 @@ local function from_cache(node)
for bufname, severity in pairs(NODE_SEVERITIES) do
local node_contains_buf = vim.startswith(bufname, nodepath .. "/")
if node_contains_buf then
if severity == M.severity.max then
if not max_severity or severity < max_severity then
max_severity = severity
break
else
max_severity = math.min(max_severity or severity, severity)
end
end
end
@@ -145,27 +123,76 @@ local function from_cache(node)
return { value = max_severity, cache_version = NODE_SEVERITIES_VERSION }
end
---Fired on DiagnosticChanged and CocDiagnosticChanged events:
---debounced retrieval, cache update, version increment and draw
function M.update()
if not M.enable then
---Fired on DiagnosticChanged for a single buffer.
---This will be called on set and reset of diagnostics.
---On disabling LSP, a reset event will be sent for all buffers.
---@param ev table standard event with data.diagnostics populated
function M.update_lsp(ev)
log.line("diagnostics", "DiagnosticChanged")
if not config.g.diagnostics.enable or not ev or not ev.data or not ev.data.diagnostics then
return
end
utils.debounce("diagnostics", M.debounce_delay, function()
local profile = log.profile_start "diagnostics update"
if is_using_coc() then
NODE_SEVERITIES = from_coc()
else
NODE_SEVERITIES = from_nvim_lsp()
local profile_event = log.profile_start("DiagnosticChanged event")
local diagnostics = vim.diagnostic.get(ev.buf)
-- use the buffer from the event, as ev.data.diagnostics will be empty on resolved diagnostics
local bufname = uniformize_path(vim.api.nvim_buf_get_name(ev.buf))
---@type vim.diagnostic.Severity?
local new_severity = nil
-- most severe (lowest) severity in user range
for _, diagnostic in ipairs(diagnostics) do
if is_severity_in_range(diagnostic.severity) then
if not new_severity or diagnostic.severity < new_severity then
new_severity = diagnostic.severity
end
end
end
-- record delta and schedule a redraw
if new_severity ~= NODE_SEVERITIES[bufname] then
NODE_SEVERITIES[bufname] = new_severity
NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1
if log.enabled "diagnostics" then
utils.debounce("DiagnosticChanged redraw", config.g.diagnostics.debounce_delay, function()
local profile_redraw = log.profile_start("DiagnosticChanged redraw")
local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end
log.profile_end(profile_redraw)
end)
end
log.profile_end(profile_event)
end
---Fired on CocDiagnosticChanged events:
---debounced retrieval, cache update, version increment and draw
function M.update_coc()
log.line("diagnostics", "CocDiagnosticChange")
if not config.g.diagnostics.enable then
return
end
utils.debounce("CocDiagnosticChanged update", config.g.diagnostics.debounce_delay, function()
local profile = log.profile_start("CocDiagnosticChanged update")
NODE_SEVERITIES = from_coc()
NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1
if log.enabled("diagnostics") then
for bufname, severity in pairs(NODE_SEVERITIES) do
log.line("diagnostics", "Indexing bufname '%s' with severity %d", bufname, severity)
log.line("diagnostics", "COC Indexing bufname '%s' with severity %d", bufname, severity)
end
end
log.profile_end(profile)
if view.is_buf_valid(view.get_bufnr()) then
local bufnr = view.get_bufnr()
local should_draw = bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr)
if should_draw then
local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
@@ -179,12 +206,12 @@ end
---@param node Node
---@return DiagStatus|nil
function M.get_diag_status(node)
if not M.enable then
if not config.g.diagnostics.enable then
return nil
end
-- dir but we shouldn't show on dirs at all
if node.nodes ~= nil and not M.show_on_dirs then
if node:is(DirectoryNode) and not config.g.diagnostics.show_on_dirs then
return nil
end
@@ -195,29 +222,18 @@ function M.get_diag_status(node)
node.diag_status = from_cache(node)
end
local dir = node:as(DirectoryNode)
-- file
if not node.nodes then
if not dir then
return node.diag_status
end
-- dir is closed or we should show on open_dirs
if not node.open or M.show_on_open_dirs then
if not dir.open or config.g.diagnostics.show_on_open_dirs then
return node.diag_status
end
return nil
end
function M.setup(opts)
M.enable = opts.diagnostics.enable
M.debounce_delay = opts.diagnostics.debounce_delay
M.severity = opts.diagnostics.severity
if M.enable then
log.line("diagnostics", "setup")
end
M.show_on_dirs = opts.diagnostics.show_on_dirs
M.show_on_open_dirs = opts.diagnostics.show_on_open_dirs
end
return M

Some files were not shown because too many files have changed in this diff Show More