Skip to content
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

File tree integration with nvim-tree and NERDTree #179

Merged
merged 4 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
64 changes: 40 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ Auto Session takes advantage of Neovim's existing session management capabilitie
:warning: Please note that if there are errors in your config, restoring the session might fail, if that happens, auto session will then disable auto saving for the current session.
Manually saving a session can still be done by calling `:SaveSession`.

AutoSession now tracks `cwd` changes!
zwhitchcox marked this conversation as resolved.
Show resolved Hide resolved
By default, handling is as follows:
DirChangedPre (before the cwd actually changes):
- Save the current session
- Clear all buffers `%bd!`. This guarantees buffers don't bleed to the
next session.
- Clear jumps. Also done so there is no bleading between sessions.
- Run the `pre_cwd_changed_hook`
DirChanged (after the cwd has changed):
- Restore session using new cwd
- Run the `post_cwd_changed_hook`
AutoSession now tracks `cwd` changes!
By default, handling is as follows:
DirChangedPre (before the cwd actually changes):
- Save the current session
- Clear all buffers `%bd!`. This guarantees buffers don't bleed to the
next session.
- Clear jumps. Also done so there is no bleading between sessions.
- Run the `pre_cwd_changed_hook`
DirChanged (after the cwd has changed):
- Restore session using new cwd
- Run the `post_cwd_changed_hook`

Now when the user changes the cwd with `:cd some/new/dir` auto-session handles it gracefully, saving the current session so there aren't losses and loading the session for the upcoming cwd if it exists.

Hooks are available for custom actions _before_ and _after_ the `cwd` is changed. These hooks can be configured through the `cwd_change_handling` key as follows:
Expand Down Expand Up @@ -113,18 +113,19 @@ require('lualine').setup{

### Options

| Config | Options | Default | Description |
| -------------------------------- | ------------------------ | ------------------------------------ | --------------------------------------------------------------- |
| log_level | 'debug', 'info', 'error' | 'info' | Sets the log level of the plugin |
| auto_session_enable_last_session | false, true | false | Loads the last loaded session if session for cwd does not exist |
| auto_session_root_dir | "/some/path/you/want" | vim.fn.stdpath('data').."/sessions/" | Changes the root dir for sessions |
| auto_session_enabled | false, true | true | Enables/disables the plugin's auto save _and_ restore features |
| auto_session_create_enabled | false, true | true | Enables/disables the plugin's session auto creation |
| auto_save_enabled | false, true, nil | nil | Enables/disables auto saving |
| auto_restore_enabled | false, true, nil | nil | Enables/disables auto restoring |
| auto_session_suppress_dirs | ["list", "of paths"] | nil | Suppress session create/restore if in one of the list of dirs |
| auto_session_allowed_dirs | ["list", "of paths"] | nil | Allow session create/restore if in one of the list of dirs |
| auto_session_use_git_branch | false, true, nil | nil | Use the git branch to differentiate the session name |
| Config | Options | Default | Description |
| ------------------------------------------| ------------------------ | ------------------------------------ | --------------------------------------------------------------- |
| log_level | 'debug', 'info', 'error' | 'info' | Sets the log level of the plugin |
| auto_session_enable_last_session | false, true | false | Loads the last loaded session if session for cwd does not exist |
| auto_session_enable_file_tree_integration | false, true | false | Fixes problems with nvim-tree and NERDTree compatibility |
| auto_session_root_dir | "/some/path/you/want" | vim.fn.stdpath('data').."/sessions/" | Changes the root dir for sessions |
| auto_session_enabled | false, true | true | Enables/disables the plugin's auto save _and_ restore features |
| auto_session_create_enabled | false, true | true | Enables/disables the plugin's session auto creation |
| auto_save_enabled | false, true, nil | nil | Enables/disables auto saving |
| auto_restore_enabled | false, true, nil | nil | Enables/disables auto restoring |
| auto_session_suppress_dirs | ["list", "of paths"] | nil | Suppress session create/restore if in one of the list of dirs |
| auto_session_allowed_dirs | ["list", "of paths"] | nil | Allow session create/restore if in one of the list of dirs |
| auto_session_use_git_branch | false, true, nil | nil | Use the git branch to differentiate the session name |

#### Notes

Expand Down Expand Up @@ -176,6 +177,21 @@ require('auto-session').setup {

:warning: WARNING :warning: If the directory does not exist, default directory will be used and an error message will be printed.

### File Tree Integration

This fixes a lot of conflics with [`nvim-tree`](https://github.com/nvim-tree/nvim-tree.lua) and [`NERDTree`](https://github.com/preservim/nerdtree),
as they both try to control the `cwd`, which causes infinite loops and corrupted session data.
This feature synchronizes the `cwd` between `auto-session` and the file tree plugin, which turns out to be very powerful and fun to use.
:warning: This feature is still experimental. Bugs to be expected.

```lua
require('auto-session').setup {
auto_session_enable_file_tree_integration=true,
}
```

:warning: Note :warning: If you already have broken session data, this will not fix it. You will have to delete that session and start again

# 📢 Commands

Auto Session exposes two commands that can be used or mapped to any keybindings for manually saving and restoring sessions.
Expand Down
107 changes: 75 additions & 32 deletions lua/auto-session-autocmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@ local Lib = require "auto-session-library"

local M = {}

-- don't autorestore if there are open buffers (indicating a failed save session
local function has_open_buffers()
local result = false
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
if vim.fn.bufloaded(bufnr) then
local bufname = vim.api.nvim_buf_get_name(bufnr)
if bufname ~= "" then
if vim.fn.bufwinnr(bufnr) ~= -1 then
if result then
result = true
Lib.logger.debug("There are buffer(s) present: ")
end
Lib.logger.debug(" " .. bufname)
end
end
end
end
return result
end

---Setup autocmds for DirChangedPre and DirChanged
---@param config table auto session config
---@param AutoSession table auto session instance
Expand All @@ -12,46 +32,69 @@ M.setup_autocmds = function(config, AutoSession)
end

local conf = config.cwd_change_handling
local scopes = { "global" }
if config.auto_session_enable_file_tree_integration then
table.insert(scopes, "window")
end

vim.api.nvim_create_autocmd("DirChangedPre", {
callback = function()
Lib.logger.debug "DirChangedPre"
Lib.logger.debug("cwd: " .. vim.fn.getcwd())
for _, pattern in ipairs(scopes) do
vim.api.nvim_create_autocmd("DirChangedPre", {
callback = function()
Lib.logger.debug "DirChangedPre"
Lib.logger.debug(" cwd: " .. vim.fn.getcwd())
Lib.logger.debug(" target: " .. vim.v.event.directory)
Lib.logger.debug(" changed window: " .. tostring(vim.v.event.changed_window))
Lib.logger.debug(" scope: " .. vim.v.event.scope)

AutoSession.AutoSaveSession()
if vim.v.event.scope == "window" then
-- don't save session for all `lcd`'s (local change directory), just file tree explorers
if not Lib.tree_buf_type(vim.api.nvim_get_current_buf()) then
Lib.logger.debug(" not file tree event, returning")
return
end
end

-- Clear all buffers and jumps after session save so session doesn't blead over to next session.
vim.cmd "%bd!"
vim.cmd "clearjumps"
AutoSession.AutoSaveSession()

if type(conf.pre_cwd_changed_hook) == "function" then
conf.pre_cwd_changed_hook()
end
end,
pattern = "global",
})
-- Clear all buffers and jumps after session save so session doesn't blead over to next session.
vim.cmd "%bd!"
vim.cmd "clearjumps"

if conf.restore_upcoming_session then
vim.api.nvim_create_autocmd("DirChanged", {
callback = function()
Lib.logger.debug "DirChanged"
Lib.logger.debug("cwd: " .. vim.fn.getcwd())
if type(conf.pre_cwd_changed_hook) == "function" then
conf.pre_cwd_changed_hook()
end
end,
pattern = pattern,
})

-- Deferring to avoid otherwise there are tresitter highlighting issues
vim.defer_fn(function()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This approach was causing an infinite loop. From the docs:

DirChanged After the current-directory was changed.
...
Non-recursive (event cannot trigger itself).

By triggering Autosession.AutoRestoreSession asynchronously, it caused an infinite loop where the opened buffer could change the global directory (somehow) after opening. So, I moved the asynchronous part to after the session was restored. It then runs filetype detect on all the open buffers which fixes the syntax highlighting error.

local success = AutoSession.AutoRestoreSession()
if conf.restore_upcoming_session then
vim.api.nvim_create_autocmd("DirChanged", {
callback = function()
Lib.logger.debug "DirChanged"
Lib.logger.debug(" cwd: " .. vim.fn.getcwd() )
Lib.logger.debug(" changed window: " .. tostring(vim.v.event.changed_window))
Lib.logger.debug(" scope: " .. vim.v.event.scope)

if not success then
Lib.logger.info("Could not load session. A session file is likely missing for this cwd." .. vim.fn.getcwd())
end
-- all buffers should've been deleted in `DirChangedPre`, something probably went wrong
if has_open_buffers() then
Lib.logger.debug("Cancelling session restore")
return
end

if type(conf.post_cwd_changed_hook) == "function" then
conf.post_cwd_changed_hook()
end
end, 50)
end,
pattern = "global",
})
local success = AutoSession.AutoRestoreSession()

if not success then
Lib.logger.info("Could not load session. A session file is likely missing for this cwd." .. vim.fn.getcwd())
return
end

if type(conf.post_cwd_changed_hook) == "function" then
conf.post_cwd_changed_hook()
end
end,
pattern = pattern,
})
end
end
end

Expand Down
25 changes: 25 additions & 0 deletions lua/auto-session-library.lua
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,29 @@ function Lib.logger.error(...)
vim.notify(vim.fn.join({ "error: ", tostring(...) }, " "), vim.log.levels.ERROR)
end

-- return type of file tree explorer or false if not a file tree explorers
function Lib.tree_buf_type(bufnr)
if bufnr == nil then
bufnr = 0
end
if vim.fn.bufexists(bufnr) then
local bufname = vim.api.nvim_buf_get_name(bufnr)
local filename = vim.fn.fnamemodify(bufname, ":t")
if filename:match "^NvimTree_[0-9]+$" then
if vim.bo[bufnr].filetype == "NvimTree" then
return "nvimtree"
elseif vim.fn.filereadable(bufname) == 0 then
return "nvimtree"
end
elseif filename:match "^NERD_tree_[0-9]+$" then
if vim.bo[bufnr].filetype == "nerdtree" then
return "nerdtree"
elseif vim.fn.filereadable(bufname) == 0 then
return "nerdtree"
end
end
end
return false
end

return Lib
32 changes: 32 additions & 0 deletions lua/auto-session.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ local AutoSession = {
local defaultConf = {
log_level = vim.g.auto_session_log_level or AutoSession.conf.logLevel or AutoSession.conf.log_level or "info", -- Sets the log level of the plugin (debug, info, error). camelCase logLevel for compatibility.
auto_session_enable_last_session = vim.g.auto_session_enable_last_session or false, -- Enables/disables the "last session" feature
auto_session_enable_file_tree_integration = vim.g.auto_session_enable_file_tree_integration or false, -- Enables/disables the "last session" feature
auto_session_root_dir = vim.fn.stdpath "data" .. "/sessions/", -- Root dir where sessions will be stored
auto_session_enabled = true, -- Enables/disables auto creating, saving and restoring
auto_session_create_enabled = nil, -- Enables/disables auto creating new sessions
Expand Down Expand Up @@ -480,6 +481,35 @@ function AutoSession.RestoreSessionFromFile(session_file)
AutoSession.RestoreSession(string.format(AutoSession.get_root_dir() .. "%s.vim", session_file:gsub("/", "%%")))
end

--
-- Refresh syntax highlighting and file trees
local function post_restore_refresh()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is what fixes the syntax highlighting. I moved it to after the restore session, so that it's always triggered. Although, I'm not sure if the problem exists other than in during the autocmd callbacks? So, it might be better to move it back there if not.

Copy link
Owner

Choose a reason for hiding this comment

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

Interesting, I hadn't noticed problems with this. But this should be fine anyway 👍

-- refresh sytax highlighting
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
if not vim.api.nvim_buf_is_loaded(bufnr) then
goto continue
end

vim.api.nvim_buf_call(bufnr, function()
vim.cmd 'filetype detect'
end)

if AutoSession.conf.auto_session_enable_file_tree_integration then
-- refresh file trees
local tree_type = Lib.tree_buf_type(bufnr)
-- we only open the tree if it was open before
if (tree_type == "nvimtree") then
pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
require('nvim-tree').open()
elseif (tree_type == "nerdtree") then
pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
vim.cmd 'NERDTreeOpen'
end
end
::continue::
end
end

-- TODO: make this more readable!
---Restores the session by sourcing the session file if it exists/is readable.
---This function is intended to be called by the user but it is also called by `AutoRestoreSession`
Expand Down Expand Up @@ -513,6 +543,8 @@ function AutoSession.RestoreSession(sessions_dir_or_file)

local post_cmds = AutoSession.get_cmds "post_restore"
run_hook_cmds(post_cmds, "post-restore")

vim.defer_fn(post_restore_refresh, 0)
end

-- I still don't like reading this chunk, please cleanup
Expand Down