Skip to content

feat(#1851): persist bookmarks #3033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lua/nvim-tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Comment on lines +514 to +517
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
marks = {
enable_persistence = false,
save_path = nil, -- nil will default to stdpath("data") .. "/nvim-tree-bookmarks.json"
},
bookmarks = {
persist = false,
},

Rather than a toggle and path, we can use just one option that is boolean or string. This is consistent with other options.

See proposed help

We then add the type explitictly so that it may be validated. Add to ACCEPTED_TYPES:

  bookmarks = {
    persist = { "boolean", "string" },
  },

}-- END_DEFAULT_OPTS

local function merge_options(conf)
return vim.tbl_deep_extend("force", DEFAULT_OPTS, conf or {})
Expand Down
4 changes: 2 additions & 2 deletions lua/nvim-tree/explorer/filters.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +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
status.bookmarks[node.absolute_path] = node.type
for key, node in pairs(explorer.marks.marks) do
Copy link
Member

@alex-courtis alex-courtis Feb 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is private and should not be accessed, see CI failure

Please use Marks:list()

I know this is not your code, but we can clean this up by using self.explorer (guaranteed not null) instead of calling core.

Edit: it appears that self.explorer.marks:list() is now returning an array of booleans instead of an array of nodes.

status.bookmarks[key] = node
end
end

Expand Down
54 changes: 53 additions & 1 deletion lua/nvim-tree/marks/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,45 @@ 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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
local file = io.open(storepath, "w")
local file, errmsg = io.open(storepath, "w")

if file then
local data = {}
for path, _ in pairs(marks) do
table.insert(data, path)
end
file:write(vim.json.encode(data))
file:close()
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
end
end
else
notify.warn("Invalid marks.save_path, disabling persistence: " .. errmsg)
opts.marks.enable_persistence = false

Print errmsg and disable persistence on a bad path, permission etc.

[NvimTree] Invalid marks.save_path, disabling persistence: /home/alex/marks.json: Permission denied

[NvimTree] Invalid marks.save_path, disabling persistence: /home/alexxxxxxxxxxxxxxxxxxxx/mar ks.json: No such file or directory

end

local function load_bookmarks(opts)
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<string, Node> by absolute path
Expand All @@ -26,8 +65,15 @@ local Marks = Class:extend()
---@param args MarksArgs
function Marks:new(args)
self.explorer = args.explorer

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
Expand Down Expand Up @@ -59,6 +105,12 @@ function Marks:toggle(node)
self.marks[node.absolute_path] = node
end

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

Expand Down
Loading