Skip to content

Commit

Permalink
Optimise template functions containing static attributes (#21)
Browse files Browse the repository at this point in the history
When an element contains "static" attributes don't wait until runtime to render them to a `String`, and instead do it during macro expansion to save rerunning it on every single call to the template function.
  • Loading branch information
MichaelHatherly authored Jan 5, 2024
1 parent 054100b commit df4d29a
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 21 deletions.
73 changes: 52 additions & 21 deletions src/nodes/element.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ end
function expression(c::BuilderContext, a::Attribute)
name = Symbol(a.name)
if name == :...
return Expr(:(...), Meta.parse(a.value))
return Expr(:..., Meta.parse(a.value))
elseif a.dynamic
return Expr(:(kw), name, Meta.parse(a.value))
return Expr(:kw, name, Meta.parse(a.value))
elseif a.interpolate
return Expr(:(kw), name, Meta.parse("\"\"\"$(a.value)\"\"\""))
return Expr(:kw, name, Meta.parse("\"\"\"$(a.value)\"\"\""))
else
return Expr(:kw, name, a.value)
end
Expand Down Expand Up @@ -135,31 +135,32 @@ const VOID_ELEMENTS = [
function expression(c::BuilderContext, e::Element)
attrs = expression(c, e.attributes)
if e.name in VALID_HTML_ELEMENTS || e.name in VALID_SVG_ELEMENTS
attrs, static_attrs = _split_attributes(attrs)
opening_tag = "<$(e.name)"
opening_expr = quote
print($(c.io), $(opening_tag))
$(print_attributes)(
$(c.io),
$(static_attrs);
$(_data_filename_attr)($(c.file), $(e.line))...,
$(attrs...),
)
end
if e.name in VOID_ELEMENTS
quote
print($(c.io), "<", $(e.name))
$(print_attributes)(
$(c.io);
$(_data_filename_attr)($(c.file), $(e.line))...,
$(attrs...),
)
$(opening_expr)
print($(c.io), "/>")
end |> lln_replacer(c.file, e.line)
end
else
name = Symbol(e.name)
closing_tag = "</$(e.name)>"
body = expression(c, e.body)
quote
print($(c.io), "<", $(e.name))
$(print_attributes)(
$(c.io);
$(_data_filename_attr)($(c.file), $(e.line))...,
$(attrs...),
)
$(opening_expr)
print($(c.io), ">")
$(body)
print($(c.io), "</", $(e.name), ">")
end |> lln_replacer(c.file, e.line)
end
print($(c.io), $(closing_tag))
end
end |> lln_replacer(c.file, e.line)
else
name = Symbol(e.name)
function builder(slot, body)
Expand All @@ -181,12 +182,42 @@ function expression(c::BuilderContext, e::Element)
end
end

# Optimised printing of attributes that we know are "static" within the template
# functions, e.g. they are just strings. We print them all to a single `String`
# during macro expansion time and then just interpolate that string into the
# template function. This avoids runtime overhead of printing the attributes one
# by one every single time the template function is called.
function _split_attributes(attrs::Vector{Expr})
dynamic_attrs = Expr[]
static_attrs = Pair{Symbol,String}[]
for each in attrs
if _is_static_attribute(each)
push!(static_attrs, each.args[1] => each.args[2])
else
push!(dynamic_attrs, each)
end
end
if isempty(static_attrs)
return dynamic_attrs, ""
else
buffer = IOBuffer()
print_attributes(buffer; static_attrs...)
return dynamic_attrs, String(take!(buffer))
end
end

_is_static_attribute(ex::Expr) =
Meta.isexpr(ex, :kw, 2) && isa(ex.args[1], Symbol) && isa(ex.args[2], String)

# Used in `HypertextTemplatesReviseExt` to toggle the `data-htloc` attribute
# on and off during tests. Not a public API, do not rely on this.
const _DATA_FILENAME_ATTR = Ref(true)
_data_filename_attr(::Any, line) = (;)

function print_attributes(io::IO; attrs...)
function print_attributes(io::IO, static_attrs::String = ""; attrs...)
if !isempty(static_attrs)
print(io, static_attrs)
end
for (k, v) in attrs
if v isa AbstractString && k === Symbol(v)
print(io, " ", k)
Expand Down
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ end
props = (; option = "option"),
)

@test_reference joinpath(basic, "svg.1.txt") render(Templates.var"svg-content")

@test_throws_st UndefVarError render(Templates.var"file-and-line-info-1") [
"file-and-line-info.html:2",
"file-and-line-info.html:1",
Expand Down
1 change: 1 addition & 0 deletions test/templates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ template"templates/basic/custom-elements.html"
template"templates/basic/splat.html"
template"templates/basic/splatted-props.html"
template"templates/basic/file-and-line-info.html"
template"templates/basic/svg.html"

module Complex

Expand Down
1 change: 1 addition & 0 deletions test/templates/basic/svg.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12a7.5 7.5 0 0 0 15 0m-15 0a7.5 7.5 0 1 1 15 0m-15 0H3m16.5 0H21m-1.5 0H12m-8.457 3.077 1.41-.513m14.095-5.13 1.41-.513M5.106 17.785l1.15-.964m11.49-9.642 1.149-.964M7.501 19.795l.75-1.3m7.5-12.99.75-1.3m-6.063 16.658.26-1.477m2.605-14.772.26-1.477m0 17.726-.26-1.477M10.698 4.614l-.26-1.477M16.5 19.794l-.75-1.299M7.5 4.205 12 12m6.894 5.785-1.149-.964M6.256 7.178l-1.15-.964m15.352 8.864-1.41-.513M4.954 9.435l-1.41-.514M12.002 12l-3.75 6.495"></path></svg>
16 changes: 16 additions & 0 deletions test/templates/basic/svg.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<function svg-content>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.5 12a7.5 7.5 0 0 0 15 0m-15 0a7.5 7.5 0 1 1 15 0m-15 0H3m16.5 0H21m-1.5 0H12m-8.457 3.077 1.41-.513m14.095-5.13 1.41-.513M5.106 17.785l1.15-.964m11.49-9.642 1.149-.964M7.501 19.795l.75-1.3m7.5-12.99.75-1.3m-6.063 16.658.26-1.477m2.605-14.772.26-1.477m0 17.726-.26-1.477M10.698 4.614l-.26-1.477M16.5 19.794l-.75-1.299M7.5 4.205 12 12m6.894 5.785-1.149-.964M6.256 7.178l-1.15-.964m15.352 8.864-1.41-.513M4.954 9.435l-1.41-.514M12.002 12l-3.75 6.495"
/>
</svg>
</function>

0 comments on commit df4d29a

Please sign in to comment.