Markup: pages, cache & validation
lib/markup/tui_markup.sh, lib/markup/tui_cache.sh, lib/markup/tui_validate.sh - the XML page loader, its record/replay cache, and the validator that checks pages before an app starts. Guide: ../guide/markup.md. ← API index for the full module list and a task-oriented tour with examples.
Functions return 0 unless their entry says otherwise.
Pages
| Function | Summary |
|---|---|
tui.load |
Parses a markup page and builds its panes and widgets through tui.* calls. Nothing is drawn. |
tui.goto |
Switches to another page: clears the current UI, loads FILE (from the page cache when valid) and repaints in one frame. |
tui.reset_ui |
Clears the whole UI and leaves an empty full-screen root pane. tui.goto calls it. |
tui.load_cached |
Like tui.load, but replays the page from its recorded call log when it is cached and unchanged, and records it otherwise. |
tui.load
tui.load FILE
Parses a markup page and builds its panes and widgets through tui.* calls. Nothing is drawn.
Returns: 1 and a message on stderr when FILE can’t be read.
Notes
- Loads the framework’s default theme first, then the page’s
<theme>files. - Does not validate.
tui.startvalidates before loading; calltui.validate.filesyourself when loading pages another way. - Does not use the page cache and does not reset the current UI; use
tui.gototo switch pages.
tui.goto
tui.goto FILE
Switches to another page: clears the current UI, loads FILE (from the page cache when valid) and repaints in one frame.
Parameters
FILE: relative paths resolve against the current page’s folder.
Notes
- Everything page-scoped is dropped: panes, widgets, timers, watches,
tui.execinstances, the modal, the footer,--pagebindings. Tick listeners fromtui.tick.addand overlays are kept. - The previous page is pushed onto the history for
tui.action.back(up to 30 entries). - Reloading the same page keeps the focused widget and cursor when that widget still exists.
- The page’s
on_visitfunction runs on every visit, also when the page is replayed from the cache. - Fires the
pagehook with the resolved path.
Example
open_settings() { tui.goto settings.xml; }
tui.reset_ui
tui.reset_ui
Clears the whole UI and leaves an empty full-screen root pane. tui.goto calls it.
Notes
- Removes panes, widgets, pane output, styles, timers, watches,
tui.execinstances, the modal, dialogs, the footer and page-scoped bindings. - Keeps tick listeners, overlays, toasts, the theme class table, app-wide bindings and settings.
- Clears the screen immediately.
tui.load_cached
tui.load_cached FILE
Like tui.load, but replays the page from its recorded call log when it is cached and unchanged, and records it otherwise.
Notes
- “Unchanged” means the page and every
<include>have the mtimes they had when recorded. Callback scripts are re-sourced on every load, so edits to them apply without invalidating the cache. on_visitis never recorded: it runs fresh every time.
Page cache
A loaded page is recorded as the list of tui.* calls it made and replayed on later visits, as long as the page and its includes keep their mtimes. Why and how: ../design/write-ahead-logging-and-replay.md. Cache keys are absolute, normalized paths.
| Function | Summary |
|---|---|
tui.cache.valid |
Returns 0 when FILE has a recorded page and neither it nor any of its includes changed since, else 1. |
tui.cache.record |
Loads FILE with recording on and stores the call log and a signature of its files. |
tui.cache.replay |
Rebuilds a page by replaying its recorded call log. Returns 1 when nothing is recorded for FILE. |
tui.cache.signature |
Prints path=mtime; for every file, sorted by path, as one line without a newline. Used as the cache validity key. |
tui.cache.deps_of |
Appends FILE and every file it includes, recursively, to the array named ARRAY_NAME, as absolute paths. |
tui.cache.dump_dir |
Writes every recorded page to DIR, three files per page (.key, .cache, .sig). |
tui.cache.load_dir |
Reads pages written by tui.cache.dump_dir and drops every entry that is no longer valid. |
tui.cache.disk_dir |
Prints the folder of the persistent page cache, $TUI_HOME/cache/pages, without a newline. |
tui.cache.warm_with_spinner |
Records pages into the cache in a background worker while showing the D.A.B.T logo and a progress bar. |
tui.cache.init |
Wraps the builder functions so page loads can be recorded. Called once when tui.sh is sourced; never call it again. |
tui.cache.cleanup |
Removes the temporary stylesheet stamp folder. Called on exit. |
tui.cache.valid
tui.cache.valid FILE
Returns 0 when FILE has a recorded page and neither it nor any of its includes changed since, else 1.
Notes
FILEmust be the absolute, normalized path used as the cache key.- Runs one
statper dependency file.
tui.cache.record
tui.cache.record FILE
Loads FILE with recording on and stores the call log and a signature of its files.
Notes
- Builds the page as a side effect, exactly like
tui.load.
tui.cache.replay
tui.cache.replay FILE
Rebuilds a page by replaying its recorded call log. Returns 1 when nothing is recorded for FILE.
Notes
- Does not check whether the recording is still valid; call
tui.cache.validfirst.
tui.cache.signature
tui.cache.signature FILE...
Prints path=mtime; for every file, sorted by path, as one line without a newline. Used as the cache validity key.
tui.cache.deps_of
tui.cache.deps_of FILE ARRAY_NAME
Appends FILE and every file it includes, recursively, to the array named ARRAY_NAME, as absolute paths.
Notes
- Unreadable files and files already in the array are skipped.
Example
local -a deps=()
tui.cache.deps_of "$page" deps
tui.cache.dump_dir
tui.cache.dump_dir DIR
Writes every recorded page to DIR, three files per page (.key, .cache, .sig).
Notes
- Creates
DIR. Existing files for other pages are left alone.
tui.cache.load_dir
tui.cache.load_dir DIR
Reads pages written by tui.cache.dump_dir and drops every entry that is no longer valid.
Notes
- A missing
DIRis not an error.
tui.cache.disk_dir
tui.cache.disk_dir
Prints the folder of the persistent page cache, $TUI_HOME/cache/pages, without a newline.
Notes
dabt clear-cachedeletes it.
tui.cache.warm_with_spinner
tui.cache.warm_with_spinner PAGE...
Records pages into the cache in a background worker while showing the D.A.B.T logo and a progress bar.
Notes
- Used by
tui.start_cachedbefore the app starts. The worker runs with its output discarded; its stderr goes to a temporary folder. - Loads the recorded pages into this process when done.
tui.cache.init
tui.cache.init
Wraps the builder functions so page loads can be recorded. Called once when tui.sh is sourced; never call it again.
tui.cache.cleanup
tui.cache.cleanup
Removes the temporary stylesheet stamp folder. Called on exit.
Validation
tui.start and tui.start_cached validate every page before the terminal is taken over. Errors abort the start (dabt --ignore-invalid-xml or TUI_IGNORE_INVALID_XML=1 starts anyway and shows a notification); warnings are only logged. TUI_VALIDATE=0 skips validation. A clean result is remembered in $TUI_HOME/cache/validated, so only the first start after a change pays for the check.
The default rules live in lib/markup/tui_validate_rules.sh and use the same registration functions an app can call to add its own.
| Function | Summary |
|---|---|
tui.validate.files |
Validates pages and their includes, replacing any earlier findings. |
tui.validate.page |
Validates one page, adding to the current findings. Ids are checked per page. |
tui.validate.report |
Prints every finding, then a N error(s), M warning(s) summary. Prints nothing when there are no findings. |
tui.validate.log |
Writes every finding to the app log (tui.log) with level error or warn. |
tui.validate.messages |
Formats every finding into the array _TV_LINES, one sentence each, in the order found. |
tui.validate.files
tui.validate.files PAGE...
Validates pages and their includes, replacing any earlier findings.
Returns: 1 when any error was found, else 0 (warnings don’t count).
Sets: TUI_V_ERRORS, TUI_V_WARNINGS.
Notes
- Reports unknown tags, unclosed or mismatched tags, missing and invalid attributes, conflicting attributes, duplicate ids, widgets in missing or split panes, grid cells out of bounds, and missing script/theme/include/page files, each with file, line and column.
tui.startandtui.start_cachedrun it automatically before the terminal is taken over.
Example
tui.validate.files config/*.xml || { tui.validate.report >&2; exit 1; }
See also: tui.validate.report
tui.validate.page
tui.validate.page FILE
Validates one page, adding to the current findings. Ids are checked per page.
Notes
- Use
tui.validate.files, which resets the findings first.
tui.validate.report
tui.validate.report
Prints every finding, then a N error(s), M warning(s) summary. Prints nothing when there are no findings.
Output: one line per finding, e.g. erroneous configuration in config/home.xml line 12 col 5: <button> needs a pane. Warnings start with questionable configuration. Errors are red and warnings yellow when stderr is a terminal.
Notes
- Writes to stdout; redirect to stderr yourself:
tui.validate.report >&2.
tui.validate.log
tui.validate.log
Writes every finding to the app log (tui.log) with level error or warn.
tui.validate.messages
tui.validate.messages
Formats every finding into the array _TV_LINES, one sentence each, in the order found.
Notes
- The formatter behind
tui.validate.reportandtui.validate.log.
Writing rules
| Function | Summary |
|---|---|
tui.validate.tag |
Declares tags the loader understands. Any other tag is reported as unknown. |
tui.validate.container |
Declares tags that may have children (<tag>…</tag>). Implies tui.validate.tag. |
tui.validate.widget |
Declares widget tags. Their id and pane are recorded for the cross-reference checks (duplicate ids, missing panes). |
tui.validate.self_closing |
Declares tags that must be written self-closing: <tag … />. |
tui.validate.require |
Declares attributes that must be present and non-empty on TAG. Repeated calls add to the list. |
tui.validate.enum |
Restricts an attribute to a list of values. * applies it to every tag. |
tui.validate.int |
Requires an attribute to be a whole number of at least MIN (which may be negative). * applies it to every tag. |
tui.validate.conflict |
Reports an error when both attributes are given on TAG. WHY is added to the message. |
tui.validate.needs |
Declares that ATTR only has an effect when OTHER="VALUE"; using it otherwise is reported. |
tui.validate.parent |
Restricts which tags may enclose TAG. - stands for the top level of the page. |
tui.validate.parent_split |
Declares that ATTR only has an effect inside a <pane split="SPLIT">. |
tui.validate.rule |
Registers a custom check. |
tui.validate.attr |
Inside an element rule: stores the value of attribute NAME in REPLY. Returns 1 when the attribute is absent. |
tui.validate.here |
Inside an element rule: stores the location of the current element (or of ATTR on it) in REPLY as file\|line\|col. |
tui.validate.error |
Inside an element rule: reports an error at the current element, or at attribute ATTR. |
tui.validate.warn |
Inside an element rule: reports a warning at the current element, or at attribute ATTR. Warnings are logged but never stop a start. |
tui.validate.error_at |
Reports an error at LOC (file\|line\|col; col may be empty). For end rules, which have no current element. |
tui.validate.warn_at |
Reports a warning at LOC (file\|line\|col). |
tui.validate.tag
tui.validate.tag TAG...
Declares tags the loader understands. Any other tag is reported as unknown.
tui.validate.container
tui.validate.container TAG...
Declares tags that may have children (<tag>…</tag>). Implies tui.validate.tag.
tui.validate.widget
tui.validate.widget TAG...
Declares widget tags. Their id and pane are recorded for the cross-reference checks (duplicate ids, missing panes).
tui.validate.self_closing
tui.validate.self_closing TAG...
Declares tags that must be written self-closing: <tag … />.
tui.validate.require
tui.validate.require TAG ATTR...
Declares attributes that must be present and non-empty on TAG. Repeated calls add to the list.
tui.validate.enum
tui.validate.enum TAG|* ATTR "a|b|c"
Restricts an attribute to a list of values. * applies it to every tag.
Example
tui.validate.enum pane border "none|single|double|heavy"
tui.validate.int
tui.validate.int TAG|* ATTR MIN
Requires an attribute to be a whole number of at least MIN (which may be negative). * applies it to every tag.
tui.validate.conflict
tui.validate.conflict TAG ATTR_A ATTR_B [WHY]
Reports an error when both attributes are given on TAG. WHY is added to the message.
tui.validate.needs
tui.validate.needs TAG ATTR OTHER VALUE
Declares that ATTR only has an effect when OTHER="VALUE"; using it otherwise is reported.
tui.validate.parent
tui.validate.parent TAG PARENT...
Restricts which tags may enclose TAG. - stands for the top level of the page.
tui.validate.parent_split
tui.validate.parent_split TAG ATTR SPLIT
Declares that ATTR only has an effect inside a <pane split="SPLIT">.
tui.validate.rule
tui.validate.rule page|element|end FN
Registers a custom check.
Parameters
page:FNruns once before a page is walked.element:FNruns for every element, withTUI_V_FILE,TUI_V_LINE,TUI_V_COL,TUI_V_TAG,TUI_V_RAW,TUI_V_SELFCLOSE,TUI_V_DEPTH,TUI_V_PARENT_TAG,TUI_V_PARENT_ID,TUI_V_PARENT_SPLIT,TUI_V_GRANDPARENT_SPLITset. Read attributes withtui.validate.attr.end:FNruns after the walk, withTUI_V_PAGEand the mapsTUI_V_PANES,TUI_V_PANE_SPLIT,TUI_V_WIDGETS,TUI_V_WIDGET_PANE,TUI_V_WIDGET_PANE_AT(locations arefile|line|col).
Returns: 1 for another kind.
Notes
- Report with
tui.validate.error/tui.validate.warn. - Register rules after sourcing
tui.shand beforetui.start. - A clean validation is remembered per page set and file mtimes. After adding a rule, pages that already passed are not checked again until a page or include changes; delete
$TUI_HOME/cache/validated(or rundabt clear-cache) to force it.
Example
no_empty_title() {
[[ "$TUI_V_TAG" == pane ]] || return 0
tui.validate.attr title && [[ -z "$REPLY" ]] && tui.validate.warn "empty title" title
return 0
}
tui.validate.rule element no_empty_title
tui.validate.attr
tui.validate.attr NAME
Inside an element rule: stores the value of attribute NAME in REPLY. Returns 1 when the attribute is absent.
tui.validate.here
tui.validate.here [ATTR]
Inside an element rule: stores the location of the current element (or of ATTR on it) in REPLY as file|line|col.
tui.validate.error
tui.validate.error MSG [ATTR]
Inside an element rule: reports an error at the current element, or at attribute ATTR.
tui.validate.warn
tui.validate.warn MSG [ATTR]
Inside an element rule: reports a warning at the current element, or at attribute ATTR. Warnings are logged but never stop a start.
tui.validate.error_at
tui.validate.error_at LOC MSG
Reports an error at LOC (file|line|col; col may be empty). For end rules, which have no current element.
tui.validate.warn_at
tui.validate.warn_at LOC MSG
Reports a warning at LOC (file|line|col).
