FlowContent and PhrasingContent separation is useless
See original GitHub issueApparently those contents come from XSD, but in the end of the day, they are quite useless and very confusing in kotlinx.html.
It does not help with static type check
if I write something invalid according to XSD, like body > span > div
body { // BODY creates FlowContent
span { // SPAN creates PhrasingContent
div { // DIV can only be created in _FlowContent_, not in SPAN
// DIV is still allowed, because it is actually created in BODY,
// not in innermost parent SPAN
}
}
}
it is still silently accepted by the compiler and even leads to obscure IllegalStateException("You can't change tag attribute because it was already passed to the downstream") if I try to change attributes.
To me this seems like a general problem of ‘fall-through’ builder design.
It breaks the abstraction
Both functions, FlowContent.i and PhrasingContent.i are identical: they produce the same I object.
But there’s no common abstraction between those two functions. Which leads to the third problem.
It prevents reusable code
I chose to use Tag as a basis for my DSL, which seemed like a good base class. But in the end I had to write the ugliest code in a decade of my career:
fun Tag.mdl_icon(icon: String, text: String? = null) {
if (this is FlowContent) {
i("material-icons") {
+icon
}
if (text != null) {
span(classes = "visuallyhidden") {
+text
}
}
} else if (this is PhrasingContent) {
i("material-icons") {
+icon
}
if (text != null) {
span(classes = "visuallyhidden") {
+text
}
}
}
}
Both if and else part are identical, but functions i and span are different.
Condition like if (this is FlowContent || this is PhrasingContent) cannot work, of course.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:4
- Comments:7 (5 by maintainers)

Top Related StackOverflow Question
Very simple suggestion is to declare all tag-functions for the
Tagclass and discard *Content. We’ll have exactly oneSPANclass and exactly oneTag.spanfunction.Pros:
Cons:
You can use
FlowContentorHtmlBlockTagfor that purpose. Just need to be documented