"green")/>"
#->
A `Symbol` or `String` inside a tag is an empty attribute.
@htl "
"
#->
#? VERSION >= v"1.6.0-DEV"
@htl "
"
#->
To expand an object into a set of attributes, implement `inside_tag()`.
For example, let's suppose we have an object that represents both a list
of CSS classes and a custom style.
using HypertextLiteral: attribute_pair, Reprint
struct CustomCSS class::Vector{Symbol}; style end
HypertextLiteral.inside_tag(s::CustomCSS) = begin
myclass = join((string(x) for x in s.class), " ")
Reprint() do io::IO
print(io, attribute_pair(:class, myclass))
print(io, attribute_pair(:style, s.style))
end
end
style = CustomCSS([:one, :two], :background_color => "#92a8d1")
print(@htl "
Hello
")
#->
Hello
## Style Tag
Within a `"""
#->
In this context, content is validated to ensure it doesn't contain
`""`.
expr = """"""
@htl ""
#-> …ERROR: "Content within a style tag must not contain ``"⋮
## Edge Cases
Attribute names should be non-empty and not in a list of excluded
characters.
@htl "
"value")/>"
#-> ERROR: LoadError: "Attribute name must not be empty."⋮
@htl " "value")/>"
#=>
ERROR: LoadError: DomainError with &att:
Invalid character ('&') found within an attribute name.⋮
=#
We don't permit adjacent unquoted attribute values.
@htl("
ERROR: LoadError: DomainError with :invalid:
Unquoted attribute interpolation is limited to a single component⋮
=#
Unquoted interpolation adjacent to a raw string is also an error.
@htl("
ERROR: LoadError: DomainError with :invalid:
Unquoted attribute interpolation is limited to a single component⋮
=#
@htl("
ERROR: LoadError: DomainError with bare=literal:
Unquoted attribute interpolation is limited to a single component⋮
=#
Ensure that dictionary style objects are serialized. See issue #7.
let
h = @htl(" "red"))>asdf
")
repr(MIME"text/html"(), h)
end
#-> "asdf
"
Let's ensure that attribute values in a dictionary are escaped.
@htl ""'&\"<"))/>"
#->
When we normalize attribute names, we strip leading underscores.
@htl " :value)/>"
#->
We don't expand into attributes things that don't look like attributes.
@htl ""
#-> ERROR: MethodError: no method matching inside_tag(::Int64)⋮
One can add additional attributes following a bare name.
@htl ""
#->
Inside a tag, tuples can have many kinds of pairs.
a1 = "a1"
@htl ""
#->
The macro attempts to expand attributes inside a tag. To ensure the
runtime dispatch also works, let's do a few things once indirect.
hello = "Hello"
defer(x) = x
@htl " hello))/>"
#->
@htl ""
#->
@htl " defer(hello))/>"
#->
@htl " hello)/>"
#->
It's a lexing error to have an attribute lacking a name.
@htl ""
#=>
ERROR: LoadError: DomainError with =value/>:
unexpected equals sign before attribute name⋮
=#
It's a lexing error to have an attribute lacking a value.
@htl ""
#=>
ERROR: LoadError: DomainError with =>:
missing attribute value⋮
=#
Attribute names and values can be spaced out.
@htl ""
#->
Invalid attribute names are reported.
@htl ""
#=>
ERROR: LoadError: DomainError with t"
#=>
ERROR: LoadError: DomainError with t'ribute=…
unexpected character in attribute name⋮
=#
@htl """"""
#=>
ERROR: LoadError: DomainError with t"ribute=…
unexpected character in attribute name⋮
=#
While assignment operator is permitted in Julia string interpolation, we
exclude it to guard it against accidently forgetting a comma.
@htl ""
#->
@htl("")
#=>
ERROR: LoadError: DomainError with data_value = 42:
assignments are not permitted in an interpolation⋮
=#
@htl("")
#=>
ERROR: LoadError: DomainError with data_value = 42:
assignments are not permitted in an interpolation⋮
=#
Interpolation of adjacent values should work.
x = 'X'; y = 'Y';
@htl("")
#->