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
This commit is contained in:
Adam Poniatowski 2025-02-23 16:22:35 +01:00
parent 3c629b657f
commit 231250d459
1 changed files with 218 additions and 0 deletions

218
lua/plugins/mini.lua Normal file
View File

@ -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,
}