From eefb8974d6a092da6e1631855e4288499b651fdd Mon Sep 17 00:00:00 2001 From: Folke Lemaitre Date: Wed, 27 Mar 2024 08:48:55 +0100 Subject: [PATCH] fix(ui): special handling for floats closed before VimEnter. Seems that WinClosed events dont execute before that. Fixes #1390 --- lua/lazy/util.lua | 30 ++++++++++++++++++ lua/lazy/view/float.lua | 69 +++++++++++++++++++++++++++++------------ lua/lazy/view/init.lua | 16 +++++----- 3 files changed, 89 insertions(+), 26 deletions(-) diff --git a/lua/lazy/util.lua b/lua/lazy/util.lua index a3b94f6..705ca39 100644 --- a/lua/lazy/util.lua +++ b/lua/lazy/util.lua @@ -98,6 +98,36 @@ function M.throttle(ms, fn) end end +--- Creates a weak reference to an object. +--- Calling the returned function will return the object if it has not been garbage collected. +---@generic T: table +---@param obj T +---@return T|fun():T? +function M.weak(obj) + local weak = { _obj = obj } + ---@return table + local function get() + local ret = rawget(weak, "_obj") + return ret == nil and error("Object has been garbage collected", 2) or ret + end + local mt = { + __mode = "v", + __call = function(t) + return rawget(t, "_obj") + end, + __index = function(_, k) + return get()[k] + end, + __newindex = function(_, k, v) + get()[k] = v + end, + __pairs = function() + return pairs(get()) + end, + } + return setmetatable(weak, mt) +end + ---@class LazyCmdOptions: LazyFloatOptions ---@field cwd? string ---@field env? table diff --git a/lua/lazy/view/float.lua b/lua/lazy/view/float.lua index d0cd6c7..bfbc4eb 100644 --- a/lua/lazy/view/float.lua +++ b/lua/lazy/view/float.lua @@ -24,6 +24,7 @@ local ViewConfig = require("lazy.view.config") ---@field win_opts LazyWinOpts ---@field backdrop_buf number ---@field backdrop_win number +---@field id number ---@overload fun(opts?:LazyFloatOptions):LazyFloat local M = {} @@ -33,6 +34,12 @@ setmetatable(M, { end, }) +local _id = 0 +local function next_id() + _id = _id + 1 + return _id +end + ---@param opts? LazyFloatOptions function M.new(opts) local self = setmetatable({}, { __index = M }) @@ -42,6 +49,7 @@ end ---@param opts? LazyFloatOptions function M:init(opts) require("lazy.view.colors").setup() + self.id = next_id() self.opts = vim.tbl_deep_extend("force", { size = Config.options.ui.size, style = "minimal", @@ -65,8 +73,13 @@ function M:init(opts) title_pos = self.opts.title and self.opts.title_pos or nil, } self:mount() - self:on_key(ViewConfig.keys.close, self.close) - self:on({ "WinLeave", "BufDelete", "BufHidden" }, self.close, { once = false }) + self:on("VimEnter", function() + vim.schedule(function() + if not self:win_valid() then + self:close() + end + end) + end, { buffer = false }) return self end @@ -138,7 +151,13 @@ function M:mount() self:layout() self.win = vim.api.nvim_open_win(self.buf, true, self.win_opts) + self:on("WinClosed", function() + self:close() + self:augroup(true) + end, { win = true }) self:focus() + self:on_key(ViewConfig.keys.close, self.close) + self:on({ "BufDelete", "BufHidden" }, self.close) if vim.bo[self.buf].buftype == "" then vim.bo[self.buf].buftype = "nofile" @@ -185,27 +204,38 @@ function M:mount() }) end +---@param clear? boolean +function M:augroup(clear) + return vim.api.nvim_create_augroup("trouble.window." .. self.id, { clear = clear == true }) +end + ---@param events string|string[] ----@param fn fun(self?):boolean? ----@param opts? table +---@param fn fun(self:LazyFloat, event:{buf:number}):boolean? +---@param opts? vim.api.keyset.create_autocmd | {buffer: false, win?:boolean} function M:on(events, fn, opts) - if type(events) == "string" then - events = { events } + opts = opts or {} + if opts.win then + opts.pattern = self.win .. "" + opts.win = nil + elseif opts.buffer == nil then + opts.buffer = self.buf + elseif opts.buffer == false then + opts.buffer = nil end - for _, e in ipairs(events) do - local event, pattern = e:match("(%w+) (%w+)") - event = event or e - vim.api.nvim_create_autocmd( - event, - vim.tbl_extend("force", { - pattern = pattern, - buffer = (not pattern) and self.buf or nil, - callback = function() - return fn(self) - end, - }, opts or {}) - ) + if opts.pattern then + opts.buffer = nil end + local _self = Util.weak(self) + opts.callback = function(e) + local this = _self() + if not this then + -- delete the autocmd + return true + end + return fn(this, e) + end + opts.group = self:augroup() + vim.api.nvim_create_autocmd(events, opts) end ---@param key string @@ -223,6 +253,7 @@ end ---@param opts? {wipe:boolean} function M:close(opts) + self:augroup(true) local buf = self.buf local win = self.win local wipe = opts and opts.wipe diff --git a/lua/lazy/view/init.lua b/lua/lazy/view/init.lua index e62fc6e..5753f5b 100644 --- a/lua/lazy/view/init.lua +++ b/lua/lazy/view/init.lua @@ -53,7 +53,7 @@ function M.create() Float.init(self, { title = Config.options.ui.title, title_pos = Config.options.ui.title_pos, - noautocmd = true, + noautocmd = false, }) if Config.options.ui.wrap then @@ -69,12 +69,14 @@ function M.create() self.render = Render.new(self) self.update = Util.throttle(Config.options.ui.throttle, self.update) - self:on({ "User LazyRender", "User LazyFloatResized" }, function() - if not (self.buf and vim.api.nvim_buf_is_valid(self.buf)) then - return true - end - self:update() - end) + for _, pattern in ipairs({ "LazyRender", "LazyFloatResized" }) do + self:on({ "User" }, function() + if not (self.buf and vim.api.nvim_buf_is_valid(self.buf)) then + return true + end + self:update() + end, { pattern = pattern }) + end vim.keymap.set("n", ViewConfig.keys.abort, function() require("lazy.manage.process").abort()