Compare commits

..

19 Commits

Author SHA1 Message Date
github-actions[bot]
2bc725a3eb chore(master): release nvim-tree 1.3.3 (#2776)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-14 10:44:50 +10:00
Jacob Kania
340d3a9795 fix: nil access exception with git integration when changing branches (#2774)
Fix nil access exception appearing when changing branches
2024-05-14 10:31:56 +10:00
dependabot[bot]
edd4e25fd4 chore(deps): bump actions/checkout from 3 to 4 (#2773)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 12:14:07 +03:00
github-actions[bot]
78c4c083ed chore(master): release nvim-tree 1.3.2 (#2771)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-12 14:45:55 +10:00
Andrew Plaza
acffab931a ci: luarocks releases (#2764)
* add luarocks upload

* refactor

* restrict to full semver versions

* tweak luarocks descriptions

* remove test release following successful run

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-05-12 14:37:49 +10:00
Alexander Courtis
64f61e4c91 fix(#925): handle newlines in file names (#2754) 2024-05-04 13:51:13 +10:00
Alexander Courtis
347e1eb352 fix(#2758): use nvim-webdevicons default file icon, not renderer.icons.glyphs.default, as per :help (#2759)
fix(#2758): use nvim-webdevicons default for default files
2024-04-30 11:32:51 +10:00
github-actions[bot]
76db7ed0da chore(master): release nvim-tree 1.3.1 (#2736)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-30 11:12:07 +10:00
dependabot[bot]
5a18b98274 chore(deps): bump amannn/action-semantic-pull-request from 5.5.0 to 5.5.2 (#2756)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-25 11:35:54 +03:00
dependabot[bot]
62008e5cf2 chore(deps): bump amannn/action-semantic-pull-request from 5.4.0 to 5.5.0 (#2755)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-24 11:14:32 +03:00
Alexander Courtis
ae8e46e8fa chore: add plugin manager requirements to bug template (#2752) 2024-04-21 12:58:49 +10:00
Alexander Courtis
81eb8d5192 fix(#2733): escape trash path (#2735)
* fix(#2733): escape trash path

* fix(#2733): escape trash path

* fix(#2733): escape trash path
2024-04-06 12:28:41 +11:00
Yida Zhang
d8d3a1590a fix(#2535): TextYankPost event sends vim.v.event (#2734)
* fix TextYankPost event

* Update lua/nvim-tree/actions/fs/copy-paste.lua

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

* fix format string

* style

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-03-31 17:24:47 +11:00
github-actions[bot]
ddd1d6eb21 chore(master): release nvim-tree 1.3.0 (#2725)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-30 17:53:21 +11:00
Alexander Courtis
0aca0920f4 fix(#2658): change SpellCap groups to reduce confusion: ExecFile->Question, ImageFile->Question, SpecialFile->Title, Symlink->Underlined; add all other highlight groups to :NvimTreeHiTest (#2732)
* fix(#2658): add all highlight groups to :NvimTreeHiTest

* fix(#2658): add all highlight groups to :NvimTreeHiTest

* fix(#2658): change SpellCap groups: ExecFile->Question, ImageFile->Question, SpecialFile->Title, Symlink->Underlined
2024-03-30 17:47:30 +11:00
Alexander Courtis
308f2fcec2 docs: retire matrix (#2730) 2024-03-30 14:09:08 +11:00
remvn
2d97059661 fix: bookmark filter shows marked directory children (#2719)
* fix: bookmark filter include marked-directory's children

* fix(perf): add path_type to filter functions

* fix: replace undefined type

* fix: correct Node.fs_stat type

* fix: file info popup check fs_stat not nil

* refactor: add stat to should_filter, Node constructor

* perf: early return if bookmark is empty

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-03-30 13:21:55 +11:00
Alexander Courtis
e508bdc418 docs: contributing: PR subject (#2726)
* docs: contributing: PR subject

* docs: contributing: PR subject
2024-03-26 12:23:48 +11:00
Ava Harris
e20966ae55 feat: add update_focused_file.exclude (#2673)
* Add update_focused_file.exclude and move update_focused_file.ignore_list to update_focused_file.update_root.ignore_list

* Pass ci checks

* Add config migration for update_root and ignore_list

* Missed one mention of update root in find_file.lua

* Update migration code

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

* make docs consistent

* match on filename instead of entire path

* exclude as a function

* fix docs

* default exclude value

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
2024-03-25 15:41:05 +11:00
27 changed files with 345 additions and 112 deletions

View File

@@ -59,9 +59,9 @@ body:
If not provided it is very unlikely that the nvim-tree team will be able to address your issue. If not provided it is very unlikely that the nvim-tree team will be able to address your issue.
See [wiki: Clean Room Replication](https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting#clean-room-replication) for instructions. See [wiki: Clean Room Replication](https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting#clean-room-replication) for instructions and paste the contents of your `/tmp/nvt-min.lua` here.
Please paste the contents of your `/tmp/nvt-min.lua` here." Please do NOT post a configuration that uses other plugin managers such as lazy, see [wiki: Lazy Loading](https://github.com/nvim-tree/nvim-tree.lua/wiki/Installation#lazy-loading)"
render: lua render: lua
validations: validations:
required: true required: true

37
.github/workflows/luarocks.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
jobs:
luarocks-upload:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: LuaRocks Upload
uses: nvim-neorocks/luarocks-tag-release@v5
env:
LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
with:
summary: A File Explorer For Neovim
detailed_description: |
Automatic updates
File type icons
Git integration
Diagnostics integration - LSP and COC
(Live) filtering
Cut, copy, paste, rename, delete, create etc.
Highly customisable
Rich API
license: "GPL-3.0"
labels: neovim
dependencies: |
nvim-web-devicons

View File

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

View File

@@ -1,3 +1,3 @@
{ {
".": "1.2.0" ".": "1.3.3"
} }

View File

@@ -1,5 +1,42 @@
# Changelog # Changelog
## [1.3.3](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.2...nvim-tree-v1.3.3) (2024-05-14)
### Bug Fixes
* nil access exception with git integration when changing branches ([#2774](https://github.com/nvim-tree/nvim-tree.lua/issues/2774)) ([340d3a9](https://github.com/nvim-tree/nvim-tree.lua/commit/340d3a9795e06bdd1814228de398cd510f9bfbb0))
## [1.3.2](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.1...nvim-tree-v1.3.2) (2024-05-12)
### Bug Fixes
* **#2758:** use nvim-webdevicons default file icon, not renderer.icons.glyphs.default, as per :help ([#2759](https://github.com/nvim-tree/nvim-tree.lua/issues/2759)) ([347e1eb](https://github.com/nvim-tree/nvim-tree.lua/commit/347e1eb35264677f66a79466bb5e3d111968e12c))
* **#2758:** use nvim-webdevicons default for default files ([347e1eb](https://github.com/nvim-tree/nvim-tree.lua/commit/347e1eb35264677f66a79466bb5e3d111968e12c))
* **#925:** handle newlines in file names ([#2754](https://github.com/nvim-tree/nvim-tree.lua/issues/2754)) ([64f61e4](https://github.com/nvim-tree/nvim-tree.lua/commit/64f61e4c913047a045ff90bd188dd3b54ee443cf))
## [1.3.1](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.3.0...nvim-tree-v1.3.1) (2024-04-25)
### Bug Fixes
* **#2535:** TextYankPost event sends vim.v.event ([#2734](https://github.com/nvim-tree/nvim-tree.lua/issues/2734)) ([d8d3a15](https://github.com/nvim-tree/nvim-tree.lua/commit/d8d3a1590a05b2d8b5eb26e2ed1c6052b1b47a77))
* **#2733:** escape trash path ([#2735](https://github.com/nvim-tree/nvim-tree.lua/issues/2735)) ([81eb8d5](https://github.com/nvim-tree/nvim-tree.lua/commit/81eb8d519233c105f30dc0a278607e62b20502fd))
## [1.3.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.2.0...nvim-tree-v1.3.0) (2024-03-30)
### Features
* add update_focused_file.exclude ([#2673](https://github.com/nvim-tree/nvim-tree.lua/issues/2673)) ([e20966a](https://github.com/nvim-tree/nvim-tree.lua/commit/e20966ae558524f8d6f93dc37f5d2a8605f893e2))
### Bug Fixes
* **#2658:** change SpellCap groups to reduce confusion: ExecFile-&gt;Question, ImageFile->Question, SpecialFile->Title, Symlink->Underlined; add all other highlight groups to :NvimTreeHiTest ([#2732](https://github.com/nvim-tree/nvim-tree.lua/issues/2732)) ([0aca092](https://github.com/nvim-tree/nvim-tree.lua/commit/0aca0920f44b12a8383134bcb52da9faec123608))
* bookmark filter shows marked directory children ([#2719](https://github.com/nvim-tree/nvim-tree.lua/issues/2719)) ([2d97059](https://github.com/nvim-tree/nvim-tree.lua/commit/2d97059661c83787372c8c003e743c984ba3ac50))
## [1.2.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.1.1...nvim-tree-v1.2.0) (2024-03-24) ## [1.2.0](https://github.com/nvim-tree/nvim-tree.lua/compare/nvim-tree-v1.1.1...nvim-tree-v1.2.0) (2024-03-24)

View File

@@ -81,9 +81,31 @@ When adding or changing API please update :help nvim-tree-api
# Pull Request # Pull Request
Please reference any issues in the description e.g. "resolves #1234". Please reference any issues in the description e.g. "resolves #1234", which will be closed upon merge.
Please check "allow edits by maintainers" to allow nvim-tree developers to make small changes such as documentation tweaks. Please check "allow edits by maintainers" to allow nvim-tree developers to make small changes such as documentation tweaks.
A test case to reproduce the issue is required. A ["Clean Room" Replication](https://github.com/nvim-tree/nvim-tree.lua/wiki/Troubleshooting#clean-room-replication) is preferred. ## Subject
The merge commit message will be the subject of the PR.
A [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) subject will be validated by the Semantic Pull Request Subject CI job. Reference the issue to be used in the release notes e.g.
`fix(#2395): marks.bulk.move defaults to directory at cursor`
Available types:
* feat: A new feature
* fix: A bug fix
* docs: Documentation only changes
* style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* refactor: A code change that neither fixes a bug nor adds a feature
* perf: A code change that improves performance
* test: Adding missing tests or correcting existing tests
* build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
* chore: Other changes that don't modify src or test files
* revert: Reverts a previous commit
If in doubt, look at previous commits.
See also [The Conventional Commits ultimate cheatsheet](https://gist.github.com/gabrielecanepa/fa6cca1a8ae96f77896fe70ddee65527)

View File

@@ -24,7 +24,7 @@
Take a look at the [wiki](https://github.com/nvim-tree/nvim-tree.lua/wiki) for Showcases, Tips, Recipes and more. Take a look at the [wiki](https://github.com/nvim-tree/nvim-tree.lua/wiki) for Showcases, Tips, Recipes and more.
Community support: [matrix](https://matrix.to/#/#nvim-tree:matrix.org) Questions and general support: [Discussions](https://github.com/nvim-tree/nvim-tree.lua/discussions)
## Requirements ## Requirements

View File

@@ -500,9 +500,12 @@ Following is the default configuration. See |nvim-tree-opts| for details.
}, },
update_focused_file = { update_focused_file = {
enable = false, enable = false,
update_root = false, update_root = {
enable = false,
ignore_list = {}, ignore_list = {},
}, },
exclude = false,
},
system_open = { system_open = {
cmd = "", cmd = "",
args = {}, args = {},
@@ -1105,15 +1108,22 @@ Update the root directory of the tree if the file is not under current
root directory. It prefers vim's cwd and `root_dirs`. root directory. It prefers vim's cwd and `root_dirs`.
Otherwise it falls back to the folder containing the file. Otherwise it falls back to the folder containing the file.
Only relevant when `update_focused_file.enable` is `true` Only relevant when `update_focused_file.enable` is `true`
*nvim-tree.update_focused_file.update_root.enable*
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
*nvim-tree.update_focused_file.ignore_list* *nvim-tree.update_focused_file.update_root.ignore_list*
List of buffer names and filetypes that will not update the root dir List of buffer names and filetypes that will not update the root dir
of the tree if the file isn't found under the current root directory. of the tree if the file isn't found under the current root directory.
Only relevant when `update_focused_file.update_root` and Only relevant when `update_focused_file.update_root.enable` and
`update_focused_file.enable` are `true`. `update_focused_file.enable` are `true`.
Type: {string}, Default: `{}` Type: {string}, Default: `{}`
*nvim-tree.update_focused_file.exclude*
A function that returns true if the file should not be focused when opening.
Takes the `BufEnter` event as an argument. see |autocmd-events|
Type: {function}, Default: `false`
============================================================================== ==============================================================================
5.6 OPTS: SYSTEM OPEN *nvim-tree-opts-system-open* 5.6 OPTS: SYSTEM OPEN *nvim-tree-opts-system-open*
@@ -2869,8 +2879,10 @@ highlight group is not, hard linking as follows: >
|nvim-tree.ui.confirm.remove| |nvim-tree.ui.confirm.remove|
|nvim-tree.ui.confirm.trash| |nvim-tree.ui.confirm.trash|
|nvim-tree.update_focused_file.enable| |nvim-tree.update_focused_file.enable|
|nvim-tree.update_focused_file.ignore_list| |nvim-tree.update_focused_file.exclude|
|nvim-tree.update_focused_file.update_root| |nvim-tree.update_focused_file.update_root|
|nvim-tree.update_focused_file.update_root.enable|
|nvim-tree.update_focused_file.update_root.ignore_list|
|nvim-tree.view.centralize_selection| |nvim-tree.view.centralize_selection|
|nvim-tree.view.cursorline| |nvim-tree.view.cursorline|
|nvim-tree.view.debounce_delay| |nvim-tree.view.debounce_delay|

View File

@@ -27,7 +27,7 @@ function M.change_root(path, bufnr)
-- skip if current file is in ignore_list -- skip if current file is in ignore_list
if type(bufnr) == "number" then if type(bufnr) == "number" then
local ft = vim.api.nvim_buf_get_option(bufnr, "filetype") or "" local ft = vim.api.nvim_buf_get_option(bufnr, "filetype") or ""
for _, value in pairs(_config.update_focused_file.ignore_list) do for _, value in pairs(_config.update_focused_file.update_root.ignore_list) do
if utils.str_find(path, value) or utils.str_find(ft, value) then if utils.str_find(path, value) or utils.str_find(ft, value) then
return return
end end
@@ -149,7 +149,7 @@ function M.change_dir(name)
actions.root.change_dir.fn(name) actions.root.change_dir.fn(name)
end end
if _config.update_focused_file.enable then if _config.update_focused_file.update_root.enable then
actions.tree.find_file.fn() actions.tree.find_file.fn()
end end
end end
@@ -247,7 +247,11 @@ local function setup_autocommands(opts)
end end
if opts.update_focused_file.enable then if opts.update_focused_file.enable then
create_nvim_tree_autocmd("BufEnter", { create_nvim_tree_autocmd("BufEnter", {
callback = function() callback = function(event)
local exclude = opts.update_focused_file.exclude
if type(exclude) == "function" and exclude(event) then
return
end
utils.debounce("BufEnter:find_file", opts.view.debounce_delay, function() utils.debounce("BufEnter:find_file", opts.view.debounce_delay, function()
actions.tree.find_file.fn() actions.tree.find_file.fn()
end) end)
@@ -462,9 +466,12 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
}, },
update_focused_file = { update_focused_file = {
enable = false, enable = false,
update_root = false, update_root = {
enable = false,
ignore_list = {}, ignore_list = {},
}, },
exclude = false,
},
system_open = { system_open = {
cmd = "", cmd = "",
args = {}, args = {},
@@ -624,6 +631,9 @@ local ACCEPTED_TYPES = {
group_empty = { "boolean", "function" }, group_empty = { "boolean", "function" },
root_folder_label = { "function", "string", "boolean" }, root_folder_label = { "function", "string", "boolean" },
}, },
update_focused_file = {
exclude = { "function" },
},
filters = { filters = {
custom = { "function" }, custom = { "function" },
}, },

View File

@@ -36,12 +36,13 @@ local function search(search_dir, input_path)
while name do while name do
path = dir .. "/" .. name path = dir .. "/" .. name
---@type uv.fs_stat.result|nil
stat, _ = vim.loop.fs_stat(path) stat, _ = vim.loop.fs_stat(path)
if not stat then if not stat then
break break
end end
if not filters.should_filter(path, filter_status) then if not filters.should_filter(path, stat, filter_status) then
if string.find(path, "/" .. input_path .. "$") then if string.find(path, "/" .. input_path .. "$") then
return path return path
end end

View File

@@ -276,17 +276,24 @@ end
---@param content string ---@param content string
local function copy_to_clipboard(content) local function copy_to_clipboard(content)
local clipboard_name local clipboard_name
local reg
if M.config.actions.use_system_clipboard == true then if M.config.actions.use_system_clipboard == true then
vim.fn.setreg("+", content)
vim.fn.setreg('"', content)
clipboard_name = "system" clipboard_name = "system"
reg = "+"
else else
vim.fn.setreg('"', content)
vim.fn.setreg("1", content)
clipboard_name = "neovim" clipboard_name = "neovim"
reg = "1"
end end
vim.api.nvim_exec_autocmds("TextYankPost", {}) -- manually firing TextYankPost does not set vim.v.event
-- workaround: create a scratch buffer with the clipboard contents and send a yank command
local temp_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_text(temp_buf, 0, 0, 0, 0, { content })
vim.api.nvim_buf_call(temp_buf, function()
vim.cmd(string.format('normal! "%sy$', reg))
end)
vim.api.nvim_buf_delete(temp_buf, {})
notify.info(string.format("Copied %s to %s clipboard!", content, clipboard_name)) notify.info(string.format("Copied %s to %s clipboard!", content, clipboard_name))
end end

View File

@@ -42,7 +42,7 @@ function M.remove(node)
-- trashes a path (file or folder) -- trashes a path (file or folder)
local function trash_path(on_exit) local function trash_path(on_exit)
local need_sync_wait = utils.is_windows local need_sync_wait = utils.is_windows
local job = vim.fn.jobstart(M.config.trash.cmd .. ' "' .. node.absolute_path .. '"', { local job = vim.fn.jobstart(M.config.trash.cmd .. " " .. vim.fn.shellescape(node.absolute_path), {
detach = not need_sync_wait, detach = not need_sync_wait,
on_exit = on_exit, on_exit = on_exit,
on_stderr = on_stderr, on_stderr = on_stderr,

View File

@@ -6,6 +6,14 @@ local M = {}
---@return table ---@return table
local function get_formatted_lines(node) local function get_formatted_lines(node)
local stats = node.fs_stat local stats = node.fs_stat
if stats == nil then
return {
"",
" Can't retrieve file information",
"",
}
end
local fpath = " fullpath: " .. node.absolute_path local fpath = " fullpath: " .. node.absolute_path
local created_at = " created: " .. os.date("%x %X", stats.birthtime.sec) local created_at = " created: " .. os.date("%x %X", stats.birthtime.sec)
local modified_at = " modified: " .. os.date("%x %X", stats.mtime.sec) local modified_at = " modified: " .. os.date("%x %X", stats.mtime.sec)

View File

@@ -268,13 +268,14 @@ local function open_in_new_window(filename, mode)
local fname = vim.fn.fnameescape(filename) local fname = vim.fn.fnameescape(filename)
fname = utils.escape_special_chars(fname) fname = utils.escape_special_chars(fname)
local cmd local command
if create_new_window then if create_new_window then
cmd = string.format("%s vsplit %s", new_window_side, fname) -- generated from vim.api.nvim_parse_cmd("belowright vsplit foo", {})
command = { cmd = "vsplit", mods = { split = new_window_side }, args = { fname } }
elseif mode:match "split$" then elseif mode:match "split$" then
cmd = string.format("%s %s", mode, fname) command = { cmd = mode, args = { fname } }
else else
cmd = string.format("edit %s", fname) command = { cmd = "edit", args = { fname } }
end end
if (mode == "preview" or mode == "preview_no_picker") and view.View.float.enable then if (mode == "preview" or mode == "preview_no_picker") and view.View.float.enable then
@@ -286,7 +287,7 @@ local function open_in_new_window(filename, mode)
set_current_win_no_autocmd(target_winid, { "BufEnter" }) set_current_win_no_autocmd(target_winid, { "BufEnter" })
end end
pcall(vim.cmd, cmd) pcall(vim.cmd, command)
lib.set_target_win() lib.set_target_win()
end end

View File

@@ -55,7 +55,7 @@ function M.fn(opts)
end end
-- update root -- update root
if opts.update_root or M.config.update_focused_file.update_root then if opts.update_root or M.config.update_focused_file.update_root.enable then
require("nvim-tree").change_root(path, bufnr) require("nvim-tree").change_root(path, bufnr)
end end

View File

@@ -1,5 +1,8 @@
local appearance = require "nvim-tree.appearance" local appearance = require "nvim-tree.appearance"
-- others with name and links less than this arbitrary value are short
local SHORT_LEN = 50
local M = {} local M = {}
---@class HighlightDisplay for :NvimTreeHiTest ---@class HighlightDisplay for :NvimTreeHiTest
@@ -8,7 +11,7 @@ local M = {}
---@field def string :hi concrete definition after following any links ---@field def string :hi concrete definition after following any links
local HighlightDisplay = {} local HighlightDisplay = {}
---@param group string nvim-tree highlight group ---@param group string nvim-tree highlight group name
---@return HighlightDisplay ---@return HighlightDisplay
function HighlightDisplay:new(group) function HighlightDisplay:new(group)
local o = {} local o = {}
@@ -39,38 +42,91 @@ function HighlightDisplay:new(group)
return o return o
end end
---Render one group.
---@param bufnr number to render in
---@param fmt string format string for group, links, def
---@param l number line number to render at
---@return number l next line number
function HighlightDisplay:render(bufnr, fmt, l) 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) vim.api.nvim_buf_add_highlight(bufnr, -1, self.group, l, 0, #self.group)
return l + 1
end end
---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim ---Render many groups.
---Display all nvim-tree highlight groups, their link chain and actual definition ---@param header string before with underline line
function M.hi_test() ---@param displays HighlightDisplay[] highlight group
local displays = {} ---@param bufnr number to render in
---@param l number line number to start at
---@return number l next line number
local function render_displays(header, displays, bufnr, l)
local max_group_len = 0 local max_group_len = 0
local max_links_len = 0 local max_links_len = 0
-- build all highlight groups, name only -- build all highlight groups, using name only
for _, highlight_group in ipairs(appearance.HIGHLIGHT_GROUPS) do for _, display in ipairs(displays) do
local display = HighlightDisplay:new(highlight_group.group)
table.insert(displays, display)
max_group_len = math.max(max_group_len, #display.group) max_group_len = math.max(max_group_len, #display.group)
max_links_len = math.max(max_links_len, #display.links) max_links_len = math.max(max_links_len, #display.links)
end end
-- header
vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { header, (header:gsub(".", "-")) })
l = l + 2
-- render and highlight
local fmt = string.format("%%-%d.%ds %%-%d.%ds %%s", max_group_len, max_group_len, max_links_len, max_links_len)
for _, display in ipairs(displays) do
l = display:render(bufnr, fmt, l)
end
return l
end
---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim
---Display all nvim-tree and neovim highlight groups, their link chain and actual definition
function M.hi_test()
-- create a buffer -- create a buffer
local bufnr = vim.api.nvim_create_buf(false, true) local bufnr = vim.api.nvim_create_buf(false, true)
-- render and highlight
local l = 0 local l = 0
local fmt = string.format("%%-%d.%ds %%-%d.%ds %%s", max_group_len, max_group_len, max_links_len, max_links_len)
for _, display in ipairs(displays) do -- nvim-tree groups, ordered
display:render(bufnr, fmt, l) local displays = {}
l = l + 1 for _, highlight_group in ipairs(appearance.HIGHLIGHT_GROUPS) do
local display = HighlightDisplay:new(highlight_group.group)
table.insert(displays, display)
end end
l = render_displays("nvim-tree", displays, bufnr, l)
vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { "" })
l = l + 1
-- built in groups, ordered opaquely by nvim
local displays_short, displays_long = {}, {}
local ok, out = pcall(vim.api.nvim_cmd, { cmd = "highlight" }, { output = true })
if ok then
for group in string.gmatch(out, "(%w*)%s+xxx") do
if group:find("NvimTree", 1, true) ~= 1 then
local display = HighlightDisplay:new(group)
if #display.group + #display.links > SHORT_LEN then
table.insert(displays_long, display)
else
table.insert(displays_short, display)
end
end
end
end
-- short ones first
l = render_displays("other, short", displays_short, bufnr, l)
vim.api.nvim_buf_set_lines(bufnr, l, -1, true, { "" })
l = l + 1
-- long
render_displays("other, long", displays_long, bufnr, l)
-- finalise and focus the buffer -- finalise and focus the buffer
vim.api.nvim_buf_set_option(bufnr, "modifiable", false) vim.api.nvim_buf_set_option(bufnr, "modifiable", false)

View File

@@ -1,5 +1,11 @@
local M = {} local M = {}
---@class HighlightGroup
---@field group string
---@field link string|nil
---@field def string|nil
---@type HighlightGroup[]
-- All highlight groups: linked or directly defined. -- All highlight groups: linked or directly defined.
-- Please add new groups to help and preserve order. -- Please add new groups to help and preserve order.
-- Please avoid directly defined groups to preserve accessibility for TUI. -- Please avoid directly defined groups to preserve accessibility for TUI.
@@ -24,10 +30,10 @@ M.HIGHLIGHT_GROUPS = {
{ group = "NvimTreeStatusLineNC", link = "StatusLineNC" }, { group = "NvimTreeStatusLineNC", link = "StatusLineNC" },
-- File Text -- File Text
{ group = "NvimTreeExecFile", link = "SpellCap" }, { group = "NvimTreeExecFile", link = "Question" },
{ group = "NvimTreeImageFile", link = "SpellCap" }, { group = "NvimTreeImageFile", link = "Question" },
{ group = "NvimTreeSpecialFile", link = "SpellCap" }, { group = "NvimTreeSpecialFile", link = "Title" },
{ group = "NvimTreeSymlink", link = "SpellCap" }, { group = "NvimTreeSymlink", link = "Underlined" },
-- Folder Text -- Folder Text
{ group = "NvimTreeRootFolder", link = "Title" }, { group = "NvimTreeRootFolder", link = "Title" },

View File

@@ -11,13 +11,6 @@ local Watcher = require "nvim-tree.watcher"
local M = {} local M = {}
---@param type_ string|nil
---@param cwd string
---@return any
local function get_type_from(type_, cwd)
return type_ or (vim.loop.fs_stat(cwd) or {}).type
end
---@param handle uv.uv_fs_t ---@param handle uv.uv_fs_t
---@param cwd string ---@param cwd string
---@param node Node ---@param node Node
@@ -33,18 +26,19 @@ local function populate_children(handle, cwd, node, git_status)
end end
local abs = utils.path_join { cwd, name } local abs = utils.path_join { cwd, name }
local profile = log.profile_start("explore populate_children %s", abs) local profile = log.profile_start("explore populate_children %s", abs)
t = get_type_from(t, abs) ---@type uv.fs_stat.result|nil
if not filters.should_filter(abs, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then local stat = vim.loop.fs_stat(abs)
if not filters.should_filter(abs, stat, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
local child = nil local child = nil
if t == "directory" and vim.loop.fs_access(abs, "R") then if t == "directory" and vim.loop.fs_access(abs, "R") then
child = builders.folder(node, abs, name) child = builders.folder(node, abs, name, stat)
elseif t == "file" then elseif t == "file" then
child = builders.file(node, abs, name) child = builders.file(node, abs, name, stat)
elseif t == "link" then elseif t == "link" then
local link = builders.link(node, abs, name) local link = builders.link(node, abs, name, stat)
if link.link_to ~= nil then if link.link_to ~= nil then
child = link child = link
end end

View File

@@ -71,19 +71,36 @@ local function dotfile(path)
end end
---@param path string ---@param path string
---@param bookmarks table<string, boolean> absolute paths bookmarked ---@param path_type string|nil filetype of path
local function bookmark(path, bookmarks) ---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files
local function bookmark(path, path_type, bookmarks)
if not M.config.filter_no_bookmark then if not M.config.filter_no_bookmark then
return false return false
end end
-- if bookmark is empty, we should see a empty filetree
if next(bookmarks) == nil then
return true
end
-- add trailing slash to make it match only mark's parent directory local mark_parent = utils.path_add_trailing(path)
-- not it's siblings for mark, mark_type in pairs(bookmarks) do
local parent = utils.path_add_trailing(path) if path == mark then
for mark, _ in pairs(bookmarks) do
if path == mark or vim.fn.stridx(mark, parent) == 0 then
return false return false
end end
if path_type == "directory" then
-- check if path is mark's parent
if vim.fn.stridx(mark, mark_parent) == 0 then
return false
end
end
if mark_type == "directory" then
-- check if mark is path's parent
local path_parent = utils.path_add_trailing(mark)
if vim.fn.stridx(path, path_parent) == 0 then
return false
end
end
end end
return true return true
@@ -139,7 +156,7 @@ function M.prepare(git_status)
end end
for _, node in pairs(marks.get_marks()) do for _, node in pairs(marks.get_marks()) do
status.bookmarks[node.absolute_path] = true status.bookmarks[node.absolute_path] = node.type
end end
return status return status
@@ -147,9 +164,10 @@ end
---Check if the given path should be filtered. ---Check if the given path should be filtered.
---@param path string Absolute path ---@param path string Absolute path
---@param fs_stat uv.fs_stat.result|nil fs_stat of file
---@param status table from prepare ---@param status table from prepare
---@return boolean ---@return boolean
function M.should_filter(path, status) function M.should_filter(path, fs_stat, status)
if not M.config.enable then if not M.config.enable then
return false return false
end end
@@ -159,7 +177,11 @@ function M.should_filter(path, status)
return false return false
end end
return git(path, status.git_status) or buf(path, status.bufinfo) or dotfile(path) or custom(path) or bookmark(path, status.bookmarks) return git(path, status.git_status)
or buf(path, status.bufinfo)
or dotfile(path)
or custom(path)
or bookmark(path, fs_stat and fs_stat.type, status.bookmarks)
end end
function M.setup(opts) function M.setup(opts)

View File

@@ -6,15 +6,16 @@ local M = {}
---@param parent Node ---@param parent Node
---@param absolute_path string ---@param absolute_path string
---@param name string ---@param name string
---@param fs_stat uv.fs_stat.result|nil
---@return Node ---@return Node
function M.folder(parent, absolute_path, name) function M.folder(parent, absolute_path, name, fs_stat)
local handle = vim.loop.fs_scandir(absolute_path) local handle = vim.loop.fs_scandir(absolute_path)
local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil
local node = { local node = {
type = "directory", type = "directory",
absolute_path = absolute_path, absolute_path = absolute_path,
fs_stat = vim.loop.fs_stat(absolute_path), fs_stat = fs_stat,
group_next = nil, -- If node is grouped, this points to the next child dir/link node group_next = nil, -- If node is grouped, this points to the next child dir/link node
has_children = has_children, has_children = has_children,
name = name, name = name,
@@ -43,8 +44,9 @@ end
---@param parent Node ---@param parent Node
---@param absolute_path string ---@param absolute_path string
---@param name string ---@param name string
---@param fs_stat uv.fs_stat.result|nil
---@return Node ---@return Node
function M.file(parent, absolute_path, name) function M.file(parent, absolute_path, name, fs_stat)
local ext = string.match(name, ".?[^.]+%.(.*)") or "" local ext = string.match(name, ".?[^.]+%.(.*)") or ""
return { return {
@@ -52,7 +54,7 @@ function M.file(parent, absolute_path, name)
absolute_path = absolute_path, absolute_path = absolute_path,
executable = M.is_executable(absolute_path), executable = M.is_executable(absolute_path),
extension = ext, extension = ext,
fs_stat = vim.loop.fs_stat(absolute_path), fs_stat = fs_stat,
name = name, name = name,
parent = parent, parent = parent,
} }
@@ -66,8 +68,9 @@ end
---@param parent Node ---@param parent Node
---@param absolute_path string ---@param absolute_path string
---@param name string ---@param name string
---@param fs_stat uv.fs_stat.result|nil
---@return Node ---@return Node
function M.link(parent, absolute_path, name) function M.link(parent, absolute_path, name, fs_stat)
--- I dont know if this is needed, because in my understanding, there isn't hard links in windows, but just to be sure i changed it. --- I dont know if this is needed, because in my understanding, there isn't hard links in windows, but just to be sure i changed it.
local link_to = vim.loop.fs_realpath(absolute_path) local link_to = vim.loop.fs_realpath(absolute_path)
local open, nodes, has_children local open, nodes, has_children
@@ -84,7 +87,7 @@ function M.link(parent, absolute_path, name)
local node = { local node = {
type = "link", type = "link",
absolute_path = absolute_path, absolute_path = absolute_path,
fs_stat = vim.loop.fs_stat(absolute_path), fs_stat = fs_stat,
group_next = nil, -- If node is grouped, this points to the next child dir/link node group_next = nil, -- If node is grouped, this points to the next child dir/link node
has_children = has_children, has_children = has_children,
link_to = link_to, link_to = link_to,

View File

@@ -29,7 +29,7 @@ end
---@param absolute_path string ---@param absolute_path string
---@return GitStatus ---@return GitStatus
local function get_git_status(parent_ignored, status, absolute_path) local function get_git_status(parent_ignored, status, absolute_path)
local file_status = parent_ignored and "!!" or status.files and status.files[absolute_path] local file_status = parent_ignored and "!!" or (status and status.files and status.files[absolute_path])
return { file = file_status } return { file = file_status }
end end

View File

@@ -85,9 +85,10 @@ function M.reload(node, git_status)
node.group_next = nil node.group_next = nil
end end
local child_names = {} local remain_childs = {}
local node_ignored = explorer_node.is_git_ignored(node) local node_ignored = explorer_node.is_git_ignored(node)
---@type table<string, Node>
local nodes_by_path = utils.key_by(node.nodes, "absolute_path") local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
while true do while true do
local name, t = vim.loop.fs_scandir_next(handle, cwd) local name, t = vim.loop.fs_scandir_next(handle, cwd)
@@ -95,20 +96,12 @@ function M.reload(node, git_status)
break break
end end
local stat
local function fs_stat_cached(path)
if stat ~= nil then
return stat
end
stat = vim.loop.fs_stat(path)
return stat
end
local abs = utils.path_join { cwd, name } local abs = utils.path_join { cwd, name }
t = t or (fs_stat_cached(abs) or {}).type ---@type uv.fs_stat.result|nil
if not filters.should_filter(abs, filter_status) then local stat = vim.loop.fs_stat(abs)
child_names[abs] = true
if not filters.should_filter(abs, stat, filter_status) then
remain_childs[abs] = true
-- Recreate node if type changes. -- Recreate node if type changes.
if nodes_by_path[abs] then if nodes_by_path[abs] then
@@ -122,26 +115,26 @@ function M.reload(node, git_status)
end end
if not nodes_by_path[abs] then if not nodes_by_path[abs] then
local new_child = nil
if t == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then if t == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
local folder = builders.folder(node, abs, name) new_child = builders.folder(node, abs, name, stat)
nodes_by_path[abs] = folder
table.insert(node.nodes, folder)
elseif t == "file" then elseif t == "file" then
local file = builders.file(node, abs, name) new_child = builders.file(node, abs, name, stat)
nodes_by_path[abs] = file
table.insert(node.nodes, file)
elseif t == "link" then elseif t == "link" then
local link = builders.link(node, abs, name) local link = builders.link(node, abs, name, stat)
if link.link_to ~= nil then if link.link_to ~= nil then
nodes_by_path[abs] = link new_child = link
table.insert(node.nodes, link)
end end
end end
if new_child then
table.insert(node.nodes, new_child)
nodes_by_path[abs] = new_child
end
else else
local n = nodes_by_path[abs] local n = nodes_by_path[abs]
if n then if n then
n.executable = builders.is_executable(abs) n.executable = builders.is_executable(abs) or false
n.fs_stat = fs_stat_cached(abs) n.fs_stat = stat
end end
end end
end end
@@ -150,8 +143,8 @@ function M.reload(node, git_status)
node.nodes = vim.tbl_map( node.nodes = vim.tbl_map(
update_status(nodes_by_path, node_ignored, git_status), update_status(nodes_by_path, node_ignored, git_status),
vim.tbl_filter(function(n) vim.tbl_filter(function(n)
if child_names[n.absolute_path] then if remain_childs[n.absolute_path] then
return child_names[n.absolute_path] return remain_childs[n.absolute_path]
else else
explorer_node.node_destroy(n) explorer_node.node_destroy(n)
return false return false

View File

@@ -52,6 +52,14 @@ local function refactored(opts)
if type(opts.renderer) == "table" and type(opts.renderer.highlight_git) == "boolean" then if type(opts.renderer) == "table" and type(opts.renderer.highlight_git) == "boolean" then
opts.renderer.highlight_git = opts.renderer.highlight_git and "name" or "none" opts.renderer.highlight_git = opts.renderer.highlight_git and "name" or "none"
end end
-- 2024/02/15
if type(opts.update_focused_file) == "table" then
if type(opts.update_focused_file.update_root) ~= "table" then
opts.update_focused_file.update_root = { enable = opts.update_focused_file.update_root }
end
end
utils.move_missing_val(opts, "update_focused_file", "ignore_list", opts, "update_focused_file.update_root", "ignore_list", true)
end end
local function deprecated(opts) local function deprecated(opts)

View File

@@ -57,6 +57,7 @@ local function clone_node(node)
name = node.name, name = node.name,
open = node.open, open = node.open,
type = node.type, type = node.type,
fs_stat = node.fs_stat,
} }
if type(node.nodes) == "table" then if type(node.nodes) == "table" then

View File

@@ -6,7 +6,7 @@
---@class BaseNode ---@class BaseNode
---@field absolute_path string ---@field absolute_path string
---@field executable boolean ---@field executable boolean
---@field fs_stat uv.uv_fs_t ---@field fs_stat uv.fs_stat.result|nil
---@field git_status GitStatus|nil ---@field git_status GitStatus|nil
---@field hidden boolean ---@field hidden boolean
---@field name string ---@field name string

View File

@@ -422,11 +422,21 @@ function Builder:build_header()
end end
end end
---Sanitize lines for rendering.
---Replace newlines with literal \n
---@private
function Builder:sanitize_lines()
self.lines = vim.tbl_map(function(line)
return line and line:gsub("\n", "\\n") or ""
end, self.lines)
end
---Build all lines with highlights and signs ---Build all lines with highlights and signs
---@return Builder ---@return Builder
function Builder:build() function Builder:build()
self:build_header() self:build_header()
self:build_lines() self:build_lines()
self:sanitize_lines()
return self return self
end end

View File

@@ -64,10 +64,15 @@ local function get_file_icon_webdev(fname, extension)
elseif string.match(extension, "%.(.*)") then elseif string.match(extension, "%.(.*)") then
-- If there are more extensions to the file, try to grab the icon for them recursively -- If there are more extensions to the file, try to grab the icon for them recursively
return get_file_icon_webdev(fname, string.match(extension, "%.(.*)")) return get_file_icon_webdev(fname, string.match(extension, "%.(.*)"))
else
local devicons_default = M.devicons.get_default_icon()
if devicons_default and type(devicons_default.icon) == "string" and type(devicons_default.name) == "string" then
return devicons_default.icon, "DevIcon" .. devicons_default.name
else else
return get_file_icon_default() return get_file_icon_default()
end end
end end
end
local function config_file_icon() local function config_file_icon()
if M.config.show.file then if M.config.show.file then