From 5df2255e1d9a76771c75c5419a1294ed3b97c55d Mon Sep 17 00:00:00 2001 From: Cameron Ring Date: Sun, 7 Jul 2024 19:59:52 -0700 Subject: [PATCH] Add arg handling support Introduces two new arguments: args_allow_single_directory (default: true) Allows launching nvim with single a argument that's a directory. auto-session will try to load a session from that directory if possible args_files_auto_save (default false) Allow saving a session even when launched with a file argument (or multiple files/dirs). It does not load any existing session first. That's not super useful on it's own by args_files_auto_save can be a callback function that conditionally returns true or false --- README.md | 82 ++++++++++++++++++++++++++++++++++--- lua/auto-session/init.lua | 85 +++++++++++++++++---------------------- 2 files changed, 114 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 73624c5..b5e54a6 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,11 @@ Auto Session takes advantage of Neovim's existing session management capabilitie # 💡 Behaviour 1. When starting `nvim` with no arguments, auto-session will try to restore an existing session for the current `cwd` if one exists. -2. When starting `nvim .` with some argument, auto-session will do nothing. -3. Even after starting `nvim` with an argument, a session can still be manually restored by running `:SessionRestore`. -4. Any session saving and restoration takes into consideration the current working directory `cwd`. -5. When piping to `nvim`, e.g: `cat myfile | nvim`, auto-session behaves like #2. +2. When starting `nvim .` (or another directory), auto-session will try to restore the session for that directory. +3. When starting `nvim some_file.txt` (or multiple files), by default, auto-session won't do anything. See [argument handling](#argument-handling) for more details. +4. Even after starting `nvim` with a file argument, a session can still be manually restored by running `:SessionRestore`. +5. Any session saving and restoration takes into consideration the current working directory `cwd`. +6. When piping to `nvim`, e.g: `cat myfile | nvim`, auto-session won't do anything. :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 `:SessionSave`. @@ -30,6 +31,7 @@ By default, `cwd` handling is disabled but when enabled, it works as follows: 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: + ```lua require("auto-session").setup { log_level = "error", @@ -141,6 +143,8 @@ require("auto-session").setup { pre_cwd_changed_hook = nil, -- function: This is called after auto_session code runs for the `DirChangedPre` autocmd post_cwd_changed_hook = nil, -- function: This is called after auto_session code runs for the `DirChanged` autocmd }, + args_allow_single_directory = true, -- boolean Follow normal sesion save/load logic if launched with a single directory as the only argument + args_allow_files_auto_save = false, -- boolean|function Allow saving a session even when launched with a file argument (or multiple files/dirs). It does not load any existing session first. While you can just set this to true, you probably want to set it to a function that decides when to save a session when launched with file args. See documentation for more detail } ``` @@ -262,6 +266,74 @@ require('auto-session').setup { } ``` +## Argument Handling + +By default, when `nvim` is run with a single directory argument, auto-session will try to restore the session for that directory. If `nvim` is run with multiple directories or any file arguments, auto-session won't try to restore a session and won't auto-save a session on exit (if enabled). Those behaviors can be changed with these config parameters: + +```lua + args_allow_single_directory = true, -- boolean Follow normal sesion save/load logic if launched with a single directory as the only argument + args_allow_files_auto_save = false, -- boolean|function Allow saving a session even when launched with a file argument (or multiple files/dirs). It does not load any existing session first. While you can just set this to true, you probably want to set it to a function that decides when to save a session when launched with file args. See documentation for more detail +``` + +For `args_allow_single_directory`, if you frequently use `netrw` to look at directories, you might want to add it to `bypass_session_save_file_types` if you don't want to create a session for each directory you look at: + +```lua + bypass_session_save_file_types = { 'netrw' } +``` + +If `args_allow_files_auto_save` is true, auto-session won't load any session when `nvim` is launched with file argument(s) but it will save on exit. What's probably more useful is to set `args_allow_files_auto_save` to a function that returns true if a session should be saved and false otherwise. auto-session will call that function on auto save when run with arguments. Here's one example config where it will save the session if at least two buffers are open after being launched with arguments: + +```lua +return { + 'rmagatti/auto-session', + config = function() + require('auto-session').setup({ + auto_restore_enabled = true, + auto_save_enabled = true, + + args_allow_files_auto_save = function() + local supported = 0 + + local buffers = vim.api.nvim_list_bufs() + for _, buf in ipairs(buffers) do + -- Check if the buffer is valid and loaded + if vim.api.nvim_buf_is_valid(buf) and vim.api.nvim_buf_is_loaded(buf) then + local path = vim.api.nvim_buf_get_name(buf) + if vim.fn.filereadable(path) == 1 then supported = supported + 1 end + end + end + + -- If we have more 2 or more supported buffers, save the session + return supported >= 2 + end, + }) + end, +} + +``` + +Another possibility is to only save the session if there are at least two windows with buffers backed by normal files: + +```lua + args_allow_files_auto_save = function() + local supported = 0 + + local tabpages = vim.api.nvim_list_tabpages() + for _, tabpage in ipairs(tabpages) do + local windows = vim.api.nvim_tabpage_list_wins(tabpage) + for _, window in ipairs(windows) do + local buffer = vim.api.nvim_win_get_buf(window) + local file_name = vim.api.nvim_buf_get_name(buffer) + if vim.fn.filereadable(file_name) then supported = supported + 1 end + end + end + + -- If we have 2 or more windows with supported buffers, save the session + return supported >= 2 + end, + +``` + ## Disabling the plugin One might run into issues with Firenvim or another plugin and want to disable `auto_session` altogether based on some condition. @@ -286,7 +358,7 @@ For troubleshooting refer to the [wiki page](https://github.com/rmagatti/auto-se ## 🔭 Session Lens Session Lens has been merged into Auto Session so now you can see, load, and delete your sessions using Telescope! It's enabled by -default if you have Telescope, but here's the Lazy config that shows the configuration options: +default if you have Telescope, but here's the Lazy config that shows the configuration options: ```lua diff --git a/lua/auto-session/init.lua b/lua/auto-session/init.lua index 09f8bb3..0c73935 100644 --- a/lua/auto-session/init.lua +++ b/lua/auto-session/init.lua @@ -7,7 +7,7 @@ local AutoSession = { conf = {}, } --- Run comand hooks +-- Run command hooks local function run_hook_cmds(cmds, hook_name) local results = {} if not Lib.is_empty_table(cmds) then @@ -71,14 +71,13 @@ local defaultConf = { ---@field log_level? string|integer "debug", "info", "warn", "error" or vim.log.levels.DEBUG, vim.log.levels.INFO, vim.log.levels.WARN, vim.log.levels.ERROR ---Argv Handling ---@field args_allow_single_directory? boolean Follow normal sesion save/load logic if launched with a single directory as the only argument ----@field args_handling? string How to handle sessions when nvim is launched with arguments. Must be one of ----'replace_session': don't load existing session but save on exit ----'replace_session_only_if_multiple_buffers': don't load existing session and save session on exit if there's more than one buffer that would be saved ----'new_session_named_with_args': add arguments to session name for loading/saving (not implemented) +---@field args_allow_files_auto_save? boolean|function Allow saving a session even when launched with a file argument (or multiple files/dirs). It does not load any existing session first. While you can just set this to true, you probably want to set it to a function that decides when to save a session when launched with file args. See documentation for more detail local luaOnlyConf = { bypass_session_save_file_types = nil, -- Bypass auto save when only buffer open is one of these file types - close_unsupported_windows = true, -- Close windows that aren't backed by normal file + close_unsupported_windows = true, -- Close windows that aren't backed by normal file + args_allow_single_directory = true, -- Allow single directory arguments by default + args_allow_files_auto_save = false, -- Don't save session for file args by default ---CWD Change Handling Config ---@class CwdChangeHandling ---@field restore_upcoming_session boolean {true} restore session for upcoming cwd on cwd change @@ -205,16 +204,19 @@ end -- Returns whether Auto restoring / saving is enabled for the args nvim was launched with local launch_argv = nil -local enabled_for_command_line_argv = function(is_save) +local function enabled_for_command_line_argv(is_save) is_save = is_save or false - -- When a session is loaded, it will also load the global argument list so - -- save the argv we were actually launched with so we can always access it + -- If no args (or launch_argv has been unset, allow restoring/saving) if not launch_argv then - launch_argv = vim.fn.argv() + Lib.logger.debug "No arguments, restoring/saving enabled" + return true end + local argc = #launch_argv + Lib.logger.debug("enabled_for_command_line_argv, launch_argv: " .. vim.inspect(launch_argv)) + if argc == 0 then -- Launched with no args, saving is enabled Lib.logger.debug "No arguments, restoring/saving enabled" @@ -232,42 +234,23 @@ local enabled_for_command_line_argv = function(is_save) return true end - local args_handling = AutoSession.conf.args_handling - - if not args_handling then + if not AutoSession.conf.args_allow_files_auto_save then return false end - if args_handling == "replace_session" then - -- Don't load, but do save - if not is_save then - Lib.logger.debug "[replace_session] Not allowing restore when launched with argument" - else - Lib.logger.debug "[replace_session] Allowing save when launched with argument argument" - end - - return is_save - elseif args_handling == "replace_session_only_if_multiple_buffers" then - -- Don't restore - if not is_save then - Lib.logger.debug "[replace_session_only_if_multiple] Not allowing restore when launched with argument" - return false - end - -- Check close_unsupported_windows - if Lib.count_supported_buffers() > 1 then - Lib.logger.debug "[replace_session_only_if_multiple_buffers] multiple buffers, allow save" - return true - end - Lib.logger.debug "[replace_session_only_if_multiple_buffers] single buffer, disallow save" + if not is_save then + Lib.logger.debug "Not allowing restore when launched with argument" return false - elseif args_handling == "new_session_named_with_args" then - -- TODO: implement me or maybe not worth it - return false - else - Lib.logger.warn("Invalid value for args_handling: " .. args_handling) end - return false + if type(AutoSession.conf.args_allow_files_auto_save) == "function" then + local ret = AutoSession.conf.args_allow_files_auto_save() + Lib.logger.debug("conf.args_allow_files_auto_save() returned: " .. vim.inspect(ret)) + return ret + end + + Lib.logger.debug "Allowing possible save when launched with argument" + return true end local in_headless_mode = function() @@ -276,6 +259,7 @@ end local auto_save = function() if in_pager_mode() or in_headless_mode() or not enabled_for_command_line_argv(true) then + Lib.logger.debug "auto_save, pager, headless, or enabled_for_command_line_argv returned false" return false end @@ -732,11 +716,6 @@ end function AutoSession.SaveSession(sessions_dir, auto) Lib.logger.debug { sessions_dir = sessions_dir, auto = auto } - -- Delete global arguments since the buffers are what we want to - -- save the state of. i.e. we don't want to reopen the arguments - -- that were passed to nvim at launch time - vim.cmd "%argdel" - local session_file_name = get_session_file_name(sessions_dir) Lib.logger.debug { session_file_name = session_file_name } @@ -775,13 +754,20 @@ end local function auto_restore_session_at_vim_enter() local session_dir = nil - local argv = vim.fn.argv() + -- Save the launch args here as restoring a session will replace vim.fn.argv. We clear + -- launch_argv in restore session so it's only used for the session launched from the command + -- line + launch_argv = vim.fn.argv() -- Is there exactly one argument and is it a directory? - if AutoSession.conf.args_allow_single_directory and #argv == 1 and vim.fn.isdirectory(argv[1]) == Lib._VIM_TRUE then + if + AutoSession.conf.args_allow_single_directory + and #launch_argv == 1 + and vim.fn.isdirectory(launch_argv[1]) == Lib._VIM_TRUE + then -- Get the full path of the directory and make sure it doesn't have a trailing path_separator -- to make sure we find the session - session_dir = vim.fn.fnamemodify(argv[1], ":p"):gsub("[" .. Lib.get_path_separator() .. "]$", "") + session_dir = vim.fn.fnamemodify(launch_argv[1], ":p"):gsub("[" .. Lib.get_path_separator() .. "]$", "") Lib.logger.debug("Launched with single directory, using as session_dir: " .. session_dir) end @@ -843,6 +829,9 @@ function AutoSession.RestoreSession(sessions_dir_or_file) local cmd = AutoSession.conf.silent_restore and "silent source " .. file_path or "source " .. file_path local success, result = pcall(vim.cmd, cmd) + -- Clear any saved command line args since we don't need them anymore + launch_argv = nil + if not success then Lib.logger.error([[ Error restoring session! The session might be corrupted.