From 03714d96756a6e34c073e02f4b153f9e853d080e Mon Sep 17 00:00:00 2001 From: Cameron Ring Date: Thu, 12 Sep 2024 14:05:37 -0700 Subject: [PATCH 1/5] fix: save_extra_cmds only works with single string `Lib.run_hook_cmds` returns a table of tables (one for reach hook cmd that was run). In `save_extra_cmds`, `vim.fn.writefile` expects a table strings and if any of those strings contain newlines, they're translated into nul characters. That means that there was no way to return more than one line from a `save_extra_cmds` hook function. To fix, we combine the results across all `save_extra_cmds` hooks and we now support a `save_extra_cmds` hook returning either a string (possibly with newlines) or table. If a hook returns a string then any newlines will be used to split the string into individual line entries in the table passed to `vim.fn.writefile`. If a hook returns a table, then each of the values of the table will be added to the table passed to `vim.fn.writefile` (the same newline splitting will also happen) --- README.md | 2 +- lua/auto-session/init.lua | 13 ++++- lua/auto-session/lib.lua | 41 +++++++++++++ tests/extra_sesssion_commands_spec.lua | 81 +++++++++++++++++++++++++- tests/lib_spec.lua | 32 ++++++++++ 5 files changed, 162 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e83ac99..b4afb8e 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ require('auto-session').setup({ Command hooks exist in the format: {hook_name} - `{pre_save}`: executes _before_ a session is saved -- `{save_extra}`: executes _after_ a session is saved, return string will save to `*x.vim`, reference `:help mks` +- `{save_extra}`: executes _after_ a session is saved, saves returned string or table to `*x.vim`, reference `:help mks` - `{post_save}`: executes _after_ a session is saved - `{pre_restore}`: executes _before_ a session is restored - `{post_restore}`: executes _after_ a session is restored diff --git a/lua/auto-session/init.lua b/lua/auto-session/init.lua index afaca64..abe1588 100644 --- a/lua/auto-session/init.lua +++ b/lua/auto-session/init.lua @@ -355,12 +355,19 @@ end ---@return boolean Returns whether extra commands were saved local function save_extra_cmds_new(session_path) local data = AutoSession.run_cmds "save_extra" - if not data then + local extra_file = string.gsub(session_path, "%.vim$", "x.vim") + + -- data is a table of strings or tables, one for each hook function + -- need to combine them all here into a single table of strings + local data_to_write = Lib.flatten_table_and_split_strings(data) + + if not data_to_write or vim.tbl_isempty(data_to_write) then + -- Have to delete the file just in case there's an old file from a previous save + vim.fn.delete(extra_file) return false end - local extra_file = string.gsub(session_path, "%.vim$", "x.vim") - if vim.fn.writefile(data, extra_file) ~= 0 then + if vim.fn.writefile(data_to_write, extra_file) ~= 0 then return false end diff --git a/lua/auto-session/lib.lua b/lua/auto-session/lib.lua index 1d0210a..34edaa5 100644 --- a/lua/auto-session/lib.lua +++ b/lua/auto-session/lib.lua @@ -563,4 +563,45 @@ function Lib.run_hook_cmds(cmds, hook_name) return results end +---Split any strings on newlines and add each one to the output table +---Also flatten any embedded tables and and their values into the root table +---(non recursive so only one level deep) +---@param input table|nil +---@return table The flattened table +function Lib.flatten_table_and_split_strings(input) + local output = {} + + if not input then + return output + end + + local function add_value_to_output(value) + Lib.logger.debug("value: ", value) + if value == nil then + return + end + + local value_type = type(value) + if value_type == "number" then + table.insert(output, value) + elseif value_type == "string" then + for s in value:gmatch "[^\r\n]+" do + table.insert(output, s) + end + end + end + + for _, value in pairs(input) do + if type(value) == "table" then + for _, subvalue in pairs(value) do + add_value_to_output(subvalue) + end + else + add_value_to_output(value) + end + end + + return output +end + return Lib diff --git a/tests/extra_sesssion_commands_spec.lua b/tests/extra_sesssion_commands_spec.lua index 46cf933..70af2c6 100644 --- a/tests/extra_sesssion_commands_spec.lua +++ b/tests/extra_sesssion_commands_spec.lua @@ -6,11 +6,15 @@ describe("Config with extra session commands", function() local save_extra_cmds_called = false local as = require "auto-session" local Lib = require "auto-session.lib" + -- WARN: this test calls setup again later to change save_extra_cmds as.setup { save_extra_cmds = { function() save_extra_cmds_called = true - return [[echo "hello world"]] + return [[ + lua vim.g.extraCmdsTest = 1 + lua vim.g.extraCmdsTest2 = 2 + ]] end, }, -- log_level = "debug", @@ -36,7 +40,78 @@ describe("Config with extra session commands", function() TL.assertSessionHasFile(TL.default_session_path, TL.test_file) -- Make sure extra commands are there - assert.True(TL.fileHasString(default_extra_cmds_path, 'echo \\"hello world\\"')) + assert.True(TL.fileHasString(default_extra_cmds_path, "lua vim.g.extraCmdsTest = 1")) + end) + + it("can restore a default session with extra commands", function() + vim.g.extraCmdsTest = 0 + vim.g.extraCmdsTest2 = 0 + + assert.True(as.RestoreSession()) + + assert.True(vim.g.extraCmdsTest == 1) + assert.True(vim.g.extraCmdsTest2 == 2) + end) + + it("can clear x.vim if there are no extra commands", function() + -- make sure the file is there now + assert.equals(1, vim.fn.filereadable(default_extra_cmds_path)) + + -- remove the handler + as.setup { + save_extra_cmds = nil, + } + + -- generate default session + assert.True(as.AutoSaveSession()) + + -- Make sure the session was created + assert.equals(1, vim.fn.filereadable(TL.default_session_path)) + + -- make sure the extra commands file was removed + assert.equals(0, vim.fn.filereadable(default_extra_cmds_path)) + end) + + TL.clearSessionFilesAndBuffers() + + it("can save a default session with extra commands in a table", function() + vim.cmd("e " .. TL.test_file) + + save_extra_cmds_called = false + + as.setup { + save_extra_cmds = { + function() + save_extra_cmds_called = true + return { "lua vim.g.extraCmdsTest = 1", "lua vim.g.extraCmdsTest2 = 2" } + end, + }, + } + + -- generate default session + assert.True(as.AutoSaveSession()) + + -- Make sure the session was created + assert.equals(1, vim.fn.filereadable(TL.default_session_path)) + assert.equals(1, vim.fn.filereadable(default_extra_cmds_path)) + + assert.True(save_extra_cmds_called) + + -- Make sure the session has our buffer + TL.assertSessionHasFile(TL.default_session_path, TL.test_file) + + -- Make sure extra commands are there + assert.True(TL.fileHasString(default_extra_cmds_path, "lua vim.g.extraCmdsTest = 1")) + end) + + it("can restore a default session with extra commands", function() + vim.g.extraCmdsTest = 0 + vim.g.extraCmdsTest2 = 0 + + assert.True(as.RestoreSession()) + + assert.True(vim.g.extraCmdsTest == 1) + assert.True(vim.g.extraCmdsTest2 == 2) end) local session_name = "x" @@ -61,7 +136,7 @@ describe("Config with extra session commands", function() TL.assertSessionHasFile(session_path, TL.test_file) -- Make sure extra commands are there - assert.True(TL.fileHasString(extra_cmds_path, 'echo \\"hello world\\"')) + assert.True(TL.fileHasString(extra_cmds_path, "lua vim.g.extraCmdsTest = 1")) end) it("can correctly differentiate x.vim session and xx.vim custom commands", function() diff --git a/tests/lib_spec.lua b/tests/lib_spec.lua index fbd790f..728df6e 100644 --- a/tests/lib_spec.lua +++ b/tests/lib_spec.lua @@ -183,4 +183,36 @@ describe("Lib / Helper functions", function() assert.equals("", Lib.current_session_name()) assert.equals("", Lib.current_session_name(true)) end) + + it("flatten_table_and_split_string() works with strings and tables", function() + local data = { "one\ntwo\nthree\n" } + local output = Lib.flatten_table_and_split_strings(data) + + assert.equals(#output, 3) + assert.equals(output[1], "one") + assert.equals(output[2], "two") + assert.equals(output[3], "three") + + data = { { "a", "b", "c\nd" }, "e", "f\ng" } + output = Lib.flatten_table_and_split_strings(data) + + assert.equals(#output, 7) + assert.equals(output[1], "a") + assert.equals(output[2], "b") + assert.equals(output[3], "c") + assert.equals(output[4], "d") + assert.equals(output[5], "e") + assert.equals(output[6], "f") + assert.equals(output[7], "g") + + data = {} + output = Lib.flatten_table_and_split_strings(data) + + assert.True(vim.tbl_isempty(output)) + + data = { {} } + output = Lib.flatten_table_and_split_strings(data) + + assert.True(vim.tbl_isempty(output)) + end) end) From 3312d7e19af537a24bb7dbc1438776d155a5a070 Mon Sep 17 00:00:00 2001 From: Cameron Ring Date: Thu, 12 Sep 2024 22:02:35 -0700 Subject: [PATCH 2/5] docs(README): add save_extra_cmds quickfix example --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b4afb8e..7358f7b 100644 --- a/README.md +++ b/README.md @@ -312,12 +312,25 @@ require('auto-session').setup { end }, + -- Save quickfix list and open it when restoring the session save_extra_cmds = { function() - return [[echo "hello world"]] - end - } -} + local qflist = vim.fn.getqflist() + -- return nil to clear any old qflist + if #qflist == 0 then return nil end + local qfinfo = vim.fn.getqflist({ title = 1 }) + + for _, entry in ipairs(qflist) do + -- use filename instead of bufnr so it can be reloaded + entry.filename = vim.api.nvim_buf_get_name(entry.bufnr) + entry.bufnr = nil + end + + local setqflist = 'call setqflist(' .. vim.fn.string(qflist) .. ')' + local setqfinfo = 'call setqflist([], "a", ' .. vim.fn.string(qfinfo) .. ')' + return { setqflist, setqfinfo, 'copen' } + end, + }, ``` ## ➖ Statusline From e862eee04c68325d7d405b40aefb8d4faed49a60 Mon Sep 17 00:00:00 2001 From: Cameron Ring Date: Sun, 15 Sep 2024 09:03:56 -0700 Subject: [PATCH 3/5] docs(README): don't need telescope in dependencies The code will already load it on demand as needed. If someone uses Telescope, they'll already have it specified in their config so we don't need to list it as a dependency --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 7358f7b..ab85dcd 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,6 @@ AutoSession takes advantage of Neovim's existing session management capabilities { 'rmagatti/auto-session', lazy = false, - dependencies = { - 'nvim-telescope/telescope.nvim', -- Only needed if you want to use session lens - }, ---enables autocomplete for opts ---@module "auto-session" @@ -158,9 +155,6 @@ You can use Telescope to see, load, and delete your sessions. It's enabled by de { 'rmagatti/auto-session', lazy = false, - dependencies = { - 'nvim-telescope/telescope.nvim', - }, keys = { -- Will use Telescope if installed or a vim.ui.select picker otherwise { 'wr', 'SessionSearch', desc = 'Session search' }, From b120c72e13b303c77288a0f0fff26f4704f90755 Mon Sep 17 00:00:00 2001 From: Cameron Ring Date: Sun, 15 Sep 2024 21:30:34 -0700 Subject: [PATCH 4/5] fix: #377 typo in last session config option --- lua/auto-session/config.lua | 4 ++-- lua/auto-session/init.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/auto-session/config.lua b/lua/auto-session/config.lua index 30bb350..3973609 100644 --- a/lua/auto-session/config.lua +++ b/lua/auto-session/config.lua @@ -14,7 +14,7 @@ local M = {} ---@field auto_create? boolean|function Enables/disables auto creating new session files. Can take a function that should return true/false if a new session file should be created or not ---@field suppressed_dirs? table Suppress auto session for directories ---@field allowed_dirs? table Allow auto session for directories, if empty then all directories are allowed except for suppressed ones ----@field auto_restore_last_lession? boolean On startup, loads the last saved session if session for cwd does not exist +---@field auto_restore_last_session? boolean On startup, loads the last saved session if session for cwd does not exist ---@field use_git_branch? boolean Include git branch name in session name to differentiate between sessions for different git branches ---@field lazy_support? boolean Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging ---@field bypass_save_filetypes? table List of file types to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards @@ -159,7 +159,7 @@ local function check_old_config_names(config) auto_session_allowed_dirs = "allowed_dirs", auto_session_suppress_dirs = "suppressed_dirs", auto_session_create_enabled = "auto_create", - auto_session_enable_last_session = "auto_restore_last_lession", + auto_session_enable_last_session = "auto_restore_last_session", auto_session_use_git_branch = "use_git_branch", auto_restore_lazy_delay_enabled = "lazy_support", bypass_session_save_file_types = "bypass_save_filetypes", diff --git a/lua/auto-session/init.lua b/lua/auto-session/init.lua index abe1588..65fbed6 100644 --- a/lua/auto-session/init.lua +++ b/lua/auto-session/init.lua @@ -479,7 +479,7 @@ function AutoSession.auto_restore_session_at_vim_enter() end -- Check to see if the last session feature is on - if Config.auto_restore_last_lession then + if Config.auto_restore_last_session then Lib.logger.debug "Last session is enabled, checking for session" local last_session_name = Lib.get_latest_session(AutoSession.get_root_dir()) From 5b93b898a61f6388645473a20c118e9f58e90422 Mon Sep 17 00:00:00 2001 From: cameronr Date: Mon, 16 Sep 2024 04:32:17 +0000 Subject: [PATCH 5/5] chore(docs): auto-generate vimdoc --- doc/auto-session.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/auto-session.txt b/doc/auto-session.txt index 73a1ff8..2f0138c 100644 --- a/doc/auto-session.txt +++ b/doc/auto-session.txt @@ -18,7 +18,7 @@ AutoSession.Config *AutoSession.Config* {auto_create?} (boolean|function) Enables/disables auto creating new session files. Can take a function that should return true/false if a new session file should be created or not {suppressed_dirs?} (table) Suppress auto session for directories {allowed_dirs?} (table) Allow auto session for directories, if empty then all directories are allowed except for suppressed ones - {auto_restore_last_lession?} (boolean) On startup, loads the last saved session if session for cwd does not exist + {auto_restore_last_session?} (boolean) On startup, loads the last saved session if session for cwd does not exist {use_git_branch?} (boolean) Include git branch name in session name to differentiate between sessions for different git branches {lazy_support?} (boolean) Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging {bypass_save_filetypes?} (table) List of file types to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards