From 51d262b488a91c9016aaefc7fd563cd945005b41 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 22 Dec 2024 14:23:47 +0800 Subject: [PATCH 01/13] Support Persist Bookmarks --- lua/nvim-tree/marks/init.lua | 77 +++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index c940f999983..0c00bcd3e35 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -10,6 +10,7 @@ local utils = require("nvim-tree.utils") local Class = require("nvim-tree.classic") local DirectoryNode = require("nvim-tree.node.directory") +local json = vim.json ---@class (exact) Marks: Class ---@field private explorer Explorer @@ -22,14 +23,6 @@ local Marks = Class:extend() ---@class (exact) MarksArgs ---@field explorer Explorer ----@protected ----@param args MarksArgs -function Marks:new(args) - self.explorer = args.explorer - - self.marks = {} -end - ---Clear all marks and reload if watchers disabled ---@private function Marks:clear_reload() @@ -46,22 +39,6 @@ function Marks:clear() self.explorer.renderer:draw() end ----@public ----@param node Node -function Marks:toggle(node) - if node.absolute_path == nil then - return - end - - if self:get(node) then - self.marks[node.absolute_path] = nil - else - self.marks[node.absolute_path] = node - end - - self.explorer.renderer:draw() -end - ---Return node if marked ---@public ---@param node Node @@ -243,6 +220,58 @@ function Marks:navigate_next() self:navigate(false) end +-- local json = vim.json + +local function save_bookmarks(marks) + local path = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" + local file = io.open(path, "w") + if file then + local data = {} + for path, _ in pairs(marks) do + table.insert(data, path) + end + file:write(vim.fn.json_encode(data)) + file:close() + end +end + +local function load_bookmarks() + local path = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" + local file = io.open(path, "r") + if file then + local content = file:read("*all") + file:close() + if content and content ~= "" then + local data = vim.fn.json_decode(content) + local marks = {} + for _, path in ipairs(data) do + marks[path] = true -- or reconstruct node if needed + end + return marks + end + end + return {} +end + +function Marks:new(args) + self.explorer = args.explorer + self.marks = load_bookmarks() or {} +end + +function Marks:toggle(node) + if node.absolute_path == nil then + return + end + + if self:get(node) then + self.marks[node.absolute_path] = nil + else + self.marks[node.absolute_path] = node + end + + save_bookmarks(self.marks) + self.explorer.renderer:draw() +end ---Prompts for selection of a marked node, sorted by absolute paths. ---A folder will be focused, a file will be opened. ---@public From 6ec9fc13ffcd22a4d456929b4b14c0fb4954e0f9 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 22 Dec 2024 14:44:44 +0800 Subject: [PATCH 02/13] Fix node new open error --- lua/nvim-tree/explorer/filters.lua | 4 +++- lua/nvim-tree/marks/init.lua | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lua/nvim-tree/explorer/filters.lua b/lua/nvim-tree/explorer/filters.lua index 62230687e40..e9b3f2c7d0e 100644 --- a/lua/nvim-tree/explorer/filters.lua +++ b/lua/nvim-tree/explorer/filters.lua @@ -209,7 +209,9 @@ function Filters:prepare(project) local explorer = require("nvim-tree.core").get_explorer() if explorer then for _, node in pairs(explorer.marks:list()) do - status.bookmarks[node.absolute_path] = node.type + if type(node) == "table" then + status.bookmarks[node.absolute_path] = node.type + end end end diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 0c00bcd3e35..5f05d272fdc 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -223,8 +223,8 @@ end -- local json = vim.json local function save_bookmarks(marks) - local path = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" - local file = io.open(path, "w") + local storepath = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" + local file = io.open(storepath, "w") if file then local data = {} for path, _ in pairs(marks) do @@ -236,8 +236,8 @@ local function save_bookmarks(marks) end local function load_bookmarks() - local path = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" - local file = io.open(path, "r") + local storepath = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" + local file = io.open(storepath, "r") if file then local content = file:read("*all") file:close() From 318f0ba1cf84933037a5c87b3dd61aaf27c73d8a Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 22 Dec 2024 15:12:54 +0800 Subject: [PATCH 03/13] Fix filter marks index issue --- lua/nvim-tree/explorer/filters.lua | 6 ++---- lua/nvim-tree/marks/init.lua | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lua/nvim-tree/explorer/filters.lua b/lua/nvim-tree/explorer/filters.lua index e9b3f2c7d0e..5518f31b43e 100644 --- a/lua/nvim-tree/explorer/filters.lua +++ b/lua/nvim-tree/explorer/filters.lua @@ -208,10 +208,8 @@ function Filters:prepare(project) local explorer = require("nvim-tree.core").get_explorer() if explorer then - for _, node in pairs(explorer.marks:list()) do - if type(node) == "table" then - status.bookmarks[node.absolute_path] = node.type - end + for key, node in pairs(explorer.marks.marks) do + status.bookmarks[key] = node end end diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 5f05d272fdc..930b4c85797 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -10,7 +10,6 @@ local utils = require("nvim-tree.utils") local Class = require("nvim-tree.classic") local DirectoryNode = require("nvim-tree.node.directory") -local json = vim.json ---@class (exact) Marks: Class ---@field private explorer Explorer @@ -220,7 +219,6 @@ function Marks:navigate_next() self:navigate(false) end --- local json = vim.json local function save_bookmarks(marks) local storepath = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" From 464930d333230a4d62b6be0445103744fe796a95 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 16 Feb 2025 17:37:32 +0800 Subject: [PATCH 04/13] Made saving/loading conditional on enable_persistence, --- lua/nvim-tree.lua | 6 +- lua/nvim-tree/marks/init.lua | 113 +++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 7bf6891c131..a67aa0b91a8 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -511,7 +511,11 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS watcher = false, }, }, -} -- END_DEFAULT_OPTS + marks = { + enable_persistence = false, + save_path = nil, -- nil will default to stdpath("data") .. "/nvim-tree-bookmarks.json" + }, +}-- END_DEFAULT_OPTS local function merge_options(conf) return vim.tbl_deep_extend("force", DEFAULT_OPTS, conf or {}) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 930b4c85797..3604d1e6f03 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -11,6 +11,49 @@ local utils = require("nvim-tree.utils") local Class = require("nvim-tree.classic") local DirectoryNode = require("nvim-tree.node.directory") +local function get_save_path(opts) + return opts.marks.save_path or (vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json") +end + +local function save_bookmarks(marks, opts) + if not opts.marks.enable_persistence then + return + end + + local storepath = get_save_path(opts) + local file = io.open(storepath, "w") + if file then + local data = {} + for path, _ in pairs(marks) do + table.insert(data, path) + end + print(storepath) + file:write(vim.json.encode(data)) + file:close() + end +end + +local function load_bookmarks(opts) + if not opts.marks.enable_persistence then + return {} + end + local storepath = get_save_path(opts) + local file = io.open(storepath, "r") + if file then + local content = file:read("*all") + file:close() + if content and content ~= "" then + local data = vim.json.decode(content) + local marks = {} + for _, path in ipairs(data) do + marks[path] = true -- or reconstruct node if needed + end + return marks + end + end + return {} +end + ---@class (exact) Marks: Class ---@field private explorer Explorer ---@field private marks table by absolute path @@ -22,6 +65,11 @@ local Marks = Class:extend() ---@class (exact) MarksArgs ---@field explorer Explorer +function Marks:new(args) + self.explorer = args.explorer + self.marks = load_bookmarks(self.explorer.opts) or {} +end + ---Clear all marks and reload if watchers disabled ---@private function Marks:clear_reload() @@ -38,6 +86,20 @@ function Marks:clear() self.explorer.renderer:draw() end +function Marks:toggle(node) + if node.absolute_path == nil then + return + end + + if self:get(node) then + self.marks[node.absolute_path] = nil + else + self.marks[node.absolute_path] = node + end + save_bookmarks(self.marks, self.explorer.opts) + self.explorer.renderer:draw() +end + ---Return node if marked ---@public ---@param node Node @@ -219,57 +281,6 @@ function Marks:navigate_next() self:navigate(false) end - -local function save_bookmarks(marks) - local storepath = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" - local file = io.open(storepath, "w") - if file then - local data = {} - for path, _ in pairs(marks) do - table.insert(data, path) - end - file:write(vim.fn.json_encode(data)) - file:close() - end -end - -local function load_bookmarks() - local storepath = vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" - local file = io.open(storepath, "r") - if file then - local content = file:read("*all") - file:close() - if content and content ~= "" then - local data = vim.fn.json_decode(content) - local marks = {} - for _, path in ipairs(data) do - marks[path] = true -- or reconstruct node if needed - end - return marks - end - end - return {} -end - -function Marks:new(args) - self.explorer = args.explorer - self.marks = load_bookmarks() or {} -end - -function Marks:toggle(node) - if node.absolute_path == nil then - return - end - - if self:get(node) then - self.marks[node.absolute_path] = nil - else - self.marks[node.absolute_path] = node - end - - save_bookmarks(self.marks) - self.explorer.renderer:draw() -end ---Prompts for selection of a marked node, sorted by absolute paths. ---A folder will be focused, a file will be opened. ---@public From fbfa65b5bea21eb82bc2887b8f76e6c876a1f0d0 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 16 Feb 2025 17:42:40 +0800 Subject: [PATCH 05/13] add method comments --- lua/nvim-tree/marks/init.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 3604d1e6f03..9f86d706be9 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -65,6 +65,8 @@ local Marks = Class:extend() ---@class (exact) MarksArgs ---@field explorer Explorer +---@protected +---@param args MarksArgs function Marks:new(args) self.explorer = args.explorer self.marks = load_bookmarks(self.explorer.opts) or {} @@ -86,6 +88,8 @@ function Marks:clear() self.explorer.renderer:draw() end +---@public +---@param node Node function Marks:toggle(node) if node.absolute_path == nil then return From 69f5a6342476e22021c4184069d62c22654ee9e1 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 16 Feb 2025 17:57:02 +0800 Subject: [PATCH 06/13] Use a pcall to prevent an error being raised --- lua/nvim-tree/marks/init.lua | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 9f86d706be9..3225d4d15fd 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -27,16 +27,12 @@ local function save_bookmarks(marks, opts) for path, _ in pairs(marks) do table.insert(data, path) end - print(storepath) file:write(vim.json.encode(data)) file:close() end end local function load_bookmarks(opts) - if not opts.marks.enable_persistence then - return {} - end local storepath = get_save_path(opts) local file = io.open(storepath, "r") if file then @@ -69,7 +65,15 @@ local Marks = Class:extend() ---@param args MarksArgs function Marks:new(args) self.explorer = args.explorer - self.marks = load_bookmarks(self.explorer.opts) or {} + self.marks = {} + if self.explorer.opts.marks.enable_persistence then + local ok, loaded_marks = pcall(load_bookmarks, self.explorer.opts) + if ok then + self.marks = loaded_marks + else + notify.warn("Failed to load bookmarks: " .. loaded_marks) + end + end end ---Clear all marks and reload if watchers disabled @@ -100,7 +104,13 @@ function Marks:toggle(node) else self.marks[node.absolute_path] = node end - save_bookmarks(self.marks, self.explorer.opts) + + if self.explorer.opts.marks.enable_persistence then + local ok, err = pcall(save_bookmarks, self.marks, self.explorer.opts) + if not ok then + notify.warn("Failed to save bookmarks: " .. err) + end + end self.explorer.renderer:draw() end From 27e794a4472efc735aeb024c6a2599d36130250d Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:15:17 +0800 Subject: [PATCH 07/13] Print errmsg and disable persistence on a bad path Co-authored-by: Alexander Courtis --- lua/nvim-tree/marks/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 8e3f3dbb3fd..4afaab27caa 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -30,6 +30,9 @@ local function save_bookmarks(marks, opts) file:write(vim.json.encode(data)) file:close() end +else + notify.warn("Invalid marks.save_path, disabling persistence: " .. errmsg) + opts.marks.enable_persistence = false end local function load_bookmarks(opts) From 4343b8b113a1e4644d9e554cec2e18778a70a14b Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:15:04 +0800 Subject: [PATCH 08/13] fix: use public list() method instead of accessing private marks field --- lua/nvim-tree/explorer/filters.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/nvim-tree/explorer/filters.lua b/lua/nvim-tree/explorer/filters.lua index c9d28b9cc30..e2e4b151268 100644 --- a/lua/nvim-tree/explorer/filters.lua +++ b/lua/nvim-tree/explorer/filters.lua @@ -208,8 +208,8 @@ function Filters:prepare(project) local explorer = require("nvim-tree.core").get_explorer() if explorer then - for key, node in pairs(explorer.marks.marks) do - status.bookmarks[key] = node + for _, node in ipairs(explorer.marks:list()) do + status.bookmarks[node.absolute_path] = node end end From c7dd016bfdba7c31022cc5bf376f8795d155d4ea Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:34:11 +0800 Subject: [PATCH 09/13] refactor: rename marks config to bookmarks and simplify persistence API - Rename opts.marks to opts.bookmarks for better clarity - Simplify persist option to boolean | string (true uses default path) - Implement lazy node resolution for loaded bookmarks - Update documentation with new bookmarks configuration - Improve bulk operations to use public list() method consistently --- doc/nvim-tree-lua.txt | 19 +++++++-- lua/nvim-tree.lua | 10 +++-- lua/nvim-tree/marks/init.lua | 75 ++++++++++++++++++++++++++++-------- 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 6aadaf3dc27..abf03db7534 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -32,8 +32,9 @@ CONTENTS *nvim-tree* 5.16 Opts: Notify |nvim-tree-opts-notify| 5.17 Opts: Help |nvim-tree-opts-help| 5.18 Opts: UI |nvim-tree-opts-ui| - 5.19 Opts: Experimental |nvim-tree-opts-experimental| - 5.20 Opts: Log |nvim-tree-opts-log| + 5.19 Opts: Bookmarks |nvim-tree-opts-bookmarks| + 5.20 Opts: Experimental |nvim-tree-opts-experimental| + 5.21 Opts: Log |nvim-tree-opts-log| 6. API |nvim-tree-api| 6.1 API Tree |nvim-tree-api.tree| 6.2 API File System |nvim-tree-api.fs| @@ -1657,14 +1658,24 @@ Confirmation prompts. Type: `boolean`, Default: `false` ============================================================================== - 5.19 OPTS: EXPERIMENTAL *nvim-tree-opts-experimental* + 5.19 OPTS: BOOKMARKS *nvim-tree-opts-bookmarks* + +*nvim-tree.bookmarks.persist* +Persist bookmarks to a json file containing a list of absolute paths. +Type: `boolean` | `string`, Default: `false` + +`true`: use default: `stdpath("data") .. "/nvim-tree-bookmarks.json"` +`string`: absolute path of your choice. + +============================================================================== + 5.20 OPTS: EXPERIMENTAL *nvim-tree-opts-experimental* *nvim-tree.experimental* Experimental features that may become default or optional functionality. In the event of a problem please disable the experiment and raise an issue. ============================================================================== - 5.20 OPTS: LOG *nvim-tree-opts-log* + 5.21 OPTS: LOG *nvim-tree-opts-log* Configuration for diagnostic logging. diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 3c93b03db0c..9fe579ccdfe 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -514,6 +514,9 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS default_yes = false, }, }, + bookmarks = { + persist = false, + }, experimental = { }, log = { @@ -530,10 +533,6 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS watcher = false, }, }, - marks = { - enable_persistence = false, - save_path = nil, -- nil will default to stdpath("data") .. "/nvim-tree-bookmarks.json" - }, }-- END_DEFAULT_OPTS local function merge_options(conf) @@ -585,6 +584,9 @@ local ACCEPTED_TYPES = { }, }, }, + bookmarks = { + persist = { "boolean", "string" }, + }, } local ACCEPTED_STRINGS = { diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 4afaab27caa..7d4eed5a97a 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -12,11 +12,15 @@ local Class = require("nvim-tree.classic") local DirectoryNode = require("nvim-tree.node.directory") local function get_save_path(opts) - return opts.marks.save_path or (vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json") + if type(opts.bookmarks.persist) == "string" then + return opts.bookmarks.persist + else + return vim.fn.stdpath("data") .. "/nvim-tree-bookmarks.json" + end end local function save_bookmarks(marks, opts) - if not opts.marks.enable_persistence then + if not opts.bookmarks.persist then return end @@ -30,9 +34,6 @@ local function save_bookmarks(marks, opts) file:write(vim.json.encode(data)) file:close() end -else - notify.warn("Invalid marks.save_path, disabling persistence: " .. errmsg) - opts.marks.enable_persistence = false end local function load_bookmarks(opts) @@ -45,7 +46,8 @@ local function load_bookmarks(opts) local data = vim.json.decode(content) local marks = {} for _, path in ipairs(data) do - marks[path] = true -- or reconstruct node if needed + -- Store as boolean initially; will be lazily resolved to node on first access + marks[path] = true end return marks end @@ -69,7 +71,7 @@ local Marks = Class:extend() function Marks:new(args) self.explorer = args.explorer self.marks = {} - if self.explorer.opts.marks.enable_persistence then + if self.explorer.opts.bookmarks.persist then local ok, loaded_marks = pcall(load_bookmarks, self.explorer.opts) if ok then self.marks = loaded_marks @@ -108,7 +110,7 @@ function Marks:toggle(node) self.marks[node.absolute_path] = node end - if self.explorer.opts.marks.enable_persistence then + if self.explorer.opts.bookmarks.persist then local ok, err = pcall(save_bookmarks, self.marks, self.explorer.opts) if not ok then notify.warn("Failed to save bookmarks: " .. err) @@ -122,7 +124,23 @@ end ---@param node Node ---@return Node|nil function Marks:get(node) - return node and self.marks[node.absolute_path] + if not node or not node.absolute_path then + return nil + end + + local mark = self.marks[node.absolute_path] + if mark == true then + -- Lazy resolve: try to find node in explorer tree + local resolved_node = self.explorer:get_node_from_path(node.absolute_path) + if resolved_node then + -- Cache the resolved node + self.marks[node.absolute_path] = resolved_node + return resolved_node + end + return nil + end + + return mark end ---List marked nodes @@ -130,8 +148,24 @@ end ---@return Node[] function Marks:list() local list = {} - for _, node in pairs(self.marks) do - table.insert(list, node) + for path, mark in pairs(self.marks) do + local node + if mark == true then + -- Lazy resolve: try to find node in explorer tree + node = self.explorer:get_node_from_path(path) + if node then + -- Cache the resolved node for future access + self.marks[path] = node + end + -- If node not found (file deleted/moved), skip it silently + else + -- Already a node object + node = mark + end + + if node then + table.insert(list, node) + end end return list end @@ -145,7 +179,7 @@ function Marks:bulk_delete() end local function execute() - for _, node in pairs(self.marks) do + for _, node in ipairs(self:list()) do remove_file.remove(node) end self:clear_reload() @@ -174,7 +208,7 @@ function Marks:bulk_trash() end local function execute() - for _, node in pairs(self.marks) do + for _, node in ipairs(self:list()) do trash.remove(node) end self:clear_reload() @@ -227,7 +261,7 @@ function Marks:bulk_move() return end - for _, node in pairs(self.marks) do + for _, node in ipairs(self:list()) do local head = vim.fn.fnamemodify(node.absolute_path, ":t") local to = utils.path_join({ location, head }) rename_file.rename(node, to) @@ -314,7 +348,18 @@ function Marks:navigate_select() if not choice or choice == "" then return end - local node = self.marks[choice] + local mark = self.marks[choice] + local node + if mark == true then + -- Lazy resolve + node = self.explorer:get_node_from_path(choice) + if node then + self.marks[choice] = node + end + else + node = mark + end + if node and not node:is(DirectoryNode) and not utils.get_win_buf_from_path(node.absolute_path) then open_file.fn("edit", node.absolute_path) elseif node then From d65dabb0dbd7336f00afd79d5c687e3a4429ea2f Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:34:11 +0800 Subject: [PATCH 10/13] refactor: rename marks config to bookmarks and simplify persistence API - Rename opts.marks to opts.bookmarks for better clarity - Simplify persist option to boolean | string (true uses default path) - Implement lazy node resolution for loaded bookmarks - Update documentation with new bookmarks configuration - Improve bulk operations to use public list() method consistently --- doc/nvim-tree-lua.txt | 4 ++++ lua/nvim-tree/marks/init.lua | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index abf03db7534..a89f9ed3415 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -640,6 +640,9 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua default_yes = false, }, }, + bookmarks = { + persist = false, + }, experimental = { }, log = { @@ -3200,6 +3203,7 @@ highlight group is not, hard linking as follows: > |nvim-tree.actions.remove_file.close_window| |nvim-tree.actions.use_system_clipboard| |nvim-tree.auto_reload_on_write| +|nvim-tree.bookmarks.persist| |nvim-tree.diagnostics.debounce_delay| |nvim-tree.diagnostics.diagnostic_opts| |nvim-tree.diagnostics.enable| diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 7d4eed5a97a..2dec8338507 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -127,7 +127,6 @@ function Marks:get(node) if not node or not node.absolute_path then return nil end - local mark = self.marks[node.absolute_path] if mark == true then -- Lazy resolve: try to find node in explorer tree @@ -139,7 +138,6 @@ function Marks:get(node) end return nil end - return mark end @@ -162,7 +160,6 @@ function Marks:list() -- Already a node object node = mark end - if node then table.insert(list, node) end @@ -359,7 +356,6 @@ function Marks:navigate_select() else node = mark end - if node and not node:is(DirectoryNode) and not utils.get_win_buf_from_path(node.absolute_path) then open_file.fn("edit", node.absolute_path) elseif node then From 2d456ccc2fe1f9cbf0c7371848e7b42cf535aea1 Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:42:00 +0800 Subject: [PATCH 11/13] add errmsg in return Co-authored-by: Alexander Courtis --- lua/nvim-tree/marks/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 2dec8338507..8c21688258d 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -25,7 +25,7 @@ local function save_bookmarks(marks, opts) end local storepath = get_save_path(opts) - local file = io.open(storepath, "w") + local file, errmsg = io.open(storepath, "w") if file then local data = {} for path, _ in pairs(marks) do From f344ae9b7a093627f5d5211ad89c60ba72576352 Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:41:31 +0800 Subject: [PATCH 12/13] fix: correct indentation in filters.lua to match codestyle --- lua/nvim-tree/explorer/filters.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/nvim-tree/explorer/filters.lua b/lua/nvim-tree/explorer/filters.lua index e2e4b151268..e26739939dc 100644 --- a/lua/nvim-tree/explorer/filters.lua +++ b/lua/nvim-tree/explorer/filters.lua @@ -209,7 +209,7 @@ function Filters:prepare(project) local explorer = require("nvim-tree.core").get_explorer() if explorer then for _, node in ipairs(explorer.marks:list()) do - status.bookmarks[node.absolute_path] = node + status.bookmarks[node.absolute_path] = node end end From ddd23b99482dd60b1dae26d2b50176e9da55ad59 Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 5 Jan 2026 13:44:54 +0800 Subject: [PATCH 13/13] fix: add missing else block to handle bookmark save errors and use errmsg variable --- lua/nvim-tree/marks/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lua/nvim-tree/marks/init.lua b/lua/nvim-tree/marks/init.lua index 8c21688258d..739c35e92ce 100644 --- a/lua/nvim-tree/marks/init.lua +++ b/lua/nvim-tree/marks/init.lua @@ -33,6 +33,9 @@ local function save_bookmarks(marks, opts) end file:write(vim.json.encode(data)) file:close() + else + notify.warn("Invalid bookmarks.save_path, disabling persistence: " .. errmsg) + opts.bookmarks.persist = false end end