u.nvim/examples/form.lua
Jonathan Apodaca 103270a241
All checks were successful
NeoVim tests / code-quality (push) Successful in 1m21s
better extmark inclusivity
2025-06-17 23:37:16 -06:00

118 lines
3.6 KiB
Lua

-- form.lua:
--
-- This is a runnable example of a form. Open this file in Neovim, and execute
-- `:luafile %` to run it. It will create a new buffer to the side, and render
-- an interactive form. Edit the "inputs" between the `[...]` brackets, and
-- watch the buffer react immediately to your changes.
--
local Renderer = require('u.renderer').Renderer
local h = require('u.renderer').h
local tracker = require 'u.tracker'
-- Create a new, temporary, buffer to the side:
vim.cmd.vnew()
vim.bo.buftype = 'nofile'
vim.bo.bufhidden = 'wipe'
vim.bo.buflisted = false
local renderer = Renderer.new()
-- Create two signals:
local s_name = tracker.create_signal 'whoever-you-are'
local s_age = tracker.create_signal 'ideally-a-number'
-- We can create derived information from the signals above. Say we want to do
-- some validation on the input for `age`: we can do that with a memo:
local s_age_info = tracker.create_memo(function()
local age_raw = s_age:get()
local age_digits = age_raw:match '^%s*(%d+)%s*$'
local age_n = age_digits and tonumber(age_digits) or nil
return {
type = age_n and 'number' or 'string',
raw = age_raw,
n = age_n,
n1 = age_n and age_n + 1 or nil,
}
end)
-- This is the render effect that depends on the signals created above. This
-- will re-run every time one of the signals changes.
tracker.create_effect(function()
local name = s_name:get()
local age = s_age:get()
local age_info = s_age_info:get()
-- Each time the signals change, we re-render the buffer:
renderer:render {
h.Type({}, '# Form Example'),
'\n\n',
-- We can also listen for when specific locations in the buffer change, on
-- a tag-by-tag basis. This gives us two-way data-binding between the
-- buffer and the signals.
{
'Name: ',
h.Structure({
on_change = function(text) s_name:set(text) end,
}, name),
},
{
'\nAge: ',
h.Structure({
on_change = function(text) s_age:set(text) end,
}, age),
},
'\n\n',
-- Show the values of the signals here, too, so that we can see the
-- reactivity in action. If you change the values in the tags above, you
-- can see the changes reflected here immediately.
{ 'Hello, "', name, '"!' },
--
-- A more complex example: we can do much more complex rendering, based on
-- the state. For example, if you type different values into the `age`
-- field, you can see not only the displayed information change, but also
-- the color of the highlights in this section will adapt to the type of
-- information that has been detected.
--
-- If string input is detected, values below are shown in the
-- `String`/`ErrorMsg` highlight groups.
--
-- If number input is detected, values below are shown in the `Number`
-- highlight group.
--
-- If a valid number is entered, then this section also displays how old
-- you willl be next year (`n + 1`).
--
'\n\n',
h.Type({}, '## Computed Information (derived from `age`)'),
'\n\n',
{
'Type: ',
h('text', {
hl = age_info.type == 'number' and 'Number' or 'String',
}, age_info.type),
},
{ '\nRaw input: ', h.String({}, '"' .. age_info.raw .. '"') },
{
'\nCurrent age: ',
age_info.n
-- Show the age:
and h.Number({}, tostring(age_info.n))
-- Show an error-placeholder if the age is invalid:
or h.ErrorMsg({}, '(?)'),
},
-- This part is shown conditionally, i.e., only if the age next year can be
-- computed:
age_info.n1
and {
'\nAge next year: ',
h.Number({}, tostring(age_info.n1)),
},
}
end)