From 231250d4591bd8445929ed5ddcddaf1c40857c0b Mon Sep 17 00:00:00 2001 From: Adam Poniatowski Date: Sun, 23 Feb 2025 16:22:35 +0100 Subject: [PATCH] fix(sessions): handle LSP semantic tokens - Add safe LSP state restoration during session load - Temporarily disable semantic tokens to prevent errors - Add deferred semantic tokens refresh after session load - Improve session file naming and buffer cleanup - Add user notifications for session operations --- lua/plugins/mini.lua | 218 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 lua/plugins/mini.lua diff --git a/lua/plugins/mini.lua b/lua/plugins/mini.lua new file mode 100644 index 00000000..a8b4c30e --- /dev/null +++ b/lua/plugins/mini.lua @@ -0,0 +1,218 @@ +return { -- Collection of various small independent plugins/modules + 'echasnovski/mini.nvim', + config = function() + -- Better Around/Inside textobjects + -- + -- Examples: + -- - va) - [V]isually select [A]round [)]paren + -- - yinq - [Y]ank [I]nside [N]ext [Q]uote + -- - ci' - [C]hange [I]nside [']quote + require('mini.ai').setup { n_lines = 500 } + + -- Add/delete/replace surroundings (brackets, quotes, etc.) + -- + -- - saiw) - [S]urround [A]dd [I]nner [W]ord [)]Paren + -- - sd' - [S]urround [D]elete [']quotes + -- - sr)' - [S]urround [R]eplace [)] ['] + require('mini.surround').setup() + + -- Simple and easy statusline. + -- You could remove this setup call if you don't like it, + -- and try some other statusline plugin + local statusline = require 'mini.statusline' + -- set use_icons to true if you have a Nerd Font + statusline.setup { use_icons = vim.g.have_nerd_font } + + -- You can configure sections in the statusline by overriding their + -- default behavior. For example, here we set the section for + -- cursor location to LINE:COLUMN + ---@diagnostic disable-next-line: duplicate-set-field + statusline.section_location = function() + return '%2l:%-2v' + end + + -- Session management + local session = require('mini.sessions') + + -- Function to safely restore LSP state + local function safe_restore_lsp() + -- Disable semantic tokens temporarily during session load + local semantic_tokens = {} + for _, client in pairs(vim.lsp.get_active_clients()) do + semantic_tokens[client.id] = client.server_capabilities.semanticTokensProvider + client.server_capabilities.semanticTokensProvider = nil + end + + -- Return a function to restore semantic tokens + return function() + for id, tokens in pairs(semantic_tokens) do + local client = vim.lsp.get_client_by_id(id) + if client then + client.server_capabilities.semanticTokensProvider = tokens + end + end + end + end + + -- Get session name based on current directory + local function get_session_name() + local cwd = vim.fn.getcwd() + -- Replace path separators and spaces with underscores + local name = string.gsub(cwd, '[/\\%s]', '_') + return name + end + + -- Clean up buffers before saving session + local function pre_save() + -- Store current buffers + local bufs = vim.api.nvim_list_bufs() + + -- Close special buffers that we don't want to save + for _, buf in ipairs(bufs) do + if vim.api.nvim_buf_is_valid(buf) then + local buftype = vim.api.nvim_buf_get_option(buf, 'buftype') + local filetype = vim.api.nvim_buf_get_option(buf, 'filetype') + if buftype ~= '' or filetype == 'TelescopePrompt' or filetype == 'neo-tree' then + vim.api.nvim_buf_delete(buf, { force = true }) + end + end + end + end + + -- Clean up after loading session + local function post_load() + -- Close any empty buffers that might have been created + local bufs = vim.api.nvim_list_bufs() + for _, buf in ipairs(bufs) do + if vim.api.nvim_buf_is_valid(buf) then + local buftype = vim.api.nvim_buf_get_option(buf, 'buftype') + local filetype = vim.api.nvim_buf_get_option(buf, 'filetype') + local modified = vim.api.nvim_buf_get_option(buf, 'modified') + local name = vim.api.nvim_buf_get_name(buf) + + if not modified and (name == '' or buftype ~= '' or filetype == 'TelescopePrompt' or filetype == 'neo-tree') then + vim.api.nvim_buf_delete(buf, { force = true }) + end + end + end + end + + -- Create session functions for use in keymaps + _G.MiniSession = { + save = function() + local name = get_session_name() + local ok, err = pcall(function() session.write(name) end) + if ok then + vim.notify(string.format('Session: Successfully saved "%s"', name), + vim.log.levels.INFO) + else + vim.notify(string.format('Session: Failed to save "%s": %s', name, err), + vim.log.levels.ERROR) + end + end, + + load = function() + local name = get_session_name() + local ok, err = pcall(function() session.read(name) end) + if ok then + vim.notify(string.format('Session: Successfully loaded "%s"', name), + vim.log.levels.INFO) + else + vim.notify(string.format('Session: Failed to load "%s": %s', name, err), + vim.log.levels.ERROR) + end + end, + + delete = function() + local name = get_session_name() + local ok, err = pcall(function() session.delete(name) end) + if ok then + vim.notify(string.format('Session: Successfully deleted "%s"', name), + vim.log.levels.INFO) + else + vim.notify(string.format('Session: Failed to delete "%s": %s', name, err), + vim.log.levels.ERROR) + end + end + } + + -- Create session directory + local session_dir = vim.fn.stdpath('data') .. '/sessions' + if vim.fn.isdirectory(session_dir) == 0 then + vim.fn.mkdir(session_dir, 'p') + end + + session.setup({ + -- Directory to store session files + directory = session_dir, + -- File to use for current session + file = get_session_name(), + -- Whether to force write session file on each write operation + force_write = true, + -- Hook functions for actions + hooks = { + -- Before loading a session + pre_load = function() + -- Clean up buffers safely + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + local ft = vim.api.nvim_buf_get_option(buf, 'filetype') + -- Don't close special buffers + if ft ~= 'NvimTree' and ft ~= 'neo-tree' and ft ~= 'TelescopePrompt' then + pcall(vim.api.nvim_buf_delete, buf, { force = false }) + end + end + return safe_restore_lsp() + end, + -- After loading a session + post_load = function() + post_load() + -- Restore LSP semantic tokens after a short delay + vim.defer_fn(function() + if vim.v.vim_did_enter == 1 then + for _, client in pairs(vim.lsp.get_active_clients()) do + if client.server_capabilities.semanticTokensProvider then + vim.lsp.semantic_tokens.force_refresh(client.id) + end + end + end + end, 1000) + vim.notify('Session loaded!', vim.log.levels.INFO) + end, + -- Before saving a session + pre_save = function() + vim.notify('Saving session...', vim.log.levels.INFO) + pre_save() + end, + -- After saving a session + post_save = nil, + }, + -- Whether to read latest session if Neovim opened without file arguments + autoread = false, + -- Whether to write current session before quitting Neovim + autowrite = false, -- We'll handle this ourselves + -- Whether to disable showing non-error feedback + verbose = { + read = false, -- We'll handle our own notifications + write = false, + delete = false, + }, + }) + + -- Update session name when directory changes + vim.api.nvim_create_autocmd('DirChanged', { + callback = function() + session.setup({ file = get_session_name() }) + end, + }) + + -- Auto-save session when leaving Neovim + vim.api.nvim_create_autocmd('VimLeavePre', { + callback = function() + -- Only save if we have buffers + if #vim.fn.getbufinfo({buflisted = 1}) > 0 then + _G.MiniSession.save() + end + end, + }) + end, +}