kdl
kdl copied to clipboard
XSLT-style templating spec
We already have the equivalent of JSON Schema/XSD in the KDL Schema spec, but we don't have something that would map to XSLT--that is, a templating language for using KDL to transform input KDL into other KDL documents--or even non-kdl documents?
I'm not really sure how big a task this would be. I've never used XSLT in anger but I imagine there's quite a bit to it.
With the disclaimer that I have never used XSLTs...
A big pain point we have in Zellij is the repetition created in our layouts. We have pane_templates and tab_templates etc. but they mostly have to live inside the same file.
A long standing TODO I have is to find some sort of mechanism to solve this but I've mostly been putting this off because I don't want to deal with the usual dependency problems. I think these sorts of capabilities (especially if they'll be built in to the existing SDKs) can take away a lot of this work from me.
@imsnif so, as far as I can tell/imagine, KDL templating would be a lot like your pane_template and tab_template, except it would be "official" and be a little more powerful: it would let you, for example, use KQL selectors to do surgical insertions into more complex templates, for example, rather than limiting you to children. And KQL selectors are about as powerful as CSS selectors (modulo some of the more advanced special CSS selectors, but we should probably add those eventually too). They're mostly the same syntax, too.
And yeah, it would be easy enough to put them in separate files that you pass directly to kdl-rs, and it'll apply the templates to a particular document parse for you, transforming things as needed. And, I would hope, giving you informative errors.
For example:
// my-templates.kdl
$:template match=vertical-sandwich { // KQL selector
pane split_direction=vertical {
$:select "[pos = above]"
$:select "[pos != above][pos != below]"
$:select "[pos = below]"
}
}
$:template match=cmd {
pane command="{$:join $:args ,}"
}
$:template match=watch {
cmd cargo watch -x $:args
}
// layout.kdl
// This isn't magic. Zellij itself would be responsible for
// loading these files and passing them to kdl-rs
@include "./my-templates.kdl"
vertical-sandwich {
cmd pos=above fancy-line --color red --width 10
cmd pos=below htop
pane split_direction=horizontal {
watch test
watch check
}
}
We could, of course, go further. We could have a whole module system of imports/exports where you can distribute "libraries" of templates and everything. You could also get a lot fancier with how you select arguments and values and interpolate them around. Here's a fun one:
$:template match=group {
$:select "[]" {
$:apply $:entries
}
}
group pos=above {
cmd foo
cmd bar
cmd baz
}
// Expands to:
cmd foo pos=above
cmd bar pos=above
cmd baz pos=above
how does that sound so far?
A XSLT-like styling language would, by default, recursively process all nodes (which if empty, would yield nothing) .
So the default behavior of an XSLT processor on:
<foo>
text1
<bar>text 2</bar>
<baz/>
</foo>
would be
text1
text2
See: https://www.w3.org/TR/1999/REC-xslt-19991116#built-in-rule
The pattern in XSLT is to define templates for the elements you want to transform (via custom output)
So if your code above would take a KML file, like the one in your example:
group pos=above {
cmd foo
cmd bar
cmd baz
}
and produce, by default
foo
bar
baz
and, assuming that cmd is contextually the same as an element, then you could extend the implementation by allowing the user to specify templates which match nodes: ie group[pos='above'] in an XPath-y syntax, yielding the expected output.
@emceeaich In my examples, above, the definition for cmd was given:
$:template match=cmd {
pane command="{$:join $:args ,}"
}
so the final expansion would be:
pane command=foo pos=above
pane command=bar pos=above
pane command=baz pos=above
the pos properties are applied by group itself (with the $:apply node)
That makes sense, my example was me thinking in the default behavior of XSLT.
The default behavior in XSLT is to recursively process the tree, applying the default template, unless the stylesheet contains templates that match some expression in the document, if it exists.
yeah. I think in my mind, since KDL isn't a markup language that's intended to be a layer on top of plain text, it makes more sense for us to be more explicit about how the data is passed around and applied. And it'll give us nicer errors to boot, imo.
I guess the catch with this is that you couldn't, for example, use this templating system to generate non-KDL templates. In a way, this is more a "function definition" system than a "templating" system.
Yes, setting the scope of what you expect it to do is the right thing. I do hope you find some of the ideas in XSLT useful.
Hey @zkat - thanks for the detailed run-down. This indeed looks really cool. I initially thought this might help with the "magic" part you were referring to in the comments (that's the big pain point in this area as I'm not enthusiastic about implementing dependency resolution and all that is involved there).
Would be cool to also have these features though, but one thing at a time :) I still think this is a super useful addition to KDL.
Yeah I think we could provide some helpers around “including” templates