-- 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' -- Utility to trim brackets from strings: local function trimb(s) return (s:gsub('^%[(.*)%]$', '%1')) end -- 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 = trimb(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), }, '\n', { 'Age: ', 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, "', trimb(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)