Compare commits

..

43 Commits

Author SHA1 Message Date
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
58 changed files with 1399 additions and 579 deletions

View File

@@ -7,6 +7,9 @@ end_of_line = lf
[nvim-tree-lua.txt] [nvim-tree-lua.txt]
max_line_length = 78 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] [*.lua]
indent_style = space indent_style = space
max_line_length = 140 max_line_length = 140

View File

@@ -20,45 +20,25 @@ jobs:
strategy: strategy:
matrix: matrix:
lua_version: [ 5.1 ] lua_version: [ 5.1 ]
luacheck_version: [ 1.2.0 ]
steps: steps:
- uses: actions/checkout@v4 - name: checkout
uses: actions/checkout@v4
- uses: leafo/gh-actions-lua@v10 - name: install lua ${{ matrix.lua_version }}
uses: leafo/gh-actions-lua@v11
with: with:
luaVersion: ${{ matrix.lua_version }} luaVersion: ${{ matrix.lua_version }}
- uses: leafo/gh-actions-luarocks@v4 - name: install luarocks
uses: leafo/gh-actions-luarocks@v5
- run: luarocks install luacheck 1.1.1 - name: install luacheck ${{ matrix.luacheck_version }}
run: luarocks install luacheck ${{ matrix.luacheck_version }}
- run: make lint - run: make lint
style:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ matrix.emmy_lua_code_style_version }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
strategy:
matrix:
emmy_lua_code_style_version: [ 1.5.6 ]
steps:
- uses: actions/checkout@v4
- name: install emmy_lua_code_style
run: |
mkdir -p CodeFormat
curl -L "https://github.com/CppCXY/EmmyLuaCodeStyle/releases/download/${{ matrix.emmy_lua_code_style_version }}/linux-x64.tar.gz" | tar zx --directory CodeFormat
- run: echo "CodeFormat/linux-x64/bin" >> "$GITHUB_PATH"
- run: make style
- run: make style-doc
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -69,26 +49,31 @@ jobs:
strategy: strategy:
matrix: matrix:
nvim_version: [ stable, nightly ] nvim_version: [ stable, nightly ]
luals_version: [ 3.11.0 ] luals_version: [ 3.13.9 ]
env:
VIMRUNTIME: /home/runner/nvim-${{ matrix.nvim_version }}/share/nvim/runtime
steps: steps:
- uses: actions/checkout@v4 - name: checkout
uses: actions/checkout@v4
- uses: rhysd/action-setup-vim@v1 - name: install nvim ${{ matrix.nvim_version }}
uses: rhysd/action-setup-vim@v1
with: with:
neovim: true neovim: true
version: ${{ matrix.nvim_version }} version: ${{ matrix.nvim_version }}
- name: install luals - name: install lua-language-server ${{ matrix.luals_version }}
run: | run: |
mkdir -p luals 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 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" - run: make check
- name: make check
env:
VIMRUNTIME: /home/runner/nvim-${{ matrix.nvim_version }}/share/nvim/runtime
run: make check
- run: make help-check - run: make help-check
- run: make style
- run: make style-doc

View File

@@ -1,9 +1,11 @@
name: Luarocks Release name: Luarocks Release
on: on:
push: push:
tags: tags:
- 'v[0-9]+.[0-9]+.[0-9]+' - v*
workflow_dispatch: workflow_dispatch:
jobs: jobs:
luarocks-upload: luarocks-upload:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

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

View File

@@ -1,12 +1,23 @@
{ {
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
"runtime.version.luals-check-only": "Lua 5.1",
"workspace": { "workspace": {
"library": [ "library": [
"$VIMRUNTIME/lua/vim", "$VIMRUNTIME/lua/vim",
"${3rd}/luv/library" "${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": { "diagnostics": {
"libraryFiles": "Disable", "libraryFiles": "Disable",
"globals": [], "globals": [],

View File

@@ -1,3 +1,3 @@
{ {
".": "1.8.0" ".": "1.13.0"
} }

View File

@@ -1,5 +1,91 @@
# Changelog # Changelog
## [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) ## [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)

View File

@@ -12,9 +12,9 @@ Language server: [luals](https://luals.github.io)
Lint: [luacheck](https://github.com/lunarmodules/luacheck/) Lint: [luacheck](https://github.com/lunarmodules/luacheck/)
Style: [EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle): `CodeCheck` Style Fixing: [EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle): `CodeCheck`
nvim-tree.lua migrated from stylua to EmmyLuaCodeStyle ~2024/10. `vim.lsp.buf.format()` may be used as it is the default formatter for luals nvim-tree.lua 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)
You can install them via you OS package manager e.g. `pacman`, `brew` or other via other package managers such as `cargo` or `luarocks` You can install them via you OS package manager e.g. `pacman`, `brew` or other via other package managers such as `cargo` or `luarocks`
@@ -36,14 +36,14 @@ make lint
## style ## style
1. Runs CodeCheck using `.editorconfig` settings 1. Runs lua language server `codestyle-check` only, using `.luarc.json` settings
1. Runs `scripts/doc-comments.sh` to validate annotated documentation 1. Runs `scripts/doc-comments.sh` to validate annotated documentation
```sh ```sh
make style make style
``` ```
You can automatically fix `CodeCheck` issues via: You can automatically fix style issues using `CodeCheck`:
```sh ```sh
make style-fix make style-fix

View File

@@ -13,11 +13,11 @@ check: luals
# subtasks # subtasks
# #
luacheck: luacheck:
luacheck -q lua luacheck --codes --quiet lua --exclude-files "**/_meta/**"
# --diagnosis-as-error does not function for workspace, hence we post-process the output # --diagnosis-as-error does not function for workspace, hence we post-process the output
style-check: style-check:
CodeFormat check --config .editorconfig --diagnosis-as-error --workspace lua @scripts/luals-check.sh codestyle-check
style-doc: style-doc:
scripts/doc-comments.sh scripts/doc-comments.sh

View File

@@ -162,13 +162,13 @@ nvim-tree exposes a public API. This is non breaking, with additions made as nec
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. 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) 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. 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.

View File

@@ -52,14 +52,16 @@ CONTENTS *nvim-tree*
8.2 Highlight: Overhaul |nvim-tree-highlight-overhaul| 8.2 Highlight: Overhaul |nvim-tree-highlight-overhaul|
9. Events |nvim-tree-events| 9. Events |nvim-tree-events|
10. Prompts |nvim-tree-prompts| 10. Prompts |nvim-tree-prompts|
11. OS Specific Restrictions |nvim-tree-os-specific| 11. Decorators |nvim-tree-decorators|
12. Netrw |nvim-tree-netrw| 11.1 Decorator Example |nvim-tree-decorator-example|
13. Legacy |nvim-tree-legacy| 12. OS Specific Restrictions |nvim-tree-os-specific|
13.1 Legacy: Opts |nvim-tree-legacy-opts| 13. Netrw |nvim-tree-netrw|
13.2 Legacy: Highlight |nvim-tree-legacy-highlight| 14. Legacy |nvim-tree-legacy|
14. Index |nvim-tree-index| 14.1 Legacy: Opts |nvim-tree-legacy-opts|
14.1 Index: Opts |nvim-tree-index-opts| 14.2 Legacy: Highlight |nvim-tree-legacy-highlight|
14.2 Index: API |nvim-tree-index-api| 15. Index |nvim-tree-index|
15.1 Index: Opts |nvim-tree-index-opts|
15.2 Index: API |nvim-tree-index-api|
============================================================================== ==============================================================================
1. INTRODUCTION *nvim-tree-introduction* 1. INTRODUCTION *nvim-tree-introduction*
@@ -204,7 +206,7 @@ Show the mappings: `g?`
`S` Search |nvim-tree-api.tree.search_node()| `S` Search |nvim-tree-api.tree.search_node()|
`u` Rename: Full Path |nvim-tree-api.fs.rename_full()| `u` Rename: Full Path |nvim-tree-api.fs.rename_full()|
`U` Toggle Filter: Hidden |nvim-tree-api.tree.toggle_custom_filter()| `U` Toggle Filter: Hidden |nvim-tree-api.tree.toggle_custom_filter()|
`W` Collapse |nvim-tree-api.tree.collapse_all()| `W` Collapse All |nvim-tree-api.tree.collapse_all()|
`x` Cut |nvim-tree-api.fs.cut()| `x` Cut |nvim-tree-api.fs.cut()|
`y` Copy Name |nvim-tree-api.fs.copy.filename()| `y` Copy Name |nvim-tree-api.fs.copy.filename()|
`Y` Copy Relative Path |nvim-tree-api.fs.copy.relative_path()| `Y` Copy Relative Path |nvim-tree-api.fs.copy.relative_path()|
@@ -339,7 +341,7 @@ See |nvim-tree-highlight| for details.
See |nvim-tree-api.tree.collapse_all()| See |nvim-tree-api.tree.collapse_all()|
Calls: `api.tree.collapse_all(false)` Calls: `api.tree.collapse_all({ keep_buffers = false })`
*:NvimTreeCollapseKeepBuffers* *:NvimTreeCollapseKeepBuffers*
@@ -348,7 +350,7 @@ See |nvim-tree-highlight| for details.
See |nvim-tree-api.tree.collapse_all()| See |nvim-tree-api.tree.collapse_all()|
Calls: `api.tree.collapse_all(true)` Calls: `api.tree.collapse_all({ keep_buffers = true })`
*:NvimTreeHiTest* *:NvimTreeHiTest*
@@ -425,6 +427,7 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" }, special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none", hidden_display = "none",
symlink_destination = true, symlink_destination = true,
decorators = { "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", },
highlight_git = "none", highlight_git = "none",
highlight_diagnostics = "none", highlight_diagnostics = "none",
highlight_opened_files = "none", highlight_opened_files = "none",
@@ -459,7 +462,10 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua
hidden_placement = "after", hidden_placement = "after",
diagnostics_placement = "signcolumn", diagnostics_placement = "signcolumn",
bookmarks_placement = "signcolumn", bookmarks_placement = "signcolumn",
padding = " ", padding = {
icon = " ",
folder_arrow = " ",
},
symlink_arrow = " ➛ ", symlink_arrow = " ➛ ",
show = { show = {
file = true, file = true,
@@ -527,7 +533,7 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua
enable = false, enable = false,
show_on_dirs = false, show_on_dirs = false,
show_on_open_dirs = true, show_on_open_dirs = true,
debounce_delay = 50, debounce_delay = 500,
severity = { severity = {
min = vim.diagnostic.severity.HINT, min = vim.diagnostic.severity.HINT,
max = vim.diagnostic.severity.ERROR, max = vim.diagnostic.severity.ERROR,
@@ -797,22 +803,22 @@ Width of the window: can be a `%` string, a number representing columns, a
function or a table. function or a table.
A table indicates that the view should be dynamically sized based on the A table indicates that the view should be dynamically sized based on the
longest line. longest line.
Type: `string | number | table | function()` returning a number Type: `string | number | table | fun(): number|string`
Default: `30` Default: `30`
*nvim-tree.view.width.min* *nvim-tree.view.width.min*
Minimum dynamic width. Minimum dynamic width.
Type: `string | number | function()` returning a number Type: `string | number | fun(): number|string`
Default: `30` Default: `30`
*nvim-tree.view.width.max* *nvim-tree.view.width.max*
Maximum dynamic width, -1 for unbounded. Maximum dynamic width, -1 for unbounded.
Type: `string | number | function()` returning a number Type: `string | number | fun(): number|string`
Default: `-1` Default: `-1`
*nvim-tree.view.width.padding* *nvim-tree.view.width.padding*
Extra padding to the right. Extra padding to the right.
Type: `number | function()` returning a number Type: `number | fun(): number|string`
Default: `1` Default: `1`
*nvim-tree.view.float* *nvim-tree.view.float*
@@ -842,9 +848,6 @@ Use nvim-tree in a floating window.
============================================================================== ==============================================================================
5.3 OPTS: RENDERER *nvim-tree-opts-renderer* 5.3 OPTS: RENDERER *nvim-tree-opts-renderer*
Highlight precedence, additive:
git < opened < modified < bookmarked < diagnostics < copied < cut
*nvim-tree.renderer.add_trailing* *nvim-tree.renderer.add_trailing*
Appends a trailing slash to folder names. Appends a trailing slash to folder names.
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
@@ -927,6 +930,22 @@ Show a summary of hidden files below the tree using `NvimTreeHiddenDisplay
Whether to show the destination of the symlink. Whether to show the destination of the symlink.
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
*nvim-tree.renderer.decorators*
Highlighting and icons for the nodes, in increasing order of precedence.
Uses strings to specify builtin decorators otherwise specify your
`nvim_tree.api.decorator.UserDecorator` class.
Type: `nvim_tree.api.decorator.Name[]`, Default: >lua
{
"Git",
"Open",
"Hidden",
"Modified",
"Bookmark",
"Diagnostics",
"Copied",
"Cut",
}
<
*nvim-tree.renderer.highlight_git* *nvim-tree.renderer.highlight_git*
Enable highlight for git attributes using `NvimTreeGit*HL` highlight groups. Enable highlight for git attributes using `NvimTreeGit*HL` highlight groups.
Requires |nvim-tree.git.enable| Requires |nvim-tree.git.enable|
@@ -996,9 +1015,6 @@ Configuration options for tree indent markers.
*nvim-tree.renderer.icons* *nvim-tree.renderer.icons*
Configuration options for icons. Configuration options for icons.
Icon order and sign column precedence:
git < hidden < modified < bookmarked < diagnostics
`renderer.icons.*_placement` options may be: `renderer.icons.*_placement` options may be:
- `"before"` : before file/folder, after the file/folders icons - `"before"` : before file/folder, after the file/folders icons
- `"after"` : after file/folder - `"after"` : after file/folder
@@ -1052,10 +1068,14 @@ Icon order and sign column precedence:
Bookmark icon placement. Bookmark icon placement.
Type: `string`, Default: `signcolumn` Type: `string`, Default: `signcolumn`
*nvim-tree.renderer.icons.padding* *nvim-tree.renderer.icons.padding.icon*
Inserted between icon and filename. Inserted between icon and filename.
Type: `string`, Default: `" "` Type: `string`, Default: `" "`
*nvim-tree.renderer.icons.padding.folder_arrow*
Inserted between folder arrow icon and file/folder icon.
Type: `string`, Default: `" "`
*nvim-tree.renderer.icons.symlink_arrow* *nvim-tree.renderer.icons.symlink_arrow*
Used as a separator between symlinks' source and target. Used as a separator between symlinks' source and target.
Type: `string`, Default: `" ➛ "` Type: `string`, Default: `" ➛ "`
@@ -1272,7 +1292,7 @@ Enable/disable the feature.
*nvim-tree.diagnostics.debounce_delay* *nvim-tree.diagnostics.debounce_delay*
Idle milliseconds between diagnostic event and update. Idle milliseconds between diagnostic event and update.
Type: `number`, Default: `50` (ms) Type: `number`, Default: `500` (ms)
*nvim-tree.diagnostics.show_on_dirs* *nvim-tree.diagnostics.show_on_dirs*
Show diagnostic icons on parent directories. Show diagnostic icons on parent directories.
@@ -1450,7 +1470,8 @@ vim |current-directory| behaviour.
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
*nvim-tree.actions.expand_all* *nvim-tree.actions.expand_all*
Configuration for expand_all behaviour. Configuration for |nvim-tree-api.tree.expand_all()| and
|nvim-tree-api.node.expand()|
*nvim-tree.actions.expand_all.max_folder_discovery* *nvim-tree.actions.expand_all.max_folder_discovery*
Limit the number of folders being explored when expanding every folders. Limit the number of folders being explored when expanding every folders.
@@ -1503,7 +1524,8 @@ Configuration options for opening a file from nvim-tree.
*nvim-tree.actions.open_file.window_picker.enable* *nvim-tree.actions.open_file.window_picker.enable*
Enable the feature. If the feature is not enabled, files will open in Enable the feature. If the feature is not enabled, files will open in
window from which you last opened the tree. window from which you last opened the tree, obeying
|nvim-tree.actions.open_file.window_picker.exclude|
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
*nvim-tree.actions.open_file.window_picker.picker* *nvim-tree.actions.open_file.window_picker.picker*
@@ -1522,9 +1544,10 @@ Configuration options for opening a file from nvim-tree.
Type: `string`, Default: `"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"` Type: `string`, Default: `"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"`
*nvim-tree.actions.open_file.window_picker.exclude* *nvim-tree.actions.open_file.window_picker.exclude*
Table of buffer option names mapped to a list of option values that Table of buffer option names mapped to a list of option values.
indicates to the picker that the buffer's window should not be Windows containing matching buffers will not be:
selectable. - available when using a window picker
- selected when not using a window picker
Type: `table`, Default: >lua Type: `table`, Default: >lua
{ {
filetype = { filetype = {
@@ -1809,10 +1832,13 @@ tree.find_file({opts}) *nvim-tree-api.tree.find_file()*
tree.search_node() *nvim-tree-api.tree.search_node()* tree.search_node() *nvim-tree-api.tree.search_node()*
Open the search dialogue as per the search_node action. Open the search dialogue as per the search_node action.
tree.collapse_all({keep_buffers}) *nvim-tree-api.tree.collapse_all()* tree.collapse_all({opts}) *nvim-tree-api.tree.collapse_all()*
Collapse the tree. Collapse the tree.
Parameters: ~ Parameters: ~
• {opts} (table) optional parameters
Options: ~
• {keep_buffers} (boolean) do not collapse nodes with open buffers. • {keep_buffers} (boolean) do not collapse nodes with open buffers.
tree.expand_all({node}) *nvim-tree-api.tree.expand_all()* tree.expand_all({node}) *nvim-tree-api.tree.expand_all()*
@@ -1881,7 +1907,7 @@ tree.winid({opts}) *nvim-tree-api.tree.winid()*
• {opts} (table) optional parameters • {opts} (table) optional parameters
Options: ~ Options: ~
• {tabpage} (number|nil) tabpage, 0 or nil for current, default nil • {tabpage} (number|nil) tabpage, 0 or nil for current, default nil
Return: ~ Return: ~
(number) winid or nil if tree is not visible (number) winid or nil if tree is not visible
@@ -1994,33 +2020,99 @@ fs.print_clipboard() *nvim-tree-api.fs.print_clipboard()*
Parameters: ~ Parameters: ~
• {node} (Node|nil) file or folder • {node} (Node|nil) file or folder
node.open.edit({node}) *nvim-tree-api.node.open.edit()* node.open.edit({node}, {opts}) *nvim-tree-api.node.open.edit()*
File: open as per |nvim-tree.actions.open_file| File: open as per |nvim-tree.actions.open_file|
Folder: expand or collapse Folder: expand or collapse
Root: change directory up Root: change directory up
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
*nvim-tree-api.node.open.replace_tree_buffer()* *nvim-tree-api.node.open.replace_tree_buffer()*
node.open.replace_tree_buffer({node}) node.open.replace_tree_buffer({node})
|nvim-tree-api.node.edit()|, file will be opened in place: in the |nvim-tree-api.node.edit()|, file will be opened in place: in the
nvim-tree window. nvim-tree window.
*nvim-tree-api.node.open.no_window_picker()* *nvim-tree-api.node.open.no_window_picker()*
node.open.no_window_picker({node}) node.open.no_window_picker({node}, {opts})
|nvim-tree-api.node.edit()|, window picker will never be used as per |nvim-tree-api.node.edit()|, window picker will never be used as per
|nvim-tree.actions.open_file.window_picker.enable| `false` |nvim-tree.actions.open_file.window_picker.enable| `false`
node.open.vertical({node}) *nvim-tree-api.node.open.vertical()* Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
node.open.vertical({node}, {opts}) *nvim-tree-api.node.open.vertical()*
|nvim-tree-api.node.edit()|, file will be opened in a new vertical split. |nvim-tree-api.node.edit()|, file will be opened in a new vertical split.
node.open.horizontal({node}) *nvim-tree-api.node.open.horizontal()* Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
*nvim-tree-api.node.open.vertical_no_picker()*
node.open.vertical_no_picker({node}, {opts})
|nvim-tree-api.node.vertical()|, window picker will never be used as per
|nvim-tree.actions.open_file.window_picker.enable| `false`
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
node.open.horizontal({node}, {opts}) *nvim-tree-api.node.open.horizontal()*
|nvim-tree-api.node.edit()|, file will be opened in a new horizontal split. |nvim-tree-api.node.edit()|, file will be opened in a new horizontal split.
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
*nvim-tree-api.node.open.horizontal_no_picker()*
node.open.horizontal_no_picker({node}, {opts})
|nvim-tree-api.node.horizontal()|, window picker will never be used as per
|nvim-tree.actions.open_file.window_picker.enable| `false`
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
*nvim-tree-api.node.open.toggle_group_empty()* *nvim-tree-api.node.open.toggle_group_empty()*
node.open.toggle_group_empty({node}) node.open.toggle_group_empty({node}, {opts})
Toggle |nvim-tree.renderer.group_empty| for a specific folder. Toggle |nvim-tree.renderer.group_empty| for a specific folder.
Does nothing on files. Does nothing on files.
Needs |nvim-tree.renderer.group_empty| set. Needs |nvim-tree.renderer.group_empty| set.
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
node.open.drop({node}) *nvim-tree-api.node.open.drop()* node.open.drop({node}) *nvim-tree-api.node.open.drop()*
Switch to window with selected file if it exists. Switch to window with selected file if it exists.
Open file otherwise. Open file otherwise.
@@ -2030,9 +2122,17 @@ node.open.drop({node}) *nvim-tree-api.node.open.drop()*
Folder: expand or collapse Folder: expand or collapse
Root: change directory up Root: change directory up
node.open.tab({node}) *nvim-tree-api.node.open.tab()* node.open.tab({node}, {opts}) *nvim-tree-api.node.open.tab()*
|nvim-tree-api.node.edit()|, file will be opened in a new tab. |nvim-tree-api.node.edit()|, file will be opened in a new tab.
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
*nvim-tree-api.node.open.tab_drop()* *nvim-tree-api.node.open.tab_drop()*
node.open.tab_drop({node}) node.open.tab_drop({node})
Switch to tab containing window with selected file if it exists. Switch to tab containing window with selected file if it exists.
@@ -2042,15 +2142,31 @@ node.open.tab_drop({node})
Folder: expand or collapse Folder: expand or collapse
Root: change directory up Root: change directory up
node.open.preview({node}) *nvim-tree-api.node.open.preview()* node.open.preview({node}, {opts}) *nvim-tree-api.node.open.preview()*
|nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`. |nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`.
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
*nvim-tree-api.node.open.preview_no_picker()* *nvim-tree-api.node.open.preview_no_picker()*
node.open.preview_no_picker({node}) node.open.preview_no_picker({node}, {opts})
|nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`. |nvim-tree-api.node.edit()|, file buffer will have |bufhidden| set to `delete`.
window picker will never be used as per window picker will never be used as per
|nvim-tree.actions.open_file.window_picker.enable| `false` |nvim-tree.actions.open_file.window_picker.enable| `false`
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {quit_on_open} (boolean) quits the tree when opening the file
• {focus} (boolean) keep focus in the tree when opening the file
node.navigate.git.next({node}) *nvim-tree-api.node.navigate.git.next()* node.navigate.git.next({node}) *nvim-tree-api.node.navigate.git.next()*
Navigate to the next item showing git status. Navigate to the next item showing git status.
@@ -2141,6 +2257,45 @@ node.run.cmd({node}) *nvim-tree-api.node.run.cmd()*
node.run.system({node}) *nvim-tree-api.node.run.system()* node.run.system({node}) *nvim-tree-api.node.run.system()*
Execute |nvim-tree.system_open| Execute |nvim-tree.system_open|
node.buffer.delete({node}, {opts}) *nvim-tree-api.node.buffer.delete()*
Deletes node's related buffer, if one exists.
Executes |:bdelete| or |:bdelete|!
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {force} (boolean) delete even if buffer is modified, default false
node.buffer.wipe({node}, {opts}) *nvim-tree-api.node.buffer.wipe()*
Wipes node's related buffer, if one exists.
Executes |:bwipe| or |:bwipe|!
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {force} (boolean) wipe even if buffer is modified, default false
node.expand({node}) *nvim-tree-api.node.expand()*
Recursively expand all nodes under a directory or a file's parent
directory.
Parameters: ~
• {node} (Node|nil) file or folder
node.collapse({node}, {opts}) *nvim-tree-api.node.collapse()*
Collapse the tree under a directory or a file's parent directory.
Parameters: ~
• {node} (Node|nil) file or folder
• {opts} (table) optional parameters
Options: ~
• {keep_buffers} (boolean) do not collapse nodes with open buffers.
============================================================================== ==============================================================================
6.4 API GIT *nvim-tree-api.git* 6.4 API GIT *nvim-tree-api.git*
@@ -2395,7 +2550,7 @@ You are encouraged to copy these to your own |nvim-tree.on_attach| function. >lu
vim.keymap.set("n", "S", api.tree.search_node, opts("Search")) vim.keymap.set("n", "S", api.tree.search_node, opts("Search"))
vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path")) vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path"))
vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden")) vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden"))
vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse")) vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse All"))
vim.keymap.set("n", "x", api.fs.cut, opts("Cut")) vim.keymap.set("n", "x", api.fs.cut, opts("Cut"))
vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name")) vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name"))
vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path")) vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path"))
@@ -2644,13 +2799,21 @@ e.g. handler for node renamed: >lua
|nvim_tree_events_kind| |nvim_tree_events_kind|
- Event.Ready - Event.Ready
When NvimTree has been initialized When NvimTree has been initialized.
• Note: Handler takes no parameter.
- Event.TreePreOpen
Invoked before the window and buffer for NvimTree are created
or opened. Before |Event.TreeOpen| event.
• Note: Handler takes no parameter. • Note: Handler takes no parameter.
- Event.TreeOpen - Event.TreeOpen
Invoked after the NvimTree is opened.
• Note: Handler takes no parameter. • Note: Handler takes no parameter.
- Event.TreeClose - Event.TreeClose
Invoked after the NvimTree is closed, but before the window is
closed. Dispatched on |WinClosed| event for NvimTree window.
• Note: Handler takes no parameter. • Note: Handler takes no parameter.
- Event.Resize - When NvimTree is resized. - Event.Resize - When NvimTree is resized.
@@ -2755,7 +2918,90 @@ configurations for different types of prompts.
send all bookmarked to trash during |nvim-tree-api.marks.bulk.trash()| send all bookmarked to trash during |nvim-tree-api.marks.bulk.trash()|
============================================================================== ==============================================================================
11. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific* 11. DECORATORS *nvim-tree-decorators*
Highlighting and icons for nodes are provided by Decorators. You may provide
your own in addition to the builtin decorators.
Decorators may:
- Add icons
- Set highlight group for the name or icons
- Override node icon
Specify decorators and their precedence via |nvim-tree.renderer.decorators|
e.g. defaults with a user decorator class being overridden only by Cut: >lua
{
"Git",
"Open",
"Hidden",
"Modified",
"Bookmark",
"Diagnostics",
"Copied",
MyDecorator,
"Cut",
}
See `nvim-tree/_meta/api_decorator.lua` for full
`nvim_tree.api.decorator.UserDecorator` class documentation.
<
==============================================================================
11.1. DECORATOR EXAMPLE *nvim-tree-decorator-example*
>lua
---Create your decorator class
---@class (exact) MyDecorator: nvim_tree.api.decorator.UserDecorator
---@field private my_icon nvim_tree.api.HighlightedString
local MyDecorator = require("nvim-tree.api").decorator.UserDecorator:extend()
---Mandatory constructor :new() will be called once per tree render, with no arguments.
function MyDecorator:new()
self.enabled = true
self.highlight_range = "all"
self.icon_placement = "signcolumn"
-- create your icon once, for convenience
self.my_icon = { str = "I", hl = { "MyIcon" } }
-- Define the icon sign only once
-- Only needed if you are using icon_placement = "signcolumn"
self:define_sign(self.my_icon)
end
---Override node icon
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString? icon_node
function MyDecorator:icon_node(node)
if node.name == "example" then
return self.my_icon
else
return nil
end
end
---Return one icon for DecoratorIconPlacement
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString[]? icons
function MyDecorator:icons(node)
if node.name == "example" then
return { self.my_icon }
else
return nil
end
end
---Exactly one highlight group for DecoratorHighlightRange
---@param node nvim_tree.api.Node
---@return string? highlight_group
function MyDecorator:highlight_group(node)
if node.name == "example" then
return "MyHighlight"
else
return nil
end
end
<
==============================================================================
12. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific*
Windows WSL and PowerShell Windows WSL and PowerShell
- Trash is synchronized - Trash is synchronized
@@ -2767,7 +3013,7 @@ Windows WSL and PowerShell
issues or disable this feature. issues or disable this feature.
============================================================================== ==============================================================================
12. NETRW *nvim-tree-netrw* 13. NETRW *nvim-tree-netrw*
|netrw| is a standard neovim plugin that is enabled by default. It provides, |netrw| is a standard neovim plugin that is enabled by default. It provides,
amongst other functionality, a file/directory browser. amongst other functionality, a file/directory browser.
@@ -2788,14 +3034,14 @@ keep using |netrw| without its browser features please ensure:
|nvim-tree.hijack_netrw| ` = true` |nvim-tree.hijack_netrw| ` = true`
============================================================================== ==============================================================================
13. LEGACY *nvim-tree-legacy* 14. LEGACY *nvim-tree-legacy*
Breaking refactors have been made however the legacy versions will be silently Breaking refactors have been made however the legacy versions will be silently
migrated and used. migrated and used.
There are no plans to remove this migration. There are no plans to remove this migration.
============================================================================== ==============================================================================
13.1 LEGACY: OPTS *nvim-tree-legacy-opts* 14.1 LEGACY: OPTS *nvim-tree-legacy-opts*
Legacy options are translated to the current, making type and value changes as Legacy options are translated to the current, making type and value changes as
needed. needed.
@@ -2811,9 +3057,10 @@ needed.
`sort_by` |nvim-tree.sort.sorter| `sort_by` |nvim-tree.sort.sorter|
`git.ignore` |nvim-tree.filters.git_ignored| `git.ignore` |nvim-tree.filters.git_ignored|
`renderer.icons.webdev_colors` |nvim-tree.renderer.icons.web_devicons.file.color| `renderer.icons.webdev_colors` |nvim-tree.renderer.icons.web_devicons.file.color|
`renderer.icons.padding` |nvim-tree.renderer.icons.padding.icon|
============================================================================== ==============================================================================
13.2 LEGACY: HIGHLIGHT *nvim-tree-legacy-highlight* 14.2 LEGACY: HIGHLIGHT *nvim-tree-legacy-highlight*
Legacy highlight group are still obeyed when they are defined and the current Legacy highlight group are still obeyed when they are defined and the current
highlight group is not, hard linking as follows: > highlight group is not, hard linking as follows: >
@@ -2862,10 +3109,10 @@ highlight group is not, hard linking as follows: >
NvimTreeLspDiagnosticsHintFolderText NvimTreeDiagnosticHintFolderHL NvimTreeLspDiagnosticsHintFolderText NvimTreeDiagnosticHintFolderHL
< <
============================================================================== ==============================================================================
14 INDEX *nvim-tree-index* 15 INDEX *nvim-tree-index*
============================================================================== ==============================================================================
14.1 INDEX: OPTS *nvim-tree-index-opts* 15.1 INDEX: OPTS *nvim-tree-index-opts*
|nvim-tree.actions.change_dir| |nvim-tree.actions.change_dir|
|nvim-tree.actions.change_dir.enable| |nvim-tree.actions.change_dir.enable|
@@ -2943,6 +3190,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree.prefer_startup_root| |nvim-tree.prefer_startup_root|
|nvim-tree.reload_on_bufenter| |nvim-tree.reload_on_bufenter|
|nvim-tree.renderer.add_trailing| |nvim-tree.renderer.add_trailing|
|nvim-tree.renderer.decorators|
|nvim-tree.renderer.full_name| |nvim-tree.renderer.full_name|
|nvim-tree.renderer.group_empty| |nvim-tree.renderer.group_empty|
|nvim-tree.renderer.hidden_display| |nvim-tree.renderer.hidden_display|
@@ -2966,7 +3214,8 @@ highlight group is not, hard linking as follows: >
|nvim-tree.renderer.icons.glyphs.symlink| |nvim-tree.renderer.icons.glyphs.symlink|
|nvim-tree.renderer.icons.hidden_placement| |nvim-tree.renderer.icons.hidden_placement|
|nvim-tree.renderer.icons.modified_placement| |nvim-tree.renderer.icons.modified_placement|
|nvim-tree.renderer.icons.padding| |nvim-tree.renderer.icons.padding.folder_arrow|
|nvim-tree.renderer.icons.padding.icon|
|nvim-tree.renderer.icons.show| |nvim-tree.renderer.icons.show|
|nvim-tree.renderer.icons.show.bookmarks| |nvim-tree.renderer.icons.show.bookmarks|
|nvim-tree.renderer.icons.show.diagnostics| |nvim-tree.renderer.icons.show.diagnostics|
@@ -3033,7 +3282,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree.view.width.padding| |nvim-tree.view.width.padding|
============================================================================== ==============================================================================
14.2 INDEX: API *nvim-tree-index-api* 15.2 INDEX: API *nvim-tree-index-api*
|nvim-tree-api.commands.get()| |nvim-tree-api.commands.get()|
|nvim-tree-api.config.mappings.default_on_attach()| |nvim-tree-api.config.mappings.default_on_attach()|
@@ -3071,6 +3320,10 @@ highlight group is not, hard linking as follows: >
|nvim-tree-api.marks.navigate.prev()| |nvim-tree-api.marks.navigate.prev()|
|nvim-tree-api.marks.navigate.select()| |nvim-tree-api.marks.navigate.select()|
|nvim-tree-api.marks.toggle()| |nvim-tree-api.marks.toggle()|
|nvim-tree-api.node.buffer.delete()|
|nvim-tree-api.node.buffer.wipe()|
|nvim-tree-api.node.collapse()|
|nvim-tree-api.node.expand()|
|nvim-tree-api.node.navigate.diagnostics.next()| |nvim-tree-api.node.navigate.diagnostics.next()|
|nvim-tree-api.node.navigate.diagnostics.next_recursive()| |nvim-tree-api.node.navigate.diagnostics.next_recursive()|
|nvim-tree-api.node.navigate.diagnostics.prev()| |nvim-tree-api.node.navigate.diagnostics.prev()|
@@ -3092,6 +3345,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree-api.node.open.drop()| |nvim-tree-api.node.open.drop()|
|nvim-tree-api.node.open.edit()| |nvim-tree-api.node.open.edit()|
|nvim-tree-api.node.open.horizontal()| |nvim-tree-api.node.open.horizontal()|
|nvim-tree-api.node.open.horizontal_no_picker()|
|nvim-tree-api.node.open.no_window_picker()| |nvim-tree-api.node.open.no_window_picker()|
|nvim-tree-api.node.open.preview()| |nvim-tree-api.node.open.preview()|
|nvim-tree-api.node.open.preview_no_picker()| |nvim-tree-api.node.open.preview_no_picker()|
@@ -3100,6 +3354,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree-api.node.open.tab_drop()| |nvim-tree-api.node.open.tab_drop()|
|nvim-tree-api.node.open.toggle_group_empty()| |nvim-tree-api.node.open.toggle_group_empty()|
|nvim-tree-api.node.open.vertical()| |nvim-tree-api.node.open.vertical()|
|nvim-tree-api.node.open.vertical_no_picker()|
|nvim-tree-api.node.run.cmd()| |nvim-tree-api.node.run.cmd()|
|nvim-tree-api.node.run.system()| |nvim-tree-api.node.run.system()|
|nvim-tree-api.node.show_info_popup()| |nvim-tree-api.node.show_info_popup()|

View File

@@ -190,7 +190,7 @@ local function setup_autocommands(opts)
end end
if opts.hijack_directories.enable then if opts.hijack_directories.enable then
create_nvim_tree_autocmd({ "BufEnter", "BufNewFile" }, { callback = M.open_on_directory }) create_nvim_tree_autocmd({ "BufEnter", "BufNewFile" }, { callback = M.open_on_directory, nested = true })
end end
if opts.view.centralize_selection then if opts.view.centralize_selection then
@@ -199,6 +199,10 @@ local function setup_autocommands(opts)
callback = function() callback = function()
vim.schedule(function() vim.schedule(function()
vim.api.nvim_buf_call(0, 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]]) vim.cmd([[norm! zz]])
end) end)
end) end)
@@ -208,16 +212,16 @@ local function setup_autocommands(opts)
if opts.diagnostics.enable then if opts.diagnostics.enable then
create_nvim_tree_autocmd("DiagnosticChanged", { create_nvim_tree_autocmd("DiagnosticChanged", {
callback = function() callback = function(ev)
log.line("diagnostics", "DiagnosticChanged") log.line("diagnostics", "DiagnosticChanged")
require("nvim-tree.diagnostics").update() require("nvim-tree.diagnostics").update_lsp(ev)
end, end,
}) })
create_nvim_tree_autocmd("User", { create_nvim_tree_autocmd("User", {
pattern = "CocDiagnosticChange", pattern = "CocDiagnosticChange",
callback = function() callback = function()
log.line("diagnostics", "CocDiagnosticChange") log.line("diagnostics", "CocDiagnosticChange")
require("nvim-tree.diagnostics").update() require("nvim-tree.diagnostics").update_coc()
end, end,
}) })
end end
@@ -232,6 +236,20 @@ local function setup_autocommands(opts)
end, end,
}) })
end end
-- Handles event dispatch when tree is closed by `:q`
create_nvim_tree_autocmd("WinClosed", {
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,
})
end end
local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
@@ -284,6 +302,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" }, special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none", hidden_display = "none",
symlink_destination = true, symlink_destination = true,
decorators = { "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", },
highlight_git = "none", highlight_git = "none",
highlight_diagnostics = "none", highlight_diagnostics = "none",
highlight_opened_files = "none", highlight_opened_files = "none",
@@ -318,7 +337,10 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
hidden_placement = "after", hidden_placement = "after",
diagnostics_placement = "signcolumn", diagnostics_placement = "signcolumn",
bookmarks_placement = "signcolumn", bookmarks_placement = "signcolumn",
padding = " ", padding = {
icon = " ",
folder_arrow = " ",
},
symlink_arrow = "", symlink_arrow = "",
show = { show = {
file = true, file = true,
@@ -386,7 +408,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
enable = false, enable = false,
show_on_dirs = false, show_on_dirs = false,
show_on_open_dirs = true, show_on_open_dirs = true,
debounce_delay = 50, debounce_delay = 500,
severity = { severity = {
min = vim.diagnostic.severity.HINT, min = vim.diagnostic.severity.HINT,
max = vim.diagnostic.severity.ERROR, max = vim.diagnostic.severity.ERROR,

View File

@@ -0,0 +1,51 @@
---@meta
error("Cannot require a meta file")
--
-- Nodes
--
---Base Node, Abstract
---@class (exact) nvim_tree.api.Node
---@field type "file" | "directory" | "link" uv.fs_stat.result.type
---@field absolute_path string
---@field executable boolean
---@field fs_stat uv.fs_stat.result?
---@field git_status GitNodeStatus?
---@field hidden boolean
---@field name string
---@field parent nvim_tree.api.DirectoryNode?
---@field diag_severity lsp.DiagnosticSeverity?
---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
--
-- Various Types
--
---A string for rendering, with optional highlight groups to apply to it
---@class (exact) nvim_tree.api.HighlightedString
---@field str string
---@field hl string[]

View File

@@ -0,0 +1,54 @@
---@meta
error("Cannot require a meta file")
local nvim_tree = { api = { decorator = {} } }
---Highlight group range as per nvim-tree.renderer.highlight_*
---@alias nvim_tree.api.decorator.HighlightRange "none" | "icon" | "name" | "all"
---Icon position as per renderer.icons.*_placement
---@alias nvim_tree.api.decorator.IconPlacement "none" | "before" | "after" | "signcolumn" | "right_align"
---Names of builtin decorators or your decorator classes. Builtins are ordered lowest to highest priority.
---@alias nvim_tree.api.decorator.Name "Git" | "Opened" | "Hidden" | "Modified" | "Bookmarks" | "Diagnostics" | "Copied" | "Cut" | nvim_tree.api.decorator.UserDecorator
---Custom decorator, see :help nvim-tree-decorators
---
---@class (exact) nvim_tree.api.decorator.UserDecorator
---@field protected enabled boolean
---@field protected highlight_range nvim_tree.api.decorator.HighlightRange
---@field protected icon_placement nvim_tree.api.decorator.IconPlacement
nvim_tree.api.decorator.UserDecorator = {}
---Create your decorator class
---
function nvim_tree.api.decorator.UserDecorator:extend() end
---Abstract: no-args constructor must be implemented and will be called once per tree render.
---Must set all fields.
---
function nvim_tree.api.decorator.UserDecorator:new() end
---Abstract: optionally implement to set the node's icon
---
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString? icon_node
function nvim_tree.api.decorator.UserDecorator:icon_node(node) end
---Abstract: optionally implement to provide icons and the highlight groups for your icon_placement.
---
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString[]? icons
function nvim_tree.api.decorator.UserDecorator:icons(node) end
---Abstract: optionally implement to provide one highlight group to apply to your highlight_range.
---
---@param node nvim_tree.api.Node
---@return string? highlight_group
function nvim_tree.api.decorator.UserDecorator:highlight_group(node) end
---Define a sign. This should be called in the constructor.
---
---@protected
---@param icon nvim_tree.api.HighlightedString?
function nvim_tree.api.decorator.UserDecorator:define_sign(icon) end

View File

@@ -0,0 +1,58 @@
-- Copyright 2019 Yazdani Kiyan under MIT License
local notify = require("nvim-tree.notify")
local M = {}
---@param node Node
---@param opts ApiNodeDeleteWipeBufferOpts|nil
---@return nil
function M.delete(node, opts)
M.delete_buffer("delete", node.absolute_path, opts)
end
---@param node Node
---@param opts ApiNodeDeleteWipeBufferOpts|nil
---@return nil
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 ApiNodeDeleteWipeBufferOpts|nil
---@return nil
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

@@ -50,6 +50,7 @@ local function setup_window(node)
file_path = node.absolute_path, file_path = node.absolute_path,
} }
local bufnr = vim.api.nvim_create_buf(false, true) 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_buf_set_lines(bufnr, 0, -1, false, lines)
vim.api.nvim_win_set_buf(winnr, bufnr) vim.api.nvim_win_set_buf(winnr, bufnr)
end end

View File

@@ -4,6 +4,7 @@ M.file_popup = require("nvim-tree.actions.node.file-popup")
M.open_file = require("nvim-tree.actions.node.open-file") M.open_file = require("nvim-tree.actions.node.open-file")
M.run_command = require("nvim-tree.actions.node.run-command") M.run_command = require("nvim-tree.actions.node.run-command")
M.system_open = require("nvim-tree.actions.node.system-open") M.system_open = require("nvim-tree.actions.node.system-open")
M.buffer = require("nvim-tree.actions.node.buffer")
function M.setup(opts) function M.setup(opts)
require("nvim-tree.actions.node.system-open").setup(opts) require("nvim-tree.actions.node.system-open").setup(opts)

View File

@@ -43,17 +43,6 @@ local function usable_win_ids()
end, win_ids) end, win_ids)
end 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. ---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 ---@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 --- invalid window was picked / user canceled, return nil. If there are
@@ -80,6 +69,14 @@ local function pick_win_id()
local win_map = {} local win_map = {}
local laststatus = vim.o.laststatus local laststatus = vim.o.laststatus
vim.o.laststatus = 2 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 tabpage = vim.api.nvim_get_current_tabpage()
local win_ids = vim.api.nvim_tabpage_list_wins(tabpage) local win_ids = vim.api.nvim_tabpage_list_wins(tabpage)
@@ -179,6 +176,7 @@ local function pick_win_id()
end end
vim.o.laststatus = laststatus 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(M.window_picker.chars, ""), resp) then
return return
@@ -235,12 +233,16 @@ end
local function get_target_winid(mode) local function get_target_winid(mode)
local target_winid local target_winid
if not M.window_picker.enable or mode == "edit_no_picker" or mode == "preview_no_picker" then if not M.window_picker.enable or string.find(mode, "no_picker") then
target_winid = lib.target_winid target_winid = lib.target_winid
local usable_wins = usable_win_ids()
-- first available window -- first available usable window
if not vim.tbl_contains(vim.api.nvim_tabpage_list_wins(0), target_winid) then if not vim.tbl_contains(usable_wins, target_winid) then
target_winid = first_win_id() if #usable_wins > 0 then
target_winid = usable_wins[1]
else
target_winid = -1
end
end end
else else
-- pick a window -- pick a window
@@ -280,6 +282,11 @@ local function open_in_new_window(filename, mode)
return return
end 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 -- non-floating, non-nvim-tree windows
local win_ids = vim.tbl_filter(function(id) local win_ids = vim.tbl_filter(function(id)
local config = vim.api.nvim_win_get_config(id) local config = vim.api.nvim_win_get_config(id)

View File

@@ -25,7 +25,7 @@ end
---@param new_tabpage integer ---@param new_tabpage integer
---@return boolean ---@return boolean
local function is_window_event(new_tabpage) local function is_window_event(new_tabpage)
local is_event_scope_window = vim.v.event.scope == "window" or vim.v.event.changed_window local is_event_scope_window = vim.v.event.scope == "window" or vim.v.event.changed_window or false
return is_event_scope_window and new_tabpage == M.current_tab return is_event_scope_window and new_tabpage == M.current_tab
end end

View File

@@ -2,6 +2,7 @@ local utils = require("nvim-tree.utils")
local core = require("nvim-tree.core") local core = require("nvim-tree.core")
local Iterator = require("nvim-tree.iterators.node-iterator") local Iterator = require("nvim-tree.iterators.node-iterator")
local FileNode = require("nvim-tree.node.file")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
local M = {} local M = {}
@@ -23,26 +24,30 @@ local function buf_match()
end end
end end
---@param keep_buffers boolean ---Collapse a node, root if nil
function M.fn(keep_buffers) ---@param node Node?
---@param opts ApiCollapseOpts
local function collapse(node, opts)
local explorer = core.get_explorer() local explorer = core.get_explorer()
if not explorer then if not explorer then
return return
end end
local node = explorer:get_node_at_cursor() node = node or explorer
if not node then
local node_at_cursor = explorer:get_node_at_cursor()
if not node_at_cursor then
return return
end end
local matches = buf_match() local matches = buf_match()
Iterator.builder(explorer.nodes) Iterator.builder({ node:is(FileNode) and node.parent or node:as(DirectoryNode) })
:hidden() :hidden()
:applier(function(n) :applier(function(n)
local dir = n:as(DirectoryNode) local dir = n:as(DirectoryNode)
if dir then if dir then
dir.open = keep_buffers and matches(dir.absolute_path) dir.open = opts.keep_buffers == true and matches(dir.absolute_path)
end end
end) end)
:recursor(function(n) :recursor(function(n)
@@ -51,7 +56,26 @@ function M.fn(keep_buffers)
:iterate() :iterate()
explorer.renderer:draw() explorer.renderer:draw()
utils.focus_node_or_parent(node) utils.focus_node_or_parent(node_at_cursor)
end
---@param opts ApiCollapseOpts|boolean|nil 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 ApiCollapseOpts?
function M.node(node, opts)
collapse(node, opts or {})
end end
return M return M

View File

@@ -2,6 +2,7 @@ local core = require("nvim-tree.core")
local Iterator = require("nvim-tree.iterators.node-iterator") local Iterator = require("nvim-tree.iterators.node-iterator")
local notify = require("nvim-tree.notify") local notify = require("nvim-tree.notify")
local FileNode = require("nvim-tree.node.file")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
local M = {} local M = {}
@@ -70,23 +71,38 @@ local function gen_iterator()
end end
end end
---Expand the directory node or the root ---@param node Node?
---@param node Node local function expand_node(node)
function M.fn(node) if not node then
local explorer = core.get_explorer()
local parent = node:as(DirectoryNode) or explorer
if not parent then
return return
end end
if gen_iterator()(parent) then if gen_iterator()(node) then
notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders") notify.warn("expansion iteration was halted after " .. M.MAX_FOLDER_DISCOVERY .. " discovered folders")
end end
local explorer = core.get_explorer()
if explorer then if explorer then
explorer.renderer:draw() explorer.renderer:draw()
end end
end end
---Expand the directory node or the root
---@param node Node
function M.all(node)
expand_node(node and node:as(DirectoryNode) or core.get_explorer())
end
---Expand the directory node or parent node
---@param node Node
function M.node(node)
if not node then
return
end
expand_node(node:is(FileNode) and node.parent or node:as(DirectoryNode))
end
function M.setup(opts) function M.setup(opts)
M.MAX_FOLDER_DISCOVERY = opts.actions.expand_all.max_folder_discovery M.MAX_FOLDER_DISCOVERY = opts.actions.expand_all.max_folder_discovery
M.EXCLUDE = to_lookup_table(opts.actions.expand_all.exclude) M.EXCLUDE = to_lookup_table(opts.actions.expand_all.exclude)

View File

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

View File

@@ -11,6 +11,7 @@ local notify = require("nvim-tree.notify")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
local FileLinkNode = require("nvim-tree.node.file-link") local FileLinkNode = require("nvim-tree.node.file-link")
local RootNode = require("nvim-tree.node.root") local RootNode = require("nvim-tree.node.root")
local UserDecorator = require("nvim-tree.renderer.decorator.user")
local Api = { local Api = {
tree = {}, tree = {},
@@ -23,6 +24,7 @@ local Api = {
}, },
run = {}, run = {},
open = {}, open = {},
buffer = {},
}, },
events = {}, events = {},
marks = { marks = {
@@ -39,6 +41,7 @@ local Api = {
}, },
commands = {}, commands = {},
diagnostics = {}, diagnostics = {},
decorator = {},
} }
---Print error when setup not called. ---Print error when setup not called.
@@ -69,7 +72,7 @@ end
---Inject the node as the first argument if present otherwise do nothing. ---Inject the node as the first argument if present otherwise do nothing.
---@param fn fun(node: Node, ...): any ---@param fn fun(node: Node, ...): any
---@return fun(node: Node, ...): any ---@return fun(node: Node?, ...): any
local function wrap_node(fn) local function wrap_node(fn)
return function(node, ...) return function(node, ...)
node = node or wrap_explorer("get_node_at_cursor")() node = node or wrap_explorer("get_node_at_cursor")()
@@ -80,8 +83,8 @@ local function wrap_node(fn)
end end
---Inject the node or nil as the first argument if absent. ---Inject the node or nil as the first argument if absent.
---@param fn fun(node: Node, ...): any ---@param fn fun(node: Node?, ...): any
---@return fun(node: Node, ...): any ---@return fun(node: Node?, ...): any
local function wrap_node_or_nil(fn) local function wrap_node_or_nil(fn)
return function(node, ...) return function(node, ...)
node = node or wrap_explorer("get_node_at_cursor")() node = node or wrap_explorer("get_node_at_cursor")()
@@ -179,8 +182,12 @@ Api.tree.get_nodes = wrap_explorer("get_nodes")
Api.tree.find_file = wrap(actions.tree.find_file.fn) Api.tree.find_file = wrap(actions.tree.find_file.fn)
Api.tree.search_node = wrap(actions.finders.search_node.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) ---@class ApiCollapseOpts
---@field keep_buffers boolean|nil default false
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse.all)
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand.all)
Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle") Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle")
Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored") Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored")
Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean") Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean")
@@ -219,21 +226,46 @@ Api.fs.copy.absolute_path = wrap_node(wrap_explorer_member("clipboard", "copy_ab
Api.fs.copy.filename = wrap_node(wrap_explorer_member("clipboard", "copy_filename")) 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.basename = wrap_node(wrap_explorer_member("clipboard", "copy_basename"))
Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path")) Api.fs.copy.relative_path = wrap_node(wrap_explorer_member("clipboard", "copy_path"))
---
---@class NodeEditOpts
---@field quit_on_open boolean|nil default false
---@field focus boolean|nil default true
---@param mode string ---@param mode string
---@param node Node ---@param node Node
local function edit(mode, node) ---@param edit_opts NodeEditOpts?
local function edit(mode, node, edit_opts)
local file_link = node:as(FileLinkNode) local file_link = node:as(FileLinkNode)
local path = file_link and file_link.link_to or node.absolute_path local path = file_link and file_link.link_to or node.absolute_path
local cur_tabpage = vim.api.nvim_get_current_tabpage()
actions.node.open_file.fn(mode, path) actions.node.open_file.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
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 == true
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 end
---@param mode string ---@param mode string
---@param toggle_group boolean? ---@param toggle_group boolean?
---@return fun(node: Node) ---@return fun(node: Node, edit_opts: NodeEditOpts?)
local function open_or_expand_or_dir_up(mode, toggle_group) local function open_or_expand_or_dir_up(mode, toggle_group)
---@param node Node ---@param node Node
return function(node) ---@param edit_opts NodeEditOpts?
return function(node, edit_opts)
local root = node:as(RootNode) local root = node:as(RootNode)
local dir = node:as(DirectoryNode) local dir = node:as(DirectoryNode)
@@ -242,7 +274,7 @@ local function open_or_expand_or_dir_up(mode, toggle_group)
elseif dir then elseif dir then
dir:expand_or_collapse(toggle_group) dir:expand_or_collapse(toggle_group)
elseif not toggle_group then elseif not toggle_group then
edit(mode, node) edit(mode, node, edit_opts)
end end
end end
end end
@@ -253,7 +285,9 @@ 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.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.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.vertical = wrap_node(open_or_expand_or_dir_up("vsplit"))
Api.node.open.vertical_no_picker = wrap_node(open_or_expand_or_dir_up("vsplit_no_picker"))
Api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up("split")) Api.node.open.horizontal = wrap_node(open_or_expand_or_dir_up("split"))
Api.node.open.horizontal_no_picker = wrap_node(open_or_expand_or_dir_up("split_no_picker"))
Api.node.open.tab = wrap_node(open_or_expand_or_dir_up("tabnew")) 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.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 = wrap_node(open_or_expand_or_dir_up("preview"))
@@ -282,6 +316,19 @@ Api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn({
Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn({ where = "next", what = "opened" })) 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.node.navigate.opened.prev = wrap_node(actions.moves.item.fn({ where = "prev", what = "opened" }))
Api.node.expand = wrap_node(actions.tree.modifiers.expand.node)
Api.node.collapse = wrap_node(actions.tree.modifiers.collapse.node)
---@class ApiNodeDeleteWipeBufferOpts
---@field force boolean|nil default false
Api.node.buffer.delete = wrap_node(function(node, opts)
actions.node.buffer.delete(node, opts)
end)
Api.node.buffer.wipe = wrap_node(function(node, opts)
actions.node.buffer.wipe(node, opts)
end)
Api.git.reload = wrap_explorer("reload_git") Api.git.reload = wrap_explorer("reload_git")
Api.events.subscribe = events.subscribe Api.events.subscribe = events.subscribe
@@ -311,4 +358,9 @@ Api.commands.get = wrap(function()
return require("nvim-tree.commands").get() return require("nvim-tree.commands").get()
end) end)
---Create a decorator class by calling :extend()
---See :help nvim-tree-decorators
---@type nvim_tree.api.decorator.UserDecorator
Api.decorator.UserDecorator = UserDecorator --[[@as nvim_tree.api.decorator.UserDecorator]]
return Api return Api

View File

@@ -5,6 +5,8 @@ local Class = require("nvim-tree.classic")
-- others with name and links less than this arbitrary value are short -- others with name and links less than this arbitrary value are short
local SHORT_LEN = 50 local SHORT_LEN = 50
local namespace_hi_test_id = vim.api.nvim_create_namespace("NvimTreeHiTest")
---@class (exact) HighlightDisplay: Class for :NvimTreeHiTest ---@class (exact) HighlightDisplay: Class for :NvimTreeHiTest
---@field group string nvim-tree highlight group name ---@field group string nvim-tree highlight group name
---@field links string link chain to a concretely defined group ---@field links string link chain to a concretely defined group
@@ -52,7 +54,12 @@ function HighlightDisplay:render(bufnr, fmt, l)
local text = string.format(fmt, self.group, self.links, self.def) 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_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 return l + 1
end end

View File

@@ -17,7 +17,7 @@ local COC_SEVERITY_LEVELS = {
} }
---Absolute Node path to LSP severity level ---Absolute Node path to LSP severity level
---@alias NodeSeverities table<string, lsp.DiagnosticSeverity> ---@alias NodeSeverities table<string, vim.diagnostic.Severity>
---@class DiagStatus ---@class DiagStatus
---@field value lsp.DiagnosticSeverity|nil ---@field value lsp.DiagnosticSeverity|nil
@@ -37,33 +37,6 @@ local function uniformize_path(path)
return utils.canonical_path(path:gsub("\\", "/")) return utils.canonical_path(path:gsub("\\", "/"))
end 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 ---Severity is within diagnostics.severity.min, diagnostics.severity.max
---@param severity lsp.DiagnosticSeverity ---@param severity lsp.DiagnosticSeverity
---@param config table ---@param config table
@@ -135,11 +108,8 @@ local function from_cache(node)
for bufname, severity in pairs(NODE_SEVERITIES) do for bufname, severity in pairs(NODE_SEVERITIES) do
local node_contains_buf = vim.startswith(bufname, nodepath .. "/") local node_contains_buf = vim.startswith(bufname, nodepath .. "/")
if node_contains_buf then if node_contains_buf then
if severity == M.severity.max then if not max_severity or severity < max_severity then
max_severity = severity max_severity = severity
break
else
max_severity = math.min(max_severity or severity, severity)
end end
end end
end end
@@ -147,32 +117,73 @@ local function from_cache(node)
return { value = max_severity, cache_version = NODE_SEVERITIES_VERSION } return { value = max_severity, cache_version = NODE_SEVERITIES_VERSION }
end end
---Fired on DiagnosticChanged and CocDiagnosticChanged events: ---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)
if not M.enable or not ev or not ev.data or not ev.data.diagnostics then
return
end
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 diagnostic.severity >= M.severity.max and diagnostic.severity <= M.severity.min 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
utils.debounce("DiagnosticChanged redraw", M.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 ---debounced retrieval, cache update, version increment and draw
function M.update() function M.update_coc()
if not M.enable then if not M.enable then
return return
end end
utils.debounce("diagnostics", M.debounce_delay, function() utils.debounce("CocDiagnosticChanged update", M.debounce_delay, function()
local profile = log.profile_start("diagnostics update") local profile = log.profile_start("CocDiagnosticChanged update")
if is_using_coc() then NODE_SEVERITIES = from_coc()
NODE_SEVERITIES = from_coc()
else
NODE_SEVERITIES = from_nvim_lsp()
end
NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1 NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1
if log.enabled("diagnostics") then if log.enabled("diagnostics") then
for bufname, severity in pairs(NODE_SEVERITIES) do 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
end end
log.profile_end(profile) log.profile_end(profile)
local bufnr = view.get_bufnr() local bufnr = view.get_bufnr()
local should_draw = bufnr local should_draw = bufnr and vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr)
and vim.api.nvim_buf_is_valid(bufnr)
and vim.api.nvim_buf_is_loaded(bufnr)
and vim.api.nvim_get_option_value("buflisted", { buf = bufnr })
if should_draw then if should_draw then
local explorer = core.get_explorer() local explorer = core.get_explorer()
if explorer then if explorer then

View File

@@ -8,6 +8,7 @@ M.Event = {
Ready = "Ready", Ready = "Ready",
WillRenameNode = "WillRenameNode", WillRenameNode = "WillRenameNode",
NodeRenamed = "NodeRenamed", NodeRenamed = "NodeRenamed",
TreePreOpen = "TreePreOpen",
TreeOpen = "TreeOpen", TreeOpen = "TreeOpen",
TreeClose = "TreeClose", TreeClose = "TreeClose",
WillCreateFile = "WillCreateFile", WillCreateFile = "WillCreateFile",
@@ -91,6 +92,11 @@ function M._dispatch_folder_removed(folder_name)
dispatch(M.Event.FolderRemoved, { folder_name = folder_name }) dispatch(M.Event.FolderRemoved, { folder_name = folder_name })
end end
--@private
function M._dispatch_on_tree_pre_open()
dispatch(M.Event.TreePreOpen, nil)
end
--@private --@private
function M._dispatch_on_tree_open() function M._dispatch_on_tree_open()
dispatch(M.Event.TreeOpen, nil) dispatch(M.Event.TreeOpen, nil)

View File

@@ -101,10 +101,19 @@ function Explorer:create_autocmds()
vim.api.nvim_create_autocmd("BufReadPost", { vim.api.nvim_create_autocmd("BufReadPost", {
group = self.augroup_id, group = self.augroup_id,
callback = function(data) callback = function(data)
if (self.filters.state.no_buffer or self.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then -- only handle normal files
if vim.bo[data.buf].buftype ~= "" then
return
end
if self.filters.state.no_buffer then
-- full reload is required to update the filter state
utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function() utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function()
self:reload_explorer() self:reload_explorer()
end) end)
elseif self.opts.renderer.highlight_opened_files ~= "none" then
-- draw to update opened highlight
self.renderer:draw()
end end
end, end,
}) })
@@ -113,10 +122,21 @@ function Explorer:create_autocmds()
vim.api.nvim_create_autocmd("BufUnload", { vim.api.nvim_create_autocmd("BufUnload", {
group = self.augroup_id, group = self.augroup_id,
callback = function(data) callback = function(data)
if (self.filters.state.no_buffer or self.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then -- only handle normal files
if vim.bo[data.buf].buftype ~= "" then
return
end
if self.filters.state.no_buffer then
-- full reload is required to update the filter state
utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function() utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function()
self:reload_explorer() self:reload_explorer()
end) end)
elseif self.opts.renderer.highlight_opened_files ~= "none" then
-- draw to update opened highlight; must be delayed as the buffer is still loaded during BufUnload
vim.schedule(function()
self.renderer:draw()
end)
end end
end, end,
}) })
@@ -214,8 +234,9 @@ function Explorer:reload(node, project)
end end
local abs = utils.path_join({ cwd, name }) local abs = utils.path_join({ cwd, name })
---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_lstat(abs) -- path incorrectly specified as an integer
local stat = vim.loop.fs_lstat(abs) ---@diagnostic disable-line param-type-mismatch
local filter_reason = self.filters:should_filter_as_reason(abs, stat, filter_status) local filter_reason = self.filters:should_filter_as_reason(abs, stat, filter_status)
if filter_reason == FILTER_REASON.none then if filter_reason == FILTER_REASON.none then
@@ -373,8 +394,9 @@ function Explorer:populate_children(handle, cwd, node, project, parent)
if Watcher.is_fs_event_capable(abs) then if Watcher.is_fs_event_capable(abs) then
local profile = log.profile_start("populate_children %s", abs) local profile = log.profile_start("populate_children %s", abs)
---@type uv.fs_stat.result|nil -- path incorrectly specified as an integer
local stat = vim.loop.fs_lstat(abs) local stat = vim.loop.fs_lstat(abs) ---@diagnostic disable-line param-type-mismatch
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status) local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
local child = node_factory.create({ local child = node_factory.create({
@@ -389,9 +411,9 @@ function Explorer:populate_children(handle, cwd, node, project, parent)
nodes_by_path[child.absolute_path] = true nodes_by_path[child.absolute_path] = true
child:update_git_status(node_ignored, project) child:update_git_status(node_ignored, project)
end end
else elseif node.hidden_stats then
for reason, value in pairs(FILTER_REASON) do for reason, value in pairs(FILTER_REASON) do
if filter_reason == value then if filter_reason == value and type(node.hidden_stats[reason]) == "number" then
node.hidden_stats[reason] = node.hidden_stats[reason] + 1 node.hidden_stats[reason] = node.hidden_stats[reason] + 1
end end
end end
@@ -530,7 +552,7 @@ function Explorer:place_cursor_on_node()
end end
---Api.tree.get_nodes ---Api.tree.get_nodes
---@return Node ---@return nvim_tree.api.Node
function Explorer:get_nodes() function Explorer:get_nodes()
return self:clone() return self:clone()
end end

View File

@@ -11,6 +11,8 @@ local WIN_HL = table.concat({
"CursorLine:NvimTreeCursorLine", "CursorLine:NvimTreeCursorLine",
}, ",") }, ",")
local namespace_help_id = vim.api.nvim_create_namespace("NvimTreeHelp")
local M = { local M = {
config = {}, config = {},
@@ -82,8 +84,8 @@ end
--- Compute all lines for the buffer --- Compute all lines for the buffer
---@param map table keymap.get_keymap ---@param map table keymap.get_keymap
---@return table strings of text ---@return string[] lines of text
---@return table arrays of arguments 3-6 for nvim_buf_add_highlight() ---@return HighlightRangeArgs[] hl_range_args for lines
---@return number maximum length of text ---@return number maximum length of text
local function compute(map) local function compute(map)
local head_lhs = "nvim-tree mappings" local head_lhs = "nvim-tree mappings"
@@ -130,10 +132,10 @@ local function compute(map)
local width = #lines[1] local width = #lines[1]
-- header highlight, assume one character keys -- header highlight, assume one character keys
local hl = { local hl_range_args = {
{ "NvimTreeFolderName", 0, 0, #head_lhs }, { higroup = "NvimTreeFolderName", start = { 0, 0, }, finish = { 0, #head_lhs, }, },
{ "NvimTreeFolderName", 0, width - 1, width }, { higroup = "NvimTreeFolderName", start = { 0, width - 1, }, finish = { 0, width, }, },
{ "NvimTreeFolderName", 1, width - 1, width }, { higroup = "NvimTreeFolderName", start = { 1, width - 1, }, finish = { 1, width, }, },
} }
-- mappings, left padded 1 -- mappings, left padded 1
@@ -145,10 +147,10 @@ local function compute(map)
width = math.max(#line, width) width = math.max(#line, width)
-- highlight lhs -- highlight lhs
table.insert(hl, { "NvimTreeFolderName", i + 1, 1, #l.lhs + 1 }) table.insert(hl_range_args, { higroup = "NvimTreeFolderName", start = { i + 1, 1, }, finish = { i + 1, #l.lhs + 1, }, })
end end
return lines, hl, width return lines, hl_range_args, width
end end
--- close the window and delete the buffer, if they exist --- close the window and delete the buffer, if they exist
@@ -172,7 +174,7 @@ local function open()
local map = keymap.get_keymap() local map = keymap.get_keymap()
-- text and highlight -- text and highlight
local lines, hl, width = compute(map) local lines, hl_range_args, width = compute(map)
-- create the buffer -- create the buffer
M.bufnr = vim.api.nvim_create_buf(false, true) M.bufnr = vim.api.nvim_create_buf(false, true)
@@ -187,8 +189,12 @@ local function open()
end end
-- highlight it -- highlight it
for _, h in ipairs(hl) do for _, args in ipairs(hl_range_args) do
vim.api.nvim_buf_add_highlight(M.bufnr, -1, h[1], h[2], h[3], h[4]) if vim.fn.has("nvim-0.11") == 1 and vim.hl and vim.hl.range then
vim.hl.range(M.bufnr, namespace_help_id, args.higroup, args.start, args.finish, {})
else
vim.api.nvim_buf_add_highlight(M.bufnr, -1, args.higroup, args.start[1], args.start[2], args.finish[2]) ---@diagnostic disable-line: deprecated
end
end end
-- open a very restricted window -- open a very restricted window

View File

@@ -96,7 +96,7 @@ function M.default_on_attach(bufnr)
vim.keymap.set("n", "S", api.tree.search_node, opts("Search")) vim.keymap.set("n", "S", api.tree.search_node, opts("Search"))
vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path")) vim.keymap.set("n", "u", api.fs.rename_full, opts("Rename: Full Path"))
vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden")) vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter: Hidden"))
vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse")) vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse All"))
vim.keymap.set("n", "x", api.fs.cut, opts("Cut")) vim.keymap.set("n", "x", api.fs.cut, opts("Cut"))
vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name")) vim.keymap.set("n", "y", api.fs.copy.filename, opts("Copy Name"))
vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path")) vim.keymap.set("n", "Y", api.fs.copy.relative_path, opts("Copy Relative Path"))

View File

@@ -60,6 +60,13 @@ local function refactored(opts)
end end
end end
utils.move_missing_val(opts, "update_focused_file", "ignore_list", opts, "update_focused_file.update_root", "ignore_list", true) utils.move_missing_val(opts, "update_focused_file", "ignore_list", opts, "update_focused_file.update_root", "ignore_list", true)
-- 2025/04/30
if opts.renderer and opts.renderer.icons and type(opts.renderer.icons.padding) == "string" then
local icons_padding = opts.renderer.icons.padding
opts.renderer.icons.padding = {}
opts.renderer.icons.padding.icon = icons_padding
end
end end
local function deprecated(opts) local function deprecated(opts)

View File

@@ -1,6 +1,5 @@
local view = require("nvim-tree.view") local view = require("nvim-tree.view")
local core = require("nvim-tree.core") local core = require("nvim-tree.core")
local events = require("nvim-tree.events")
local notify = require("nvim-tree.notify") local notify = require("nvim-tree.notify")
---@class LibOpenOpts ---@class LibOpenOpts
@@ -130,7 +129,6 @@ function M.open(opts)
open_view_and_draw() open_view_and_draw()
end end
view.restore_tab_state() view.restore_tab_state()
events._dispatch_on_tree_open()
end end
function M.setup(opts) function M.setup(opts)

View File

@@ -73,9 +73,10 @@ function DirectoryLinkNode:highlighted_name()
end end
---Create a sanitized partial copy of a node, populating children recursively. ---Create a sanitized partial copy of a node, populating children recursively.
---@return DirectoryLinkNode cloned ---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate
function DirectoryLinkNode:clone() ---@return nvim_tree.api.DirectoryLinkNode cloned
local clone = DirectoryNode.clone(self) --[[@as DirectoryLinkNode]] function DirectoryLinkNode:clone(api_nodes)
local clone = DirectoryNode.clone(self, api_nodes) --[[@as nvim_tree.api.DirectoryLinkNode]]
clone.link_to = self.link_to clone.link_to = self.link_to
clone.fs_stat_target = self.fs_stat_target clone.fs_stat_target = self.fs_stat_target

View File

@@ -271,18 +271,20 @@ function DirectoryNode:highlighted_name()
end end
---Create a sanitized partial copy of a node, populating children recursively. ---Create a sanitized partial copy of a node, populating children recursively.
---@return DirectoryNode cloned ---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate
function DirectoryNode:clone() ---@return nvim_tree.api.DirectoryNode cloned
local clone = Node.clone(self) --[[@as DirectoryNode]] function DirectoryNode:clone(api_nodes)
local clone = Node.clone(self, api_nodes) --[[@as nvim_tree.api.DirectoryNode]]
clone.has_children = self.has_children clone.has_children = self.has_children
clone.group_next = nil
clone.nodes = {} clone.nodes = {}
clone.open = self.open clone.open = self.open
clone.hidden_stats = nil
local clone_child
for _, child in ipairs(self.nodes) do for _, child in ipairs(self.nodes) do
table.insert(clone.nodes, child:clone()) clone_child = child:clone(api_nodes)
clone_child.parent = clone
table.insert(clone.nodes, clone_child)
end end
return clone return clone

View File

@@ -58,9 +58,10 @@ function FileLinkNode:highlighted_name()
end end
---Create a sanitized partial copy of a node ---Create a sanitized partial copy of a node
---@return FileLinkNode cloned ---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate
function FileLinkNode:clone() ---@return nvim_tree.api.FileLinkNode cloned
local clone = FileNode.clone(self) --[[@as FileLinkNode]] function FileLinkNode:clone(api_nodes)
local clone = FileNode.clone(self, api_nodes) --[[@as nvim_tree.api.FileLinkNode]]
clone.link_to = self.link_to clone.link_to = self.link_to
clone.fs_stat_target = self.fs_stat_target clone.fs_stat_target = self.fs_stat_target

View File

@@ -94,9 +94,10 @@ function FileNode:highlighted_name()
end end
---Create a sanitized partial copy of a node ---Create a sanitized partial copy of a node
---@return FileNode cloned ---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate
function FileNode:clone() ---@return nvim_tree.api.FileNode cloned
local clone = Node.clone(self) --[[@as FileNode]] function FileNode:clone(api_nodes)
local clone = Node.clone(self, api_nodes) --[[@as nvim_tree.api.FileNode]]
clone.extension = self.extension clone.extension = self.extension

View File

@@ -2,6 +2,7 @@ local Class = require("nvim-tree.classic")
---Abstract Node class. ---Abstract Node class.
---@class (exact) Node: Class ---@class (exact) Node: Class
---@field uid_node number vim.loop.hrtime() at construction time
---@field type "file" | "directory" | "link" uv.fs_stat.result.type ---@field type "file" | "directory" | "link" uv.fs_stat.result.type
---@field explorer Explorer ---@field explorer Explorer
---@field absolute_path string ---@field absolute_path string
@@ -25,6 +26,7 @@ local Node = Class:extend()
---@protected ---@protected
---@param args NodeArgs ---@param args NodeArgs
function Node:new(args) function Node:new(args)
self.uid_node = vim.loop.hrtime()
self.explorer = args.explorer self.explorer = args.explorer
self.absolute_path = args.absolute_path self.absolute_path = args.absolute_path
self.executable = false self.executable = false
@@ -112,21 +114,19 @@ end
---Highlighted name for the node ---Highlighted name for the node
---Empty for base Node ---Empty for base Node
---@return HighlightedString icon ---@return HighlightedString name
function Node:highlighted_name() function Node:highlighted_name()
return self:highlighted_name_empty() return self:highlighted_name_empty()
end end
---Create a sanitized partial copy of a node, populating children recursively. ---Create a sanitized partial copy of a node, populating children recursively.
---@return Node cloned ---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate
function Node:clone() ---@return nvim_tree.api.Node cloned
---@type Explorer function Node:clone(api_nodes)
local explorer_placeholder = nil ---@type nvim_tree.api.Node
---@type Node
local clone = { local clone = {
uid_node = self.uid_node,
type = self.type, type = self.type,
explorer = explorer_placeholder,
absolute_path = self.absolute_path, absolute_path = self.absolute_path,
executable = self.executable, executable = self.executable,
fs_stat = self.fs_stat, fs_stat = self.fs_stat,
@@ -134,10 +134,13 @@ function Node:clone()
hidden = self.hidden, hidden = self.hidden,
name = self.name, name = self.name,
parent = nil, parent = nil,
diag_status = nil, diag_severity = self.diag_status and self.diag_status.value or nil,
is_dot = self.is_dot,
} }
if api_nodes then
api_nodes[self.uid_node] = clone
end
return clone return clone
end end

View File

@@ -2,7 +2,7 @@ local Class = require("nvim-tree.classic")
---@class (exact) LinkNode: Class ---@class (exact) LinkNode: Class
---@field link_to string ---@field link_to string
---@field protected fs_stat_target uv.fs_stat.result ---@field fs_stat_target uv.fs_stat.result
local LinkNode = Class:extend() local LinkNode = Class:extend()
---@class (exact) LinkNodeArgs: NodeArgs ---@class (exact) LinkNodeArgs: NodeArgs

View File

@@ -22,4 +22,13 @@ function RootNode:destroy()
DirectoryNode.destroy(self) DirectoryNode.destroy(self)
end end
---Create a sanitized partial copy of a node, populating children recursively.
---@param api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node to populate
---@return nvim_tree.api.RootNode cloned
function RootNode:clone(api_nodes)
local clone = DirectoryNode.clone(self, api_nodes) --[[@as nvim_tree.api.RootNode]]
return clone
end
return RootNode return RootNode

View File

@@ -3,32 +3,39 @@ local utils = require("nvim-tree.utils")
local view = require("nvim-tree.view") local view = require("nvim-tree.view")
local Class = require("nvim-tree.classic") local Class = require("nvim-tree.classic")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
local DecoratorBookmarks = require("nvim-tree.renderer.decorator.bookmarks") local BookmarkDecorator = require("nvim-tree.renderer.decorator.bookmarks")
local DecoratorCopied = require("nvim-tree.renderer.decorator.copied") local CopiedDecorator = require("nvim-tree.renderer.decorator.copied")
local DecoratorCut = require("nvim-tree.renderer.decorator.cut") local CutDecorator = require("nvim-tree.renderer.decorator.cut")
local DecoratorDiagnostics = require("nvim-tree.renderer.decorator.diagnostics") local DiagnosticsDecorator = require("nvim-tree.renderer.decorator.diagnostics")
local DecoratorGit = require("nvim-tree.renderer.decorator.git") local GitDecorator = require("nvim-tree.renderer.decorator.git")
local DecoratorModified = require("nvim-tree.renderer.decorator.modified") local HiddenDecorator = require("nvim-tree.renderer.decorator.hidden")
local DecoratorHidden = require("nvim-tree.renderer.decorator.hidden") local ModifiedDecorator = require("nvim-tree.renderer.decorator.modified")
local DecoratorOpened = require("nvim-tree.renderer.decorator.opened") local OpenDecorator = require("nvim-tree.renderer.decorator.opened")
local UserDecorator = require("nvim-tree.renderer.decorator.user")
local pad = require("nvim-tree.renderer.components.padding") local pad = require("nvim-tree.renderer.components.padding")
---@class (exact) HighlightedString ---@alias HighlightedString nvim_tree.api.HighlightedString
---@field str string
---@field hl string[]
---@class (exact) AddHighlightArgs -- Builtin Decorators
---@field group string[] ---@type table<nvim_tree.api.decorator.Name, Decorator>
---@field line number local BUILTIN_DECORATORS = {
---@field col_start number Git = GitDecorator,
---@field col_end number Open = OpenDecorator,
Hidden = HiddenDecorator,
Modified = ModifiedDecorator,
Bookmark = BookmarkDecorator,
Diagnostics = DiagnosticsDecorator,
Copied = CopiedDecorator,
Cut = CutDecorator,
}
---@class (exact) Builder ---@class (exact) Builder
---@field lines string[] includes icons etc. ---@field lines string[] includes icons etc.
---@field hl_args AddHighlightArgs[] line highlights ---@field hl_range_args HighlightRangeArgs[] highlights for lines
---@field signs string[] line signs ---@field signs string[] line signs
---@field extmarks table[] extra marks for right icon placement ---@field extmarks table[] extra marks for right icon placement
---@field virtual_lines table[] virtual lines for hidden count display ---@field virtual_lines table[] virtual lines for hidden count display
@@ -39,6 +46,7 @@ local pad = require("nvim-tree.renderer.components.padding")
---@field private markers boolean[] indent markers ---@field private markers boolean[] indent markers
---@field private decorators Decorator[] ---@field private decorators Decorator[]
---@field private hidden_display fun(node: Node): string|nil ---@field private hidden_display fun(node: Node): string|nil
---@field private api_nodes table<number, nvim_tree.api.Node>? optional map of uids to api node for user decorators
local Builder = Class:extend() local Builder = Class:extend()
---@class Builder ---@class Builder
@@ -53,25 +61,37 @@ function Builder:new(args)
self.explorer = args.explorer self.explorer = args.explorer
self.index = 0 self.index = 0
self.depth = 0 self.depth = 0
self.hl_args = {} self.hl_range_args = {}
self.combined_groups = {} self.combined_groups = {}
self.lines = {} self.lines = {}
self.markers = {} self.markers = {}
self.signs = {} self.signs = {}
self.extmarks = {} self.extmarks = {}
self.virtual_lines = {} self.virtual_lines = {}
self.decorators = { self.decorators = {}
-- priority order
DecoratorCut({ explorer = args.explorer }),
DecoratorCopied({ explorer = args.explorer }),
DecoratorDiagnostics({ explorer = args.explorer }),
DecoratorBookmarks({ explorer = args.explorer }),
DecoratorModified({ explorer = args.explorer }),
DecoratorHidden({ explorer = args.explorer }),
DecoratorOpened({ explorer = args.explorer }),
DecoratorGit({ explorer = args.explorer })
}
self.hidden_display = Builder:setup_hidden_display_function(self.explorer.opts) self.hidden_display = Builder:setup_hidden_display_function(self.explorer.opts)
-- instantiate all the builtin and user decorator instances
local builtin, user
for _, d in ipairs(self.explorer.opts.renderer.decorators) do
---@type Decorator
builtin = BUILTIN_DECORATORS[d]
---@type UserDecorator
user = type(d) == "table" and type(d.as) == "function" and d:as(UserDecorator)
if builtin then
table.insert(self.decorators, builtin({ explorer = self.explorer }))
elseif user then
table.insert(self.decorators, user())
-- clone user nodes once
if not self.api_nodes then
self.api_nodes = {}
self.explorer:clone(self.api_nodes)
end
end
end
end end
---Insert ranged highlight groups into self.highlights ---Insert ranged highlight groups into self.highlights
@@ -80,7 +100,9 @@ end
---@param start number ---@param start number
---@param end_ number|nil ---@param end_ number|nil
function Builder:insert_highlight(groups, start, end_) function Builder:insert_highlight(groups, start, end_)
table.insert(self.hl_args, { groups, self.index, start, end_ or -1 }) for _, higroup in ipairs(groups) do
table.insert(self.hl_range_args, { higroup = higroup, start = { self.index, start, }, finish = { self.index, end_ or -1, } })
end
end end
---@private ---@private
@@ -113,12 +135,12 @@ end
function Builder:format_line(indent_markers, arrows, icon, name, node) function Builder:format_line(indent_markers, arrows, icon, name, node)
local added_len = 0 local added_len = 0
local function add_to_end(t1, t2) local function add_to_end(t1, t2)
if not t2 then if not t2 or vim.tbl_isempty(t2) then
return return
end end
for _, v in ipairs(t2) do for _, v in ipairs(t2) do
if added_len > 0 then if added_len > 0 then
table.insert(t1, { str = self.explorer.opts.renderer.icons.padding }) table.insert(t1, { str = self.explorer.opts.renderer.icons.padding.icon })
end end
table.insert(t1, v) table.insert(t1, v)
end end
@@ -131,22 +153,25 @@ function Builder:format_line(indent_markers, arrows, icon, name, node)
end end
end end
-- use the api node for user decorators
local api_node = self.api_nodes and self.api_nodes[node.uid_node] --[[@as Node]]
local line = { indent_markers, arrows } local line = { indent_markers, arrows }
add_to_end(line, { icon }) add_to_end(line, { icon })
for i = #self.decorators, 1, -1 do for _, d in ipairs(self.decorators) do
add_to_end(line, self.decorators[i]:icons_before(node)) add_to_end(line, d:icons_before(not d:is(UserDecorator) and node or api_node))
end end
add_to_end(line, { name }) add_to_end(line, { name })
for i = #self.decorators, 1, -1 do for _, d in ipairs(self.decorators) do
add_to_end(line, self.decorators[i]:icons_after(node)) add_to_end(line, d:icons_after(not d:is(UserDecorator) and node or api_node))
end end
local rights = {} local rights = {}
for i = #self.decorators, 1, -1 do for _, d in ipairs(self.decorators) do
add_to_end(rights, self.decorators[i]:icons_right_align(node)) add_to_end(rights, d:icons_right_align(not d:is(UserDecorator) and node or api_node))
end end
if #rights > 0 then if #rights > 0 then
self.extmarks[self.index] = rights self.extmarks[self.index] = rights
@@ -158,10 +183,14 @@ end
---@private ---@private
---@param node Node ---@param node Node
function Builder:build_signs(node) function Builder:build_signs(node)
-- use the api node for user decorators
local api_node = self.api_nodes and self.api_nodes[node.uid_node] --[[@as Node]]
-- first in priority order -- first in priority order
local sign_name local d, sign_name
for _, d in ipairs(self.decorators) do for i = #self.decorators, 1, -1 do
sign_name = d:sign_name(node) d = self.decorators[i]
sign_name = d:sign_name(not d:is(UserDecorator) and node or api_node)
if sign_name then if sign_name then
self.signs[self.index] = sign_name self.signs[self.index] = sign_name
break break
@@ -197,43 +226,50 @@ function Builder:create_combined_group(groups)
return combined_name return combined_name
end end
---Calculate highlight group for icon and name. A combined highlight group will be created ---Calculate decorated icon and name for a node.
---when there is more than one highlight. ---A combined highlight group will be created when there is more than one highlight.
---A highlight group is always calculated and upserted for the case of highlights changing. ---A highlight group is always calculated and upserted for the case of highlights changing.
---@private ---@private
---@param node Node ---@param node Node
---@return string|nil icon_hl_group ---@return HighlightedString icon
---@return string|nil name_hl_group ---@return HighlightedString name
function Builder:add_highlights(node) function Builder:icon_name_decorated(node)
-- result -- use the api node for user decorators
local icon_hl_group, name_hl_group local api_node = self.api_nodes and self.api_nodes[node.uid_node] --[[@as Node]]
-- calculate all groups -- base case
local icon = node:highlighted_icon()
local name = node:highlighted_name()
-- calculate node icon and all decorated highlight groups
local icon_groups = {} local icon_groups = {}
local name_groups = {} local name_groups = {}
local d, icon, name local hl_icon, hl_name
for i = #self.decorators, 1, -1 do for _, d in ipairs(self.decorators) do
d = self.decorators[i] -- maybe overridde icon
icon, name = d:groups_icon_name(node) icon = d:icon_node((not d:is(UserDecorator) and node or api_node)) or icon
table.insert(icon_groups, icon)
table.insert(name_groups, name) hl_icon, hl_name = d:highlight_group_icon_name((not d:is(UserDecorator) and node or api_node))
table.insert(icon_groups, hl_icon)
table.insert(name_groups, hl_name)
end end
-- one or many icon groups -- add one or many icon groups
if #icon_groups > 1 then if #icon_groups > 1 then
icon_hl_group = self:create_combined_group(icon_groups) table.insert(icon.hl, self:create_combined_group(icon_groups))
else else
icon_hl_group = icon_groups[1] table.insert(icon.hl, icon_groups[1])
end end
-- one or many name groups -- add one or many name groups
if #name_groups > 1 then if #name_groups > 1 then
name_hl_group = self:create_combined_group(name_groups) table.insert(name.hl, self:create_combined_group(name_groups))
else else
name_hl_group = name_groups[1] table.insert(name.hl, name_groups[1])
end end
return icon_hl_group, name_hl_group return icon, name
end end
---Insert node line into self.lines, calling Builder:build_lines for each directory ---Insert node line into self.lines, calling Builder:build_lines for each directory
@@ -246,13 +282,8 @@ function Builder:build_line(node, idx, num_children)
local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers) local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers)
local arrows = pad.get_arrows(node) local arrows = pad.get_arrows(node)
-- main components -- decorated node icon and name
local icon, name = node:highlighted_icon(), node:highlighted_name() local icon, name = self:icon_name_decorated(node)
-- highighting
local icon_hl_group, name_hl_group = self:add_highlights(node)
table.insert(icon.hl, icon_hl_group)
table.insert(name.hl, name_hl_group)
local line = self:format_line(indent_markers, arrows, icon, name, node) local line = self:format_line(indent_markers, arrows, icon, name, node)
table.insert(self.lines, self:unwrap_highlighted_strings(line)) table.insert(self.lines, self:unwrap_highlighted_strings(line))

View File

@@ -1,6 +1,7 @@
local M = {} local M = {}
local utils = require("nvim-tree.utils") local utils = require("nvim-tree.utils")
local view = require("nvim-tree.view")
local function hide(win) local function hide(win)
if win then if win then
@@ -32,7 +33,7 @@ local function effective_win_width()
return win_width - win_info[1].textoff return win_width - win_info[1].textoff
end end
local function show() local function show(opts)
local line_nr = vim.api.nvim_win_get_cursor(0)[1] local line_nr = vim.api.nvim_win_get_cursor(0)[1]
if vim.wo.wrap then if vim.wo.wrap then
return return
@@ -52,6 +53,11 @@ local function show()
local text_width = vim.fn.strdisplaywidth(vim.fn.substitute(line, "[^[:print:]]*$", "", "g")) local text_width = vim.fn.strdisplaywidth(vim.fn.substitute(line, "[^[:print:]]*$", "", "g"))
local win_width = effective_win_width() local win_width = effective_win_width()
-- windows width reduced by right aligned icons
local icon_ns_id = vim.api.nvim_get_namespaces()["NvimTreeExtmarks"]
local icon_extmarks = vim.api.nvim_buf_get_extmarks(0, icon_ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true })
win_width = win_width - utils.extmarks_length(icon_extmarks)
if text_width < win_width then if text_width < win_width then
return return
end end
@@ -64,7 +70,9 @@ local function show()
height = 1, height = 1,
noautocmd = true, noautocmd = true,
style = "minimal", style = "minimal",
border = "none"
}) })
vim.wo[M.popup_win].winhl = view.View.winopts.winhl
local ns_id = vim.api.nvim_get_namespaces()["NvimTreeHighlights"] local ns_id = vim.api.nvim_get_namespaces()["NvimTreeHighlights"]
local extmarks = vim.api.nvim_buf_get_extmarks(0, ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true }) local extmarks = vim.api.nvim_buf_get_extmarks(0, ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = true })
@@ -79,9 +87,18 @@ local function show()
---@type vim.api.keyset.extmark_details ---@type vim.api.keyset.extmark_details
local details = extmark[4] local details = extmark[4]
vim.api.nvim_buf_add_highlight(0, ns_id, details.hl_group, 0, col, details.end_col) if type(details) == "table" then
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
end
end
vim.cmd([[ setlocal nowrap noswapfile nobuflisted buftype=nofile bufhidden=wipe ]])
if opts.view.cursorline then
vim.cmd([[ setlocal cursorline cursorlineopt=both ]])
end end
vim.cmd([[ setlocal nowrap cursorline noswapfile nobuflisted buftype=nofile bufhidden=hide ]])
end) end)
end end
@@ -107,7 +124,7 @@ M.setup = function(opts)
pattern = { "NvimTree_*" }, pattern = { "NvimTree_*" },
callback = function() callback = function()
if utils.is_nvim_tree_buf(0) then if utils.is_nvim_tree_buf(0) then
show() show(opts)
end end
end, end,
}) })

View File

@@ -95,15 +95,15 @@ function M.get_arrows(node)
local dir = node:as(DirectoryNode) local dir = node:as(DirectoryNode)
if dir then if dir then
if dir.open then if dir.open then
str = M.config.icons.glyphs.folder["arrow_open"] .. " " str = M.config.icons.glyphs.folder["arrow_open"] .. M.config.icons.padding.folder_arrow
hl = "NvimTreeFolderArrowOpen" hl = "NvimTreeFolderArrowOpen"
else else
str = M.config.icons.glyphs.folder["arrow_closed"] .. " " str = M.config.icons.glyphs.folder["arrow_closed"] .. M.config.icons.padding.folder_arrow
end end
elseif M.config.indent_markers.enable then elseif M.config.indent_markers.enable then
str = "" str = ""
else else
str = " " str = " " .. string.rep(" ", #M.config.icons.padding.folder_arrow)
end end
return { str = str, hl = { hl } } return { str = str, hl = { hl } }

View File

@@ -1,21 +1,21 @@
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
---@class (exact) DecoratorBookmarks: Decorator ---@class (exact) BookmarkDecorator: Decorator
---@field icon HighlightedString? ---@field private explorer Explorer
local DecoratorBookmarks = Decorator:extend() ---@field private icon HighlightedString?
local BookmarkDecorator = Decorator:extend()
---@class DecoratorBookmarks ---@class BookmarkDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorBookmarks ---@overload fun(args: DecoratorArgs): BookmarkDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorBookmarks:new(args) function BookmarkDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true, self.enabled = true
hl_pos = args.explorer.opts.renderer.highlight_bookmarks or "none", self.highlight_range = self.explorer.opts.renderer.highlight_bookmarks or "none"
icon_placement = args.explorer.opts.renderer.icons.bookmarks_placement or "none", self.icon_placement = self.explorer.opts.renderer.icons.bookmarks_placement or "none"
})
if self.explorer.opts.renderer.icons.show.bookmarks then if self.explorer.opts.renderer.icons.show.bookmarks then
self.icon = { self.icon = {
@@ -28,8 +28,8 @@ end
---Bookmark icon: renderer.icons.show.bookmarks and node is marked ---Bookmark icon: renderer.icons.show.bookmarks and node is marked
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function DecoratorBookmarks:calculate_icons(node) function BookmarkDecorator:icons(node)
if self.explorer.marks:get(node) then if self.explorer.marks:get(node) then
return { self.icon } return { self.icon }
end end
@@ -37,11 +37,11 @@ end
---Bookmark highlight: renderer.highlight_bookmarks and node is marked ---Bookmark highlight: renderer.highlight_bookmarks and node is marked
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorBookmarks:calculate_highlight(node) function BookmarkDecorator:highlight_group(node)
if self.range ~= "none" and self.explorer.marks:get(node) then if self.highlight_range ~= "none" and self.explorer.marks:get(node) then
return "NvimTreeBookmarkHL" return "NvimTreeBookmarkHL"
end end
end end
return DecoratorBookmarks return BookmarkDecorator

View File

@@ -1,29 +1,29 @@
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
---@class (exact) DecoratorCopied: Decorator ---@class (exact) CopiedDecorator: Decorator
local DecoratorCopied = Decorator:extend() ---@field private explorer Explorer
local CopiedDecorator = Decorator:extend()
---@class DecoratorCopied ---@class CopiedDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorCopied ---@overload fun(args: DecoratorArgs): CopiedDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorCopied:new(args) function CopiedDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true, self.enabled = true
hl_pos = args.explorer.opts.renderer.highlight_clipboard or "none", self.highlight_range = self.explorer.opts.renderer.highlight_clipboard or "none"
icon_placement = "none", self.icon_placement = "none"
})
end end
---Copied highlight: renderer.highlight_clipboard and node is copied ---Copied highlight: renderer.highlight_clipboard and node is copied
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorCopied:calculate_highlight(node) function CopiedDecorator:highlight_group(node)
if self.range ~= "none" and self.explorer.clipboard:is_copied(node) then if self.highlight_range ~= "none" and self.explorer.clipboard:is_copied(node) then
return "NvimTreeCopiedHL" return "NvimTreeCopiedHL"
end end
end end
return DecoratorCopied return CopiedDecorator

View File

@@ -1,29 +1,29 @@
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
---@class (exact) DecoratorCut: Decorator ---@class (exact) CutDecorator: Decorator
local DecoratorCut = Decorator:extend() ---@field private explorer Explorer
local CutDecorator = Decorator:extend()
---@class DecoratorCut ---@class CutDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorCut ---@overload fun(args: DecoratorArgs): CutDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorCut:new(args) function CutDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true, self.enabled = true
hl_pos = args.explorer.opts.renderer.highlight_clipboard or "none", self.highlight_range = self.explorer.opts.renderer.highlight_clipboard or "none"
icon_placement = "none", self.icon_placement = "none"
})
end end
---Cut highlight: renderer.highlight_clipboard and node is cut ---Cut highlight: renderer.highlight_clipboard and node is cut
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorCut:calculate_highlight(node) function CutDecorator:highlight_group(node)
if self.range ~= "none" and self.explorer.clipboard:is_cut(node) then if self.highlight_range ~= "none" and self.explorer.clipboard:is_cut(node) then
return "NvimTreeCutHL" return "NvimTreeCutHL"
end end
end end
return DecoratorCut return CutDecorator

View File

@@ -30,58 +30,54 @@ local ICON_KEYS = {
["hint"] = vim.diagnostic.severity.HINT, ["hint"] = vim.diagnostic.severity.HINT,
} }
---@class (exact) DecoratorDiagnostics: Decorator ---@class (exact) DiagnosticsDecorator: Decorator
---@field icons HighlightedString[]? ---@field private explorer Explorer
local DecoratorDiagnostics = Decorator:extend() ---@field private diag_icons HighlightedString[]?
local DiagnosticsDecorator = Decorator:extend()
---@class DecoratorDiagnostics ---@class DiagnosticsDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorDiagnostics ---@overload fun(args: DecoratorArgs): DiagnosticsDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorDiagnostics:new(args) function DiagnosticsDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true,
hl_pos = args.explorer.opts.renderer.highlight_diagnostics or "none",
icon_placement = args.explorer.opts.renderer.icons.diagnostics_placement or "none",
})
if not self.enabled then self.enabled = true
return self.highlight_range = self.explorer.opts.renderer.highlight_diagnostics or "none"
end self.icon_placement = self.explorer.opts.renderer.icons.diagnostics_placement or "none"
if self.explorer.opts.renderer.icons.show.diagnostics then if self.explorer.opts.renderer.icons.show.diagnostics then
self.icons = {} self.diag_icons = {}
for name, sev in pairs(ICON_KEYS) do for name, sev in pairs(ICON_KEYS) do
self.icons[sev] = { self.diag_icons[sev] = {
str = self.explorer.opts.diagnostics.icons[name], str = self.explorer.opts.diagnostics.icons[name],
hl = { HG_ICON[sev] }, hl = { HG_ICON[sev] },
} }
self:define_sign(self.icons[sev]) self:define_sign(self.diag_icons[sev])
end end
end end
end end
---Diagnostic icon: diagnostics.enable, renderer.icons.show.diagnostics and node has status ---Diagnostic icon: diagnostics.enable, renderer.icons.show.diagnostics and node has status
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function DecoratorDiagnostics:calculate_icons(node) function DiagnosticsDecorator:icons(node)
if node and self.enabled and self.icons then if node and self.diag_icons then
local diag_status = diagnostics.get_diag_status(node) local diag_status = diagnostics.get_diag_status(node)
local diag_value = diag_status and diag_status.value local diag_value = diag_status and diag_status.value
if diag_value then if diag_value then
return { self.icons[diag_value] } return { self.diag_icons[diag_value] }
end end
end end
end end
---Diagnostic highlight: diagnostics.enable, renderer.highlight_diagnostics and node has status ---Diagnostic highlight: diagnostics.enable, renderer.highlight_diagnostics and node has status
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorDiagnostics:calculate_highlight(node) function DiagnosticsDecorator:highlight_group(node)
if not node or not self.enabled or self.range == "none" then if self.highlight_range == "none" then
return nil return nil
end end
@@ -106,4 +102,4 @@ function DecoratorDiagnostics:calculate_highlight(node)
end end
end end
return DecoratorDiagnostics return DiagnosticsDecorator

View File

@@ -3,7 +3,7 @@ local notify = require("nvim-tree.notify")
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
---@class (exact) GitHighlightedString: HighlightedString ---@class (exact) GitHighlightedString: nvim_tree.api.HighlightedString
---@field ord number decreasing priority ---@field ord number decreasing priority
---@alias GitStatusStrings "deleted" | "ignored" | "renamed" | "staged" | "unmerged" | "unstaged" | "untracked" ---@alias GitStatusStrings "deleted" | "ignored" | "renamed" | "staged" | "unmerged" | "unstaged" | "untracked"
@@ -12,31 +12,31 @@ local DirectoryNode = require("nvim-tree.node.directory")
---@alias GitIconsByXY table<GitXY, GitHighlightedString[]> porcelain status ---@alias GitIconsByXY table<GitXY, GitHighlightedString[]> porcelain status
---@alias GitGlyphsByStatus table<GitStatusStrings, string> from opts ---@alias GitGlyphsByStatus table<GitStatusStrings, string> from opts
---@class (exact) DecoratorGit: Decorator ---@class (exact) GitDecorator: Decorator
---@field file_hl_by_xy table<GitXY, string>? ---@field private explorer Explorer
---@field folder_hl_by_xy table<GitXY, string>? ---@field private file_hl_by_xy table<GitXY, string>?
---@field icons_by_status GitIconsByStatus? ---@field private folder_hl_by_xy table<GitXY, string>?
---@field icons_by_xy GitIconsByXY? ---@field private icons_by_status GitIconsByStatus?
local DecoratorGit = Decorator:extend() ---@field private icons_by_xy GitIconsByXY?
local GitDecorator = Decorator:extend()
---@class DecoratorGit ---@class GitDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorGit ---@overload fun(args: DecoratorArgs): GitDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorGit:new(args) function GitDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = args.explorer.opts.git.enable, self.enabled = self.explorer.opts.git.enable
hl_pos = args.explorer.opts.renderer.highlight_git or "none", self.highlight_range = self.explorer.opts.renderer.highlight_git or "none"
icon_placement = args.explorer.opts.renderer.icons.git_placement or "none", self.icon_placement = self.explorer.opts.renderer.icons.git_placement or "none"
})
if not self.enabled then if not self.enabled then
return return
end end
if self.range ~= "none" then if self.highlight_range ~= "none" then
self:build_file_folder_hl_by_xy() self:build_file_folder_hl_by_xy()
end end
@@ -51,7 +51,7 @@ function DecoratorGit:new(args)
end end
---@param glyphs GitGlyphsByStatus ---@param glyphs GitGlyphsByStatus
function DecoratorGit:build_icons_by_status(glyphs) function GitDecorator:build_icons_by_status(glyphs)
self.icons_by_status = {} self.icons_by_status = {}
self.icons_by_status.staged = { str = glyphs.staged, hl = { "NvimTreeGitStagedIcon" }, ord = 1 } self.icons_by_status.staged = { str = glyphs.staged, hl = { "NvimTreeGitStagedIcon" }, ord = 1 }
self.icons_by_status.unstaged = { str = glyphs.unstaged, hl = { "NvimTreeGitDirtyIcon" }, ord = 2 } self.icons_by_status.unstaged = { str = glyphs.unstaged, hl = { "NvimTreeGitDirtyIcon" }, ord = 2 }
@@ -62,8 +62,8 @@ function DecoratorGit:build_icons_by_status(glyphs)
self.icons_by_status.ignored = { str = glyphs.ignored, hl = { "NvimTreeGitIgnoredIcon" }, ord = 7 } self.icons_by_status.ignored = { str = glyphs.ignored, hl = { "NvimTreeGitIgnoredIcon" }, ord = 7 }
end end
---@param icons GitIconsByXY ---@param icons GitIconsByStatus
function DecoratorGit:build_icons_by_xy(icons) function GitDecorator:build_icons_by_xy(icons)
self.icons_by_xy = { self.icons_by_xy = {
["M "] = { icons.staged }, ["M "] = { icons.staged },
[" M"] = { icons.unstaged }, [" M"] = { icons.unstaged },
@@ -100,7 +100,7 @@ function DecoratorGit:build_icons_by_xy(icons)
} }
end end
function DecoratorGit:build_file_folder_hl_by_xy() function GitDecorator:build_file_folder_hl_by_xy()
self.file_hl_by_xy = { self.file_hl_by_xy = {
["M "] = "NvimTreeGitFileStagedHL", ["M "] = "NvimTreeGitFileStagedHL",
["C "] = "NvimTreeGitFileStagedHL", ["C "] = "NvimTreeGitFileStagedHL",
@@ -142,9 +142,9 @@ end
---Git icons: git.enable, renderer.icons.show.git and node has status ---Git icons: git.enable, renderer.icons.show.git and node has status
---@param node Node ---@param node Node
---@return HighlightedString[]|nil modified icon ---@return HighlightedString[]? icons
function DecoratorGit:calculate_icons(node) function GitDecorator:icons(node)
if not node or not self.enabled or not self.icons_by_xy then if not self.icons_by_xy then
return nil return nil
end end
@@ -159,7 +159,7 @@ function DecoratorGit:calculate_icons(node)
for _, s in pairs(git_xy) do for _, s in pairs(git_xy) do
local icons = self.icons_by_xy[s] local icons = self.icons_by_xy[s]
if not icons then if not icons then
if self.range == "none" then if self.highlight_range == "none" then
notify.warn(string.format("Unrecognized git state '%s'", git_xy)) notify.warn(string.format("Unrecognized git state '%s'", git_xy))
end end
return nil return nil
@@ -190,12 +190,12 @@ end
---Get the first icon as the sign if appropriate ---Get the first icon as the sign if appropriate
---@param node Node ---@param node Node
---@return string|nil name ---@return string|nil name
function DecoratorGit:sign_name(node) function GitDecorator:sign_name(node)
if self.icon_placement ~= "signcolumn" then if self.icon_placement ~= "signcolumn" then
return return
end end
local icons = self:calculate_icons(node) local icons = self:icons(node)
if icons and #icons > 0 then if icons and #icons > 0 then
return icons[1].hl[1] return icons[1].hl[1]
end end
@@ -203,9 +203,9 @@ end
---Git highlight: git.enable, renderer.highlight_git and node has status ---Git highlight: git.enable, renderer.highlight_git and node has status
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorGit:calculate_highlight(node) function GitDecorator:highlight_group(node)
if not node or not self.enabled or self.range == "none" then if self.highlight_range == "none" then
return nil return nil
end end
@@ -221,4 +221,4 @@ function DecoratorGit:calculate_highlight(node)
end end
end end
return DecoratorGit return GitDecorator

View File

@@ -1,22 +1,22 @@
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
---@class (exact) DecoratorHidden: Decorator ---@class (exact) HiddenDecorator: Decorator
---@field icon HighlightedString? ---@field private explorer Explorer
local DecoratorHidden = Decorator:extend() ---@field private icon HighlightedString?
local HiddenDecorator = Decorator:extend()
---@class DecoratorHidden ---@class HiddenDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorHidden ---@overload fun(args: DecoratorArgs): HiddenDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorHidden:new(args) function HiddenDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true, self.enabled = true
hl_pos = args.explorer.opts.renderer.highlight_hidden or "none", self.highlight_range = self.explorer.opts.renderer.highlight_hidden or "none"
icon_placement = args.explorer.opts.renderer.icons.hidden_placement or "none", self.icon_placement = self.explorer.opts.renderer.icons.hidden_placement or "none"
})
if self.explorer.opts.renderer.icons.show.hidden then if self.explorer.opts.renderer.icons.show.hidden then
self.icon = { self.icon = {
@@ -29,18 +29,18 @@ end
---Hidden icon: renderer.icons.show.hidden and node starts with `.` (dotfile). ---Hidden icon: renderer.icons.show.hidden and node starts with `.` (dotfile).
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function DecoratorHidden:calculate_icons(node) function HiddenDecorator:icons(node)
if self.enabled and node:is_dotfile() then if node:is_dotfile() then
return { self.icon } return { self.icon }
end end
end end
---Hidden highlight: renderer.highlight_hidden and node starts with `.` (dotfile). ---Hidden highlight: renderer.highlight_hidden and node starts with `.` (dotfile).
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorHidden:calculate_highlight(node) function HiddenDecorator:highlight_group(node)
if not self.enabled or self.range == "none" or not node:is_dotfile() then if self.highlight_range == "none" or not node:is_dotfile() then
return nil return nil
end end
@@ -51,4 +51,4 @@ function DecoratorHidden:calculate_highlight(node)
end end
end end
return DecoratorHidden return HiddenDecorator

View File

@@ -1,48 +1,52 @@
local Class = require("nvim-tree.classic") local Class = require("nvim-tree.classic")
---@alias DecoratorRange "none" | "icon" | "name" | "all"
---@alias DecoratorIconPlacement "none" | "before" | "after" | "signcolumn" | "right_align"
---Abstract Decorator ---Abstract Decorator
---Uses the factory pattern to instantiate child instances.
---@class (exact) Decorator: Class ---@class (exact) Decorator: Class
---@field protected explorer Explorer
---@field protected enabled boolean ---@field protected enabled boolean
---@field protected range DecoratorRange ---@field protected highlight_range nvim_tree.api.decorator.HighlightRange
---@field protected icon_placement DecoratorIconPlacement ---@field protected icon_placement nvim_tree.api.decorator.IconPlacement
local Decorator = Class:extend() local Decorator = Class:extend()
---@class (exact) DecoratorArgs ---@class (exact) DecoratorArgs
---@field explorer Explorer ---@field explorer Explorer
---@class (exact) AbstractDecoratorArgs: DecoratorArgs ---Abstract icon override, optionally implemented
---@field enabled boolean ---@param node Node
---@field hl_pos DecoratorRange ---@return HighlightedString? icon_node
---@field icon_placement DecoratorIconPlacement function Decorator:icon_node(node)
return self:nop(node)
---@protected
---@param args AbstractDecoratorArgs
function Decorator:new(args)
self.explorer = args.explorer
self.enabled = args.enabled
self.range = args.hl_pos
self.icon_placement = args.icon_placement
end end
---Maybe highlight groups ---Abstract icons, optionally implemented
---@protected
---@param node Node ---@param node Node
---@return string|nil icon highlight group ---@return HighlightedString[]? icons
---@return string|nil name highlight group function Decorator:icons(node)
function Decorator:groups_icon_name(node) self:nop(node)
end
---Abstract highlight group, optionally implemented
---@protected
---@param node Node
---@return string? highlight_group
function Decorator:highlight_group(node)
self:nop(node)
end
---Maybe highlight groups for icon and name
---@param node Node
---@return string? icon highlight group
---@return string? name highlight group
function Decorator:highlight_group_icon_name(node)
local icon_hl, name_hl local icon_hl, name_hl
if self.enabled and self.range ~= "none" then if self.enabled and self.highlight_range ~= "none" then
local hl = self:calculate_highlight(node) local hl = self:highlight_group(node)
if self.range == "all" or self.range == "icon" then if self.highlight_range == "all" or self.highlight_range == "icon" then
icon_hl = hl icon_hl = hl
end end
if self.range == "all" or self.range == "name" then if self.highlight_range == "all" or self.highlight_range == "name" then
name_hl = hl name_hl = hl
end end
end end
@@ -52,13 +56,13 @@ end
---Maybe icon sign ---Maybe icon sign
---@param node Node ---@param node Node
---@return string|nil name ---@return string? name
function Decorator:sign_name(node) function Decorator:sign_name(node)
if not self.enabled or self.icon_placement ~= "signcolumn" then if not self.enabled or self.icon_placement ~= "signcolumn" then
return return
end end
local icons = self:calculate_icons(node) local icons = self:icons(node)
if icons and #icons > 0 then if icons and #icons > 0 then
return icons[1].hl[1] return icons[1].hl[1]
end end
@@ -66,56 +70,40 @@ end
---Icons when "before" ---Icons when "before"
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function Decorator:icons_before(node) function Decorator:icons_before(node)
if not self.enabled or self.icon_placement ~= "before" then if not self.enabled or self.icon_placement ~= "before" then
return return
end end
return self:calculate_icons(node) return self:icons(node)
end end
---Icons when "after" ---Icons when "after"
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function Decorator:icons_after(node) function Decorator:icons_after(node)
if not self.enabled or self.icon_placement ~= "after" then if not self.enabled or self.icon_placement ~= "after" then
return return
end end
return self:calculate_icons(node) return self:icons(node)
end end
---Icons when "right_align" ---Icons when "right_align"
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function Decorator:icons_right_align(node) function Decorator:icons_right_align(node)
if not self.enabled or self.icon_placement ~= "right_align" then if not self.enabled or self.icon_placement ~= "right_align" then
return return
end end
return self:calculate_icons(node) return self:icons(node)
end
---Maybe icons, optionally implemented
---@protected
---@param _ Node
---@return HighlightedString[]|nil icons
function Decorator:calculate_icons(_)
return nil
end
---Maybe highlight group, optionally implemented
---@protected
---@param _ Node
---@return string|nil group
function Decorator:calculate_highlight(_)
return nil
end end
---Define a sign ---Define a sign
---@protected ---@protected
---@param icon HighlightedString|nil ---@param icon HighlightedString?
function Decorator:define_sign(icon) function Decorator:define_sign(icon)
if icon and #icon.hl > 0 then if icon and #icon.hl > 0 then
local name = icon.hl[1] local name = icon.hl[1]
@@ -124,9 +112,8 @@ function Decorator:define_sign(icon)
vim.fn.sign_undefine(name) vim.fn.sign_undefine(name)
end end
-- don't use sign if not defined -- don't render sign if empty
if #icon.str < 1 then if #icon.str < 1 then
self.icon_placement = "none"
return return
end end

View File

@@ -3,26 +3,22 @@ local buffers = require("nvim-tree.buffers")
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
local DirectoryNode = require("nvim-tree.node.directory") local DirectoryNode = require("nvim-tree.node.directory")
---@class (exact) DecoratorModified: Decorator ---@class (exact) ModifiedDecorator: Decorator
---@field icon HighlightedString? ---@field private explorer Explorer
local DecoratorModified = Decorator:extend() ---@field private icon HighlightedString?
local ModifiedDecorator = Decorator:extend()
---@class DecoratorModified ---@class ModifiedDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorModified ---@overload fun(args: DecoratorArgs): ModifiedDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorModified:new(args) function ModifiedDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true,
hl_pos = args.explorer.opts.renderer.highlight_modified or "none",
icon_placement = args.explorer.opts.renderer.icons.modified_placement or "none",
})
if not self.enabled then self.enabled = true
return self.highlight_range = self.explorer.opts.renderer.highlight_modified or "none"
end self.icon_placement = self.explorer.opts.renderer.icons.modified_placement or "none"
if self.explorer.opts.renderer.icons.show.modified then if self.explorer.opts.renderer.icons.show.modified then
self.icon = { self.icon = {
@@ -35,18 +31,18 @@ end
---Modified icon: modified.enable, renderer.icons.show.modified and node is modified ---Modified icon: modified.enable, renderer.icons.show.modified and node is modified
---@param node Node ---@param node Node
---@return HighlightedString[]|nil icons ---@return HighlightedString[]? icons
function DecoratorModified:calculate_icons(node) function ModifiedDecorator:icons(node)
if self.enabled and buffers.is_modified(node) then if buffers.is_modified(node) then
return { self.icon } return { self.icon }
end end
end end
---Modified highlight: modified.enable, renderer.highlight_modified and node is modified ---Modified highlight: modified.enable, renderer.highlight_modified and node is modified
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorModified:calculate_highlight(node) function ModifiedDecorator:highlight_group(node)
if not self.enabled or self.range == "none" or not buffers.is_modified(node) then if self.highlight_range == "none" or not buffers.is_modified(node) then
return nil return nil
end end
@@ -57,4 +53,4 @@ function DecoratorModified:calculate_highlight(node)
end end
end end
return DecoratorModified return ModifiedDecorator

View File

@@ -2,31 +2,31 @@ local buffers = require("nvim-tree.buffers")
local Decorator = require("nvim-tree.renderer.decorator") local Decorator = require("nvim-tree.renderer.decorator")
---@class (exact) DecoratorOpened: Decorator ---@class (exact) OpenDecorator: Decorator
---@field icon HighlightedString|nil ---@field private explorer Explorer
local DecoratorOpened = Decorator:extend() ---@field private icon HighlightedString|nil
local OpenDecorator = Decorator:extend()
---@class DecoratorOpened ---@class OpenDecorator
---@overload fun(explorer: DecoratorArgs): DecoratorOpened ---@overload fun(args: DecoratorArgs): OpenDecorator
---@protected ---@protected
---@param args DecoratorArgs ---@param args DecoratorArgs
function DecoratorOpened:new(args) function OpenDecorator:new(args)
Decorator.new(self, { self.explorer = args.explorer
explorer = args.explorer,
enabled = true, self.enabled = true
hl_pos = args.explorer.opts.renderer.highlight_opened_files or "none", self.highlight_range = self.explorer.opts.renderer.highlight_opened_files or "none"
icon_placement = "none", self.icon_placement = "none"
})
end end
---Opened highlight: renderer.highlight_opened_files and node has an open buffer ---Opened highlight: renderer.highlight_opened_files and node has an open buffer
---@param node Node ---@param node Node
---@return string|nil group ---@return string? highlight_group
function DecoratorOpened:calculate_highlight(node) function OpenDecorator:highlight_group(node)
if self.range ~= "none" and buffers.is_opened(node) then if self.highlight_range ~= "none" and buffers.is_opened(node) then
return "NvimTreeOpenedHL" return "NvimTreeOpenedHL"
end end
end end
return DecoratorOpened return OpenDecorator

View File

@@ -0,0 +1,7 @@
local Decorator = require("nvim-tree.renderer.decorator")
---Exposed as nvim_tree.api.decorator.UserDecorator
---@class (exact) UserDecorator: Decorator
local UserDecorator = Decorator:extend()
return UserDecorator

View File

@@ -11,6 +11,8 @@ local namespace_highlights_id = vim.api.nvim_create_namespace("NvimTreeHighlight
local namespace_extmarks_id = vim.api.nvim_create_namespace("NvimTreeExtmarks") local namespace_extmarks_id = vim.api.nvim_create_namespace("NvimTreeExtmarks")
local namespace_virtual_lines_id = vim.api.nvim_create_namespace("NvimTreeVirtualLines") local namespace_virtual_lines_id = vim.api.nvim_create_namespace("NvimTreeVirtualLines")
---@alias HighlightRangeArgs { higroup:string, start:integer[], finish:integer[] } named arguments for vim.hl.range
---@class (exact) Renderer: Class ---@class (exact) Renderer: Class
---@field explorer Explorer ---@field explorer Explorer
local Renderer = Class:extend() local Renderer = Class:extend()
@@ -30,11 +32,11 @@ end
---@private ---@private
---@param bufnr number ---@param bufnr number
---@param lines string[] ---@param lines string[]
---@param hl_args AddHighlightArgs[] ---@param hl_range_args HighlightRangeArgs[]
---@param signs string[] ---@param signs string[]
---@param extmarks table[] extra marks for right icon placement ---@param extmarks table[] extra marks for right icon placement
---@param virtual_lines table[] virtual lines for hidden count display ---@param virtual_lines table[] virtual lines for hidden count display
function Renderer:_draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines) function Renderer:_draw(bufnr, lines, hl_range_args, signs, extmarks, virtual_lines)
if vim.fn.has("nvim-0.10") == 1 then if vim.fn.has("nvim-0.10") == 1 then
vim.api.nvim_set_option_value("modifiable", true, { buf = bufnr }) vim.api.nvim_set_option_value("modifiable", true, { buf = bufnr })
else else
@@ -42,7 +44,7 @@ function Renderer:_draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines)
end end
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
self:render_hl(bufnr, hl_args) self:render_hl(bufnr, hl_range_args)
if vim.fn.has("nvim-0.10") == 1 then if vim.fn.has("nvim-0.10") == 1 then
vim.api.nvim_set_option_value("modifiable", false, { buf = bufnr }) vim.api.nvim_set_option_value("modifiable", false, { buf = bufnr })
@@ -77,16 +79,18 @@ function Renderer:_draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines)
end end
---@private ---@private
function Renderer:render_hl(bufnr, hl) ---@param bufnr integer
---@param hl_range_args HighlightRangeArgs[]
function Renderer:render_hl(bufnr, hl_range_args)
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then
return return
end end
vim.api.nvim_buf_clear_namespace(bufnr, namespace_highlights_id, 0, -1) vim.api.nvim_buf_clear_namespace(bufnr, namespace_highlights_id, 0, -1)
for _, data in ipairs(hl) do for _, args in ipairs(hl_range_args) do
if type(data[1]) == "table" then if vim.fn.has("nvim-0.11") == 1 and vim.hl and vim.hl.range then
for _, group in ipairs(data[1]) do vim.hl.range(bufnr, namespace_highlights_id, args.higroup, args.start, args.finish, {})
vim.api.nvim_buf_add_highlight(bufnr, namespace_highlights_id, group, data[2], data[3], data[4]) else
end vim.api.nvim_buf_add_highlight(bufnr, namespace_highlights_id, args.higroup, args.start[1], args.start[2], args.finish[2]) ---@diagnostic disable-line: deprecated
end end
end end
end end
@@ -103,7 +107,7 @@ function Renderer:draw()
local builder = Builder(self.explorer):build() local builder = Builder(self.explorer):build()
self:_draw(bufnr, builder.lines, builder.hl_args, builder.signs, builder.extmarks, builder.virtual_lines) self:_draw(bufnr, builder.lines, builder.hl_range_args, builder.signs, builder.extmarks, builder.virtual_lines)
if cursor and #builder.lines >= cursor[1] then if cursor and #builder.lines >= cursor[1] then
vim.api.nvim_win_set_cursor(view.get_winnr() or 0, cursor) vim.api.nvim_win_set_cursor(view.get_winnr() or 0, cursor)

View File

@@ -1,5 +1,4 @@
local Iterator = require("nvim-tree.iterators.node-iterator") local Iterator = require("nvim-tree.iterators.node-iterator")
local notify = require("nvim-tree.notify")
local M = { local M = {
debouncers = {}, debouncers = {},
@@ -173,6 +172,21 @@ function M.find_node_line(node)
return -1 return -1
end end
---@param extmarks vim.api.keyset.get_extmark_item[] as per vim.api.nvim_buf_get_extmarks
---@return number
function M.extmarks_length(extmarks)
local length = 0
for _, extmark in ipairs(extmarks) do
local details = extmark[4]
if details and details.virt_text then
for _, text in ipairs(details.virt_text) do
length = length + vim.fn.strchars(text[1])
end
end
end
return length
end
-- get the node in the tree state depending on the absolute path of the node -- get the node in the tree state depending on the absolute path of the node
-- (grouped or hidden too) -- (grouped or hidden too)
---@param path string ---@param path string
@@ -289,11 +303,51 @@ function M.rename_loaded_buffers(old_path, new_path)
end end
end end
local is_windows_drive = function(path)
return (M.is_windows) and (path:match("^%a:\\$") ~= nil)
end
---@param path string path to file or directory ---@param path string path to file or directory
---@return boolean ---@return boolean
function M.file_exists(path) function M.file_exists(path)
local _, error = vim.loop.fs_stat(path) if not (M.is_windows or M.is_wsl) then
return error == nil local _, error = vim.loop.fs_stat(path)
return error == nil
end
-- Windows is case-insensetive, but case-preserving
-- If a file's name is being changed into itself
-- with different casing, windows will falsely
-- report that file is already existing, so a hand-rolled
-- implementation of checking for existance is needed.
-- Same holds for WSL, since it can sometimes
-- access Windows files directly.
-- For more details see (#3117).
if is_windows_drive(path) then
return vim.fn.isdirectory(path) == 1
end
local parent = vim.fn.fnamemodify(path, ":h")
local filename = vim.fn.fnamemodify(path, ":t")
local handle = vim.loop.fs_scandir(parent)
if not handle then
-- File can not exist if its parent directory does not exist
return false
end
while true do
local name, _ = vim.loop.fs_scandir_next(handle)
if not name then
break
end
if name == filename then
return true
end
end
return false
end end
---@param path string ---@param path string
@@ -349,20 +403,6 @@ end
---@param dst_pos string value pos ---@param dst_pos string value pos
---@param remove boolean ---@param remove boolean
function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos, remove) function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos, remove)
local ok, err = pcall(vim.validate, {
src = { src, "table" },
src_path = { src_path, "string" },
src_pos = { src_pos, "string" },
dst = { dst, "table" },
dst_path = { dst_path, "string" },
dst_pos = { dst_pos, "string" },
remove = { remove, "boolean" },
})
if not ok then
notify.warn("move_missing_val: " .. (err or "invalid arguments"))
return
end
for pos in string.gmatch(src_path, "([^%.]+)%.*") do for pos in string.gmatch(src_path, "([^%.]+)%.*") do
if src[pos] and type(src[pos]) == "table" then if src[pos] and type(src[pos]) == "table" then
src = src[pos] src = src[pos]

View File

@@ -119,7 +119,7 @@ local function get_size(size)
if type(size) == "number" then if type(size) == "number" then
return size return size
elseif type(size) == "function" then elseif type(size) == "function" then
return size() return get_size(size())
end end
local size_as_number = tonumber(size:sub(0, -2)) local size_as_number = tonumber(size:sub(0, -2))
local percent_as_decimal = size_as_number / 100 local percent_as_decimal = size_as_number / 100
@@ -164,8 +164,10 @@ local function set_window_options_and_buffer()
local eventignore = vim.api.nvim_get_option("eventignore") ---@diagnostic disable-line: deprecated local eventignore = vim.api.nvim_get_option("eventignore") ---@diagnostic disable-line: deprecated
vim.api.nvim_set_option("eventignore", "all") ---@diagnostic disable-line: deprecated vim.api.nvim_set_option("eventignore", "all") ---@diagnostic disable-line: deprecated
-- #3009 vim.api.nvim_win_set_option does not set local scope without explicit winid.
-- Revert to opt_local instead of propagating it through for just the 0.10 path.
for k, v in pairs(M.View.winopts) do for k, v in pairs(M.View.winopts) do
vim.api.nvim_win_set_option(0, k, v) ---@diagnostic disable-line: deprecated vim.opt_local[k] = v
end end
vim.api.nvim_set_option("eventignore", eventignore) ---@diagnostic disable-line: deprecated vim.api.nvim_set_option("eventignore", eventignore) ---@diagnostic disable-line: deprecated
@@ -252,7 +254,6 @@ local function close(tabpage)
return return
end end
end end
events._dispatch_on_tree_close()
return return
end end
end end
@@ -268,9 +269,12 @@ function M.close_all_tabs()
end end
end end
function M.close() ---@param tabpage integer|nil
function M.close(tabpage)
if M.View.tab.sync.close then if M.View.tab.sync.close then
M.close_all_tabs() M.close_all_tabs()
elseif tabpage then
close(tabpage)
else else
M.close_this_tab_only() M.close_this_tab_only()
end end
@@ -284,6 +288,7 @@ function M.open(options)
local profile = log.profile_start("view open") local profile = log.profile_start("view open")
events._dispatch_on_tree_pre_open()
create_buffer() create_buffer()
open_window() open_window()
M.resize() M.resize()
@@ -319,8 +324,12 @@ local function grow()
max_width = get_width(M.View.max_width) - padding max_width = get_width(M.View.max_width) - padding
end end
for _, l in pairs(lines) do local ns_id = vim.api.nvim_get_namespaces()["NvimTreeExtmarks"]
for line_nr, l in pairs(lines) do
local count = vim.fn.strchars(l) local count = vim.fn.strchars(l)
-- also add space for right-aligned icons
local extmarks = vim.api.nvim_buf_get_extmarks(M.get_bufnr(), ns_id, { line_nr, 0 }, { line_nr, -1 }, { details = true })
count = count + utils.extmarks_length(extmarks)
if resizing_width < count then if resizing_width < count then
resizing_width = count resizing_width = count
end end
@@ -398,6 +407,7 @@ end
---@param opts OpenInWinOpts|nil ---@param opts OpenInWinOpts|nil
function M.open_in_win(opts) function M.open_in_win(opts)
opts = opts or { hijack_current_buf = true, resize = true } opts = opts or { hijack_current_buf = true, resize = true }
events._dispatch_on_tree_pre_open()
if opts.winid and vim.api.nvim_win_is_valid(opts.winid) then if opts.winid and vim.api.nvim_win_is_valid(opts.winid) then
vim.api.nvim_set_current_win(opts.winid) vim.api.nvim_set_current_win(opts.winid)
end end
@@ -409,6 +419,7 @@ function M.open_in_win(opts)
M.reposition_window() M.reposition_window()
M.resize() M.resize()
end end
events._dispatch_on_tree_open()
end end
function M.abandon_current_window() function M.abandon_current_window()

View File

@@ -4,6 +4,8 @@ local utils = require("nvim-tree.utils")
local Class = require("nvim-tree.classic") local Class = require("nvim-tree.classic")
local MESSAGE_EMFILE = "fs.inotify.max_user_watches exceeded, see https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting"
local FS_EVENT_FLAGS = { local FS_EVENT_FLAGS = {
-- inotify or equivalent will be used; fallback to stat has not yet been implemented -- inotify or equivalent will be used; fallback to stat has not yet been implemented
stat = false, stat = false,
@@ -75,6 +77,18 @@ function Event:start()
local event_cb = vim.schedule_wrap(function(err, filename) local event_cb = vim.schedule_wrap(function(err, filename)
if err then if err then
log.line("watcher", "event_cb '%s' '%s' FAIL : %s", self.path, filename, err) log.line("watcher", "event_cb '%s' '%s' FAIL : %s", self.path, filename, err)
-- do nothing if watchers have already been disabled
if not M.config.filesystem_watchers.enable then
return
end
-- EMFILE is catastrophic
if name == "EMFILE" then
M.disable_watchers(MESSAGE_EMFILE)
return
end
local message = string.format("File system watcher failed (%s) for path %s, halting watcher.", err, self.path) local message = string.format("File system watcher failed (%s) for path %s, halting watcher.", err, self.path)
if err == "EPERM" and (utils.is_windows or utils.is_wsl) then if err == "EPERM" and (utils.is_windows or utils.is_wsl) then
-- on directory removal windows will cascade the filesystem events out of order -- on directory removal windows will cascade the filesystem events out of order
@@ -94,7 +108,7 @@ function Event:start()
rc, _, name = self.fs_event:start(self.path, FS_EVENT_FLAGS, event_cb) rc, _, name = self.fs_event:start(self.path, FS_EVENT_FLAGS, event_cb)
if rc ~= 0 then if rc ~= 0 then
if name == "EMFILE" then if name == "EMFILE" then
M.disable_watchers("fs.inotify.max_user_watches exceeded, see https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting") M.disable_watchers(MESSAGE_EMFILE)
else else
notify.warn(string.format("Could not start the fs_event watcher for path %s : %s", self.path, name)) notify.warn(string.format("Could not start the fs_event watcher for path %s : %s", self.path, name))
end end

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
out=$(grep -nr "^--- @" lua) out=$(grep -nr "^--- @" lua)

View File

@@ -1,4 +1,4 @@
#!/bin/sh #!/usr/bin/env sh
# run after changing nvim-tree.lua DEFAULT_OPTS or keymap.lua M.default_on_attach # run after changing nvim-tree.lua DEFAULT_OPTS or keymap.lua M.default_on_attach
# scrapes and updates nvim-tree-lua.txt # scrapes and updates nvim-tree-lua.txt

View File

@@ -1,9 +1,11 @@
#!/bin/sh #!/usr/bin/env sh
# Performs a lua-language-server check on all files. # Performs a lua-language-server check on all files.
# luals-out/check.json will be produced on any issues, returning 1. # luals-out/check.json will be produced on any issues, returning 1.
# Outputs only check.json to stdout, all other messages to stderr, to allow jq etc. # Outputs only check.json to stdout, all other messages to stderr, to allow jq etc.
# $VIMRUNTIME specifies neovim runtime path, defaults to "/usr/share/nvim/runtime" if unset. # $VIMRUNTIME specifies neovim runtime path, defaults to "/usr/share/nvim/runtime" if unset.
#
# Call with codestyle-check param to enable only codestyle-check
if [ -z "${VIMRUNTIME}" ]; then if [ -z "${VIMRUNTIME}" ]; then
export VIMRUNTIME="/usr/share/nvim/runtime" export VIMRUNTIME="/usr/share/nvim/runtime"
@@ -17,11 +19,24 @@ FILE_LUARC="${DIR_OUT}/luarc.json"
rm -rf "${DIR_OUT}" rm -rf "${DIR_OUT}"
mkdir "${DIR_OUT}" mkdir "${DIR_OUT}"
# Uncomment runtime.version for strict neovim baseline 5.1 case "${1}" in
# It is not set normally, to prevent luals loading 5.1 and 5.x, resulting in both versions being chosen on vim.lsp.buf.definition() "codestyle-check")
cat "${PWD}/.luarc.json" | sed -E 's/.luals-check-only//g' > "${FILE_LUARC}" jq \
'.diagnostics.neededFileStatus[] = "None" | .diagnostics.neededFileStatus."codestyle-check" = "Any"' \
"${PWD}/.luarc.json" > "${FILE_LUARC}"
# execute inside lua to prevent luals itself from being checked ;;
*)
# Add runtime.version for strict neovim baseline 5.1
# It is not set normally, to prevent luals loading 5.1 and 5.x, resulting in both versions being chosen on vim.lsp.buf.definition
jq \
'."runtime.version" = "Lua 5.1"' \
"${PWD}/.luarc.json" > "${FILE_LUARC}"
;;
esac
# execute inside lua directory to prevent luals itself from being checked
OUT=$(lua-language-server --check="${DIR_SRC}" --configpath="${FILE_LUARC}" --checklevel=Information --logpath="${DIR_OUT}" --loglevel=error) OUT=$(lua-language-server --check="${DIR_SRC}" --configpath="${FILE_LUARC}" --checklevel=Information --logpath="${DIR_OUT}" --loglevel=error)
RC=$? RC=$?