Neomacs maintain reactive DOMs based on lwcells
. This enables observers and computed attributes to update in real-time depending on DOM content.
This section documents the low-level node classes making up Neomacs's reactive DOM. Note that the interface here is low-level in the sense that text-node
's are being exposed. The majority of Neomacs API hides text-node
's as an implementation detail and the DOM conceptually consists of element
's and character
's.
node
inherits (standard-object)
parent
next-sibling
previous-sibling
host
text-node
inherits (node)
text
element
inherits (node)
first-child
last-child
tag-name
attributes
invisible-p
element-p
(object)
text-node-p
(object)
tag-name-p
(node tag-name)
clone-node
(node &optional deep)
child-nodes
(node)
children
(node)
text-content
(node)
get-elements-by-class-name
(node class)
do-dom
(function node)
element
s and text-node
s. Returns NODE.do-elements
(function node)
do-dom
, but only call FUNCTION on element
s. Returns NODE.next-node
(node)
previous-node
(node)
In Neomacs, every attribute is backed by a cell. Other cells can depend on attribute value, and attribute values themselves can be computed and updated from other cell values. The following functions get and set attribute values or functions for compute them.
attribute
(element name)
set-attribute-function
(element attribute function)
Attributes can have any name. Those named by strings are kept in sync with renderer-side attributes with the same names, i.e. changes in Lisp side are pushed to renderer (but not the other way). Those with non-string names (typically symbols) instead have no counterpart on renderer side.
Attribute related utility functions:
add-class
(element class)
remove-class
(element class)
class-p
(node class &rest more-classes)
Some attribute name has special meaning:
read-only
:keymap
:keymap
and will be added to the buffer's active keymaps when buffer focus is inside this element. It takes precedence over any keymap
attribute of outer elements, and over any mode keymaps. See looking up key bindings for more details about key binding look up process.Low-level DOM edits
This section documents low-level primitives for modifying Lisp-side DOM. They are used to implement programmer-facing editing operations, see Editing primitives.
insert-before
(parent new-node reference)
append-child
(parent new-node)
append-children
(parent children)
remove-node
(node)
It is imperative that Lisp-side and renderer-side DOM are consistent. Otherwise, editing operations may behave erroneously and inconsistency can be propagated and amplified. All belts are off in such case (which can be recovered only by re-opening the buffer, or in some cases revert-buffer
).
Normally, all programmer-facing editing operations (Editing primitives) maintain consistency, under the assumption that the requested editing operations only ever result in valid HTML. If attempts are made to create invalid HTML, the renderer will usually perform "fix-up"s to restore the DOM to valid HTML, but currently Neomacs has no way to know about these. Note that creating invalid HTML then fix them in with-post-command
(Editing hooks) is not acceptable. Examples of operations that might shoot you in the foot:
<p>
element inside another <p>
element.<li>
element without surrounding <ol>
or <ul>
element.If you are using Low-level DOM edits, or debugging implementation of editing primitives, take care to enforce the following consistency rules:
element
's and text-node
's must 1-1 correspond. Parent, child and sibling (order included) relations must be exactly the same.