Skip to content

Commit c82b8af

Browse files
committed
fix(popup): moved="any", enter default, callback=fn
1 parent 2d9b061 commit c82b8af

File tree

5 files changed

+274
-35
lines changed

5 files changed

+274
-35
lines changed

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
test:
33
nvim --headless --noplugin -u scripts/minimal.vim -c "PlenaryBustedDirectory tests/plenary/ {minimal_init = 'tests/minimal_init.vim', sequential = true}"
44

5+
test1:
6+
nvim --headless --noplugin -u scripts/minimal.vim -c "PlenaryBustedDirectory tests/plenary/$(TEST1) {minimal_init = 'tests/minimal_init.vim', sequential = true}"
7+
58
generate_filetypes:
69
nvim --headless -c 'luafile scripts/update_filetypes_from_github.lua' -c 'qa!'
710

POPUP.md

+29-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ stablization and any required features are merged into Neovim, we can upstream
1010
this and expose the API in vimL to create better compatibility.
1111

1212
## Notices
13+
- **2024-09-19:** change `enter` default to false to follow Vim.
1314
- **2021-09-19:** we now follow Vim's convention of the first line/column of the screen being indexed 1, so that 0 can be used for centering.
1415
- **2021-08-19:** we now follow Vim's default to `noautocmd` on popup creation. This can be overriden with `vim_options.noautocmd=false`
1516

@@ -34,19 +35,26 @@ Unlikely (due to technical difficulties):
3435
- textprop
3536
- textpropwin
3637
- textpropid
37-
- [ ] "close"
38-
- But this is mostly because I don't know how to use mouse APIs in nvim. If someone knows. please make an issue in the repo, and maybe we can get it sorted out.
3938

4039
Unlikely (due to not sure if people are using):
4140
- [ ] tabpage
4241

4342
## Progress
4443

44+
Suported Functions:
45+
46+
- [x] popup.create
47+
- [x] popup.move
48+
- [ ] popup.close
49+
- [ ] popup.clear
50+
51+
4552
Suported Features:
4653

4754
- [x] what
4855
- string
4956
- list of strings
57+
- bufnr
5058
- [x] popup_create-arguments
5159
- [x] border
5260
- [x] borderchars
@@ -69,6 +77,25 @@ Suported Features:
6977
- [x] title
7078
- [x] wrap
7179
- [x] zindex
80+
- [x] callback
81+
- [ ] mousemoved
82+
- [ ] "any"
83+
- [ ] "word"
84+
- [ ] "WORD"
85+
- [ ] "expr"
86+
- [ ] (list options)
87+
- [?] close
88+
- [ ] "button"
89+
- [ ] "click"
90+
- [x] "none"
91+
92+
93+
Additional Features:
94+
95+
- [x] enter
96+
- [x] focusable
97+
- [x] noautocmd
98+
- [x] finalize_callback
7299

73100
## All known unimplemented vim features at the moment
74101

@@ -79,10 +106,7 @@ Suported Features:
79106
- filter
80107
- filtermode
81108
- mapping
82-
- callback
83109
- mouse:
84-
- mousemoved
85-
- close
86110
- drag
87111
- resize
88112

lua/plenary/popup/init.lua

+96-30
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ popup._hidden = {}
2626
-- Keep track of popup borders, so we don't have to pass them between functions
2727
popup._borders = {}
2828

29+
-- Callbacks to be called later by popup.execute_callback. Indexed by win_id.
30+
popup._callback_fn = {}
31+
32+
-- Result is passed to the callback. Indexed by win_id. See popup_win_closed.
33+
popup._result = {}
34+
2935
local function dict_default(options, key, default)
3036
if options[key] == nil then
3137
return default[key]
@@ -34,9 +40,6 @@ local function dict_default(options, key, default)
3440
end
3541
end
3642

37-
-- Callbacks to be called later by popup.execute_callback
38-
popup._callbacks = {}
39-
4043
-- Convert the positional {vim_options} to compatible neovim options and add them to {win_opts}
4144
-- If an option is not given in {vim_options}, fall back to {default_opts}
4245
local function add_position_config(win_opts, vim_options, default_opts)
@@ -112,6 +115,69 @@ local function add_position_config(win_opts, vim_options, default_opts)
112115
-- , contents on the screen. Set to TRUE to disable this.
113116
end
114117

118+
--- Closes the popup window
119+
--- Adapted from vim.lsp.util.close_preview_autocmd
120+
---
121+
---@param winnr integer window id of popup window
122+
---@param bufnrs table|nil optional list of ignored buffers
123+
local function close_window(winnr, bufnrs)
124+
vim.schedule(function()
125+
-- exit if we are in one of ignored buffers
126+
if bufnrs and vim.list_contains(bufnrs, vim.api.nvim_get_current_buf()) then
127+
return
128+
end
129+
130+
local augroup = "popup_window_" .. winnr
131+
pcall(vim.api.nvim_del_augroup_by_name, augroup)
132+
pcall(vim.api.nvim_win_close, winnr, true)
133+
end)
134+
end
135+
136+
--- Creates autocommands to close a popup window when events happen.
137+
---
138+
---@param events table list of events
139+
---@param winnr integer window id of popup window
140+
---@param bufnrs table list of buffers where the popup window will remain visible, {popup, parent}
141+
---@see autocmd-events
142+
local function close_window_autocmd(events, winnr, bufnrs)
143+
local augroup = vim.api.nvim_create_augroup("popup_window_" .. winnr, {
144+
clear = true,
145+
})
146+
147+
-- close the popup window when entered a buffer that is not
148+
-- the floating window buffer or the buffer that spawned it
149+
vim.api.nvim_create_autocmd("BufEnter", {
150+
group = augroup,
151+
callback = function()
152+
close_window(winnr, bufnrs)
153+
end,
154+
})
155+
156+
if #events > 0 then
157+
vim.api.nvim_create_autocmd(events, {
158+
group = augroup,
159+
buffer = bufnrs[2],
160+
callback = function()
161+
close_window(winnr)
162+
end,
163+
})
164+
end
165+
end
166+
--- End of code adapted from vim.lsp.util.close_preview_autocmd
167+
168+
--- Only used from 'WinClosed' autocommand
169+
--- Cleanup after popup window closes.
170+
---@param win_id integer window id of popup window
171+
local function popup_win_closed(win_id)
172+
-- Invoke the callback with the win_id and result.
173+
if popup._callback_fn[win_id] then
174+
pcall(popup._callback_fn[win_id], win_id, popup._result[win_id])
175+
popup._callback_fn[win_id] = nil
176+
end
177+
-- Forget about this window.
178+
popup._result[win_id] = nil
179+
end
180+
115181
function popup.create(what, vim_options)
116182
vim_options = vim.deepcopy(vim_options)
117183

@@ -236,19 +302,38 @@ function popup.create(what, vim_options)
236302

237303
local win_id
238304
if vim_options.hidden then
239-
assert(false, "I have not implemented this yet and don't know how")
305+
assert(false, "hidden: not implemented yet and don't know how")
240306
else
241307
win_id = vim.api.nvim_open_win(bufnr, false, win_opts)
242308
end
243309

310+
-- Set the default result. Also serves to indicate active popups.
311+
popup._result[win_id] = -1
312+
-- Always catch the popup's close
313+
local augroup = vim.api.nvim_create_augroup("popup_close_" .. win_id, {
314+
clear = true,
315+
})
316+
vim.api.nvim_create_autocmd("WinClosed", {
317+
group = augroup,
318+
pattern = tostring(win_id),
319+
callback = function()
320+
pcall(vim.api.nvim_del_augroup_by_name, augroup)
321+
popup_win_closed(win_id)
322+
end,
323+
})
324+
244325
-- Moved, handled after since we need the window ID
245326
if vim_options.moved then
246327
if vim_options.moved == "any" then
247-
vim.lsp.util.close_preview_autocmd({ "CursorMoved", "CursorMovedI" }, win_id)
248-
-- elseif vim_options.moved == "word" then
249-
-- TODO: Handle word, WORD, expr, and the range functions... which seem hard?
328+
close_window_autocmd({ "CursorMoved", "CursorMovedI" }, win_id, { bufnr, vim.fn.bufnr() })
329+
--[[
330+
else
331+
-- TODO: Handle word, WORD, expr, and the range functions... which seem hard?
332+
assert(false, "moved ~= 'any': not implemented yet and don't know how")
333+
]]
250334
end
251335
else
336+
-- TODO: If the buffer's deleted close the window. Is this needed?
252337
local silent = false
253338
vim.cmd(
254339
string.format(
@@ -397,7 +482,7 @@ function popup.create(what, vim_options)
397482
-- enter
398483
local should_enter = vim_options.enter
399484
if should_enter == nil then
400-
should_enter = true
485+
should_enter = false
401486
end
402487

403488
if should_enter then
@@ -412,22 +497,10 @@ function popup.create(what, vim_options)
412497

413498
-- callback
414499
if vim_options.callback then
415-
popup._callbacks[bufnr] = function()
416-
-- (jbyuki): Giving win_id is pointless here because it's closed right afterwards
417-
-- but it might make more sense once hidden is implemented
418-
local row, _ = unpack(vim.api.nvim_win_get_cursor(win_id))
419-
vim_options.callback(win_id, what[row])
420-
vim.api.nvim_win_close(win_id, true)
421-
end
422-
vim.api.nvim_buf_set_keymap(
423-
bufnr,
424-
"n",
425-
"<CR>",
426-
'<cmd>lua require"plenary.popup".execute_callback(' .. bufnr .. ")<CR>",
427-
{ noremap = true }
428-
)
500+
popup._callback_fn[win_id] = vim_options.callback
429501
end
430502

503+
-- TODO: Wonder what this is about? Debug? Convenience to get bufnr?
431504
if vim_options.finalize_callback then
432505
vim_options.finalize_callback(win_id, bufnr)
433506
end
@@ -478,12 +551,5 @@ function popup.move(win_id, vim_options)
478551
end
479552
end
480553

481-
function popup.execute_callback(bufnr)
482-
if popup._callbacks[bufnr] then
483-
local wrapper = popup._callbacks[bufnr]
484-
wrapper()
485-
popup._callbacks[bufnr] = nil
486-
end
487-
end
488-
489554
return popup
555+
-- vim:sw=2 ts=2 et

lua/plenary/popup/utils.lua

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ utils.bounded = function(value, min, max)
1414
return value
1515
end
1616

17+
-- TODO: Should defaults get deepcopy before table values are used?
18+
-- utils.apply_defaults is never used AFAICT.
19+
-- So I guess this comment is about plenary/tbl.lua.
1720
utils.apply_defaults = function(original, defaults)
1821
if original == nil then
1922
original = {}

0 commit comments

Comments
 (0)