Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 6 additions & 0 deletions doc/nvim-tree-lua.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,11 @@ Config: experimental *nvim-tree-config-experimental*
In the event of a problem please disable the experiment and raise an
issue.

Fields: ~
• {session_restore_nvim}? (`boolean`, default: `false`) Restore
nvim-tree buffers when restoring vim sessions
(requires 0.13+).

==============================================================================
Config: log *nvim-tree-config-log*

Expand Down Expand Up @@ -2216,6 +2221,7 @@ Following is the default configuration, see |nvim_tree.config| for details. >lua
persist = false,
},
experimental = {
session_restore_nvim = false
},
log = {
enable = false,
Expand Down
3 changes: 3 additions & 0 deletions lua/nvim-tree/_meta/config/experimental.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ error("Cannot require a meta file")
---
---@class nvim_tree.config.experimental
---
---Restore nvim-tree buffers when restoring vim sessions (requires 0.13+).
---(default: `false`)
---@field session_restore_nvim? boolean
--Example below for future reference:
--
--Buffers opened by nvim-tree will use with relative paths instead of absolute.
Expand Down
22 changes: 22 additions & 0 deletions lua/nvim-tree/autocmd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ function M.global()
end,
})

if vim.fn.has("nvim-0.13") == 1 and config.g.experimental.session_restore_nvim then
vim.api.nvim_create_autocmd("SessionWritePre", { ---@diagnostic disable-line: param-type-mismatch
group = augroup_id,
callback = function()
-- We must schedule to wait until `vim.v.this_session` is properly updated
-- This prevents overwriting the data if a new session is created with a new `:mksession`
vim.schedule(require("nvim-tree.session").save)
end,
})

vim.api.nvim_create_autocmd("SessionLoadPost", {
group = augroup_id,
-- Workaround: this event fires multiple times (report upstream),
-- failure notifications could show up many times.
once = true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems to work nicely; I only get messages once and sessions loaded correctly.

If we run into problems, say, execution on the first event is too early, we could use the debouncer instead, for delayed one-shot e.g.

    utils.debounce("DiagnosticChanged redraw", config.g.diagnostics.debounce_delay, function()
      local profile_redraw = log.profile_start("DiagnosticChanged redraw")

      local explorer = core.get_explorer()
      if explorer then
        explorer.renderer:draw()
      end

      log.profile_end(profile_redraw)
    end)

callback = function()
require("nvim-tree.session").restore()
end,
})
end


if config.g.tab.sync.open then
vim.api.nvim_create_autocmd("TabEnter", {
group = augroup_id,
Expand Down
1 change: 1 addition & 0 deletions lua/nvim-tree/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ M.d = { -- config-default-start
persist = false,
},
experimental = {
session_restore_nvim = false
},
log = {
enable = false,
Expand Down
103 changes: 103 additions & 0 deletions lua/nvim-tree/session.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
local notify = require("nvim-tree.notify")

local M = {}

local storepath = vim.fn.stdpath("data") .. "/nvim-tree-session.json"

local function decode_session_file()
local file = io.open(storepath, "r")

if file then
local content = file:read("*all")
file:close()
if content and content ~= "" then
return vim.json.decode(content)
end
end
-- Notifying on failure could be noisy, as it'd happen if the file simply did not exist
end

local function write_session_file(session_file_content)
local file, errmsg = io.open(storepath, "w")

if file then
file:write(vim.json.encode(session_file_content))
file:close()
else
notify.warn(string.format("Invalid session file (%s): %s", storepath, errmsg))
end
end

function M.save()
-- We either always save the session data, which ends up populating the storepath with empty data
-- Or we gate by the tree's visibility, which could create "out of sync" scenario:
-- if the tree is initially open and the session is saved, it won't be overridden later,
-- if the tree is no longer visible. However, when restoring, this is harmless.
if require("nvim-tree.api").tree.is_visible({ any_tabpage = true }) then
local ok, session_file_content = pcall(decode_session_file)

if not ok then
notify.warn(string.format("Failed to decode session file (%s): %s", storepath, session_file_content))
return
end

-- might be nil at this point
session_file_content = session_file_content or {}

local cwd = require("nvim-tree.core").get_cwd()

session_file_content[vim.v.this_session] = { cwd = cwd }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like using the session path as a key - minimise any ambiguities.

Test: Multiple sessions in one file are correctly loaded and the correct session is updated.


local ok2, err = pcall(write_session_file, session_file_content)

if not ok2 then
notify.warn(string.format("Failed to write session file (%s): %s", storepath, err))
end
end
end

function M.restore()
local api = require("nvim-tree.api").tree

---@type table<integer,boolean>
local tabs = {}

local ok, session_file_content = pcall(decode_session_file)

-- Failing to restore cwd is not fatal, we can continue
if not ok then
notify.warn(string.format("Failed to decode session file (%s): %s", storepath, session_file_content))
end

local path = session_file_content and session_file_content[vim.v.this_session] and session_file_content[vim.v.this_session].cwd or nil

-- Save tabs with leftover nvim-tree buffers
for _, tab in ipairs(vim.api.nvim_list_tabpages()) do
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tab)) do
local buf = vim.api.nvim_win_get_buf(win)
if api.is_tree_buf(buf) then
tabs[tab] = true
end
end
end

-- The new window may inherit options from the current one if we don't schedule
vim.schedule(function()
api.close_in_all_tabs()

for tab, _ in pairs(tabs) do
if vim.api.nvim_tabpage_is_valid(tab) then
local win = vim.api.nvim_tabpage_get_win(tab)

-- Invoke `open` from each tab's current window to prevent having to switch tabs
if vim.api.nvim_win_is_valid(win) then
vim.api.nvim_win_call(win, function()
api.open({ path = path })
end)
end
end
end
end)
end

return M
Loading