Skip to content

Commit 8e456c7

Browse files
committed
feat(form): add form:redraw() method
1 parent e7e0720 commit 8e456c7

2 files changed

Lines changed: 78 additions & 7 deletions

File tree

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,37 @@ end)
106106
| `form:submit()` | Gather values, close, and invoke `on_submit(results)`. |
107107
| `form:cancel()` | Close and invoke `on_cancel()` if provided. |
108108
| `form:results()` | Return `{ [name] = value }` without closing. |
109+
| `form:redraw()` | Tear down and re-open the form in place, preserving focus and values. |
110+
111+
#### `form:redraw()`
112+
113+
Use after a callback (`on_activate`, autocommand, external code) has mutated the form — added or
114+
removed inputs, changed a label, swapped styling — and you want the on-screen layout to catch up.
115+
No-op if the form isn't currently visible.
116+
117+
Preserved across the redraw:
118+
119+
- Focused input index (if it's still in range; otherwise wraps).
120+
- Cached input values (each input flushes its current value during `unmount()` before the window is
121+
destroyed, so text/multiline content is not lost).
122+
- The original window + editor mode captured on `:show()`, so a subsequent `:hide()` / `:cancel()` /
123+
`:submit()` still restores the user's original state.
124+
- Help popup open/closed state.
125+
126+
```lua
127+
local form
128+
form = require("input-form").create_form({
129+
inputs = {
130+
{ name = "first", label = "First", type = "text" },
131+
{ type = "button", label = "Add field", on_activate = function(f)
132+
table.insert(f._inputs, require("input-form.inputs").build({
133+
name = "extra", label = "Extra", type = "text",
134+
}))
135+
f:redraw()
136+
end },
137+
},
138+
}):show()
139+
```
109140

110141
### Input spec reference
111142

lua/input-form/form.lua

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,14 @@ function M:show()
147147
-- who was in normal mode before opening the form stays in insert mode after
148148
-- the form closes (because the form's text inputs leave the editor in
149149
-- insert mode when they're focused).
150-
self._prev_win = vim.api.nvim_get_current_win()
151-
self._prev_mode = vim.api.nvim_get_mode().mode
150+
--
151+
-- Skip the capture if we already have it (e.g. `redraw()` tore the windows
152+
-- down and is calling us back in), so we restore to the user's ORIGINAL
153+
-- window/mode rather than to the now-torn-down form window.
154+
if self._prev_mode == nil then
155+
self._prev_win = vim.api.nvim_get_current_win()
156+
self._prev_mode = vim.api.nvim_get_mode().mode
157+
end
152158

153159
-- Lazy: teach known UI plugins (nvim-scrollbar, satellite, ...) to skip
154160
-- form buffers. Runs once per nvim session.
@@ -270,11 +276,10 @@ function M:_apply_highlights()
270276
end
271277
end
272278

273-
--- Hide the form (close windows) but keep state so `:show()` can reopen it.
274-
function M:hide()
275-
if not self._visible then
276-
return
277-
end
279+
--- Tear down every window/buffer owned by the form. Shared by `hide()` and
280+
--- `redraw()`. Does NOT touch `_prev_win` / `_prev_mode` — the caller decides
281+
--- whether to restore the editor's prior state.
282+
function M:_teardown_windows()
278283
self:_close_help()
279284
for _, input in ipairs(self._inputs) do
280285
input:unmount()
@@ -288,10 +293,45 @@ function M:hide()
288293
self._parent_win = nil
289294
self._parent_buf = nil
290295
self._visible = false
296+
end
291297

298+
--- Hide the form (close windows) but keep state so `:show()` can reopen it.
299+
function M:hide()
300+
if not self._visible then
301+
return
302+
end
303+
self:_teardown_windows()
292304
self:_restore_editor_state()
293305
end
294306

307+
--- Tear the form down and re-open it in place. Use when callbacks mutate the
308+
--- form (input list, labels, styles, ...) and you need the layout to catch
309+
--- up. No-op if the form is not currently visible.
310+
---
311+
--- Preserves: focused-input index, the captured editor state used by
312+
--- `:hide()`'s mode restoration, and whether the help popup was open.
313+
--- Cached input values survive because each input's `unmount()` flushes
314+
--- them onto the input object before the window goes away.
315+
function M:redraw()
316+
if not self._visible then
317+
return
318+
end
319+
local saved_focus = self._focus_idx
320+
local help_was_open = self._help_win and vim.api.nvim_win_is_valid(self._help_win) and true
321+
or false
322+
323+
self:_teardown_windows()
324+
self:show()
325+
326+
-- `show()` focuses the first focusable input; put focus back where it was.
327+
if saved_focus then
328+
self:_focus(saved_focus)
329+
end
330+
if help_was_open then
331+
self:_open_help()
332+
end
333+
end
334+
295335
--- Restore the window + mode that were active before `show()` was called.
296336
--- Closing the form's floating windows would otherwise leave the editor in
297337
--- whatever mode the focused input had it in (typically insert for text /

0 commit comments

Comments
 (0)