Jan Vladimir Mostert
Jan Vladimir Mostert

Reputation: 12972

@DSLMarker not limiting the scope of DSL

i'm building a Kotlin DSL for HTML that caters for very specific requirements i have (hence not using kotlinx.html)

        DIV(classes = "div1") {
            +"text1"
            a(href = "#0") {
                +"text2"
                div(classes = "div2") {
                    +"text3"
                    href = "#1"
                }
                div(classes = "div3") {
                    +"text4"
                    href = "#2"
                }
            }
            hr(classes = "hr1")
            span(classes = "span1") {
                +"text5"
            }
        }

In the example above, i'm able to call href in any of the child elements of a instead of having to do [email protected] = "". How do i limit the scope so that this will only be of type DIV in this example and throw a compiler error when calling href since DIV doesn't have an href property?

Here's a shortened version of the DIV class https://github.com/persephone-unframework/dsl/blob/master/src/main/kotlin/io/persephone/dsl/element/DIV.kt

@DslMarker
annotation class DivMarker

@DivMarker
class DIV(
    classes: String? = null,
    ....
    init: (DIV.() -> Unit)? = null
) : Tag(
    tagName = "div",
    selfClosing = false
) {

    fun a(
        classes: String? = null,
        ....
        init: (A.() -> Unit)? = null
    ) = A().let {

        this.children.add(it)
        ....
        init?.invoke(it)
        it
    }

    ....

}

Similarly, class A is also marked: https://github.com/persephone-unframework/dsl/blob/master/src/main/kotlin/io/persephone/dsl/element/A.kt

@DslMarker
annotation class AMarker

@AMarker
class A(
    href: String? = null,
    ...
    init: (A.() -> Unit)? = null
) : Tag(
    tagName = "a",
    selfClosing = false
) {

    fun div(
        classes: String? = null,
        init: (DIV.() -> Unit)? = null
    ) = DIV().let {

        this.children.add(it)

        ....

        init?.invoke(it)
        it
    }

    ....
}

Any idea why the @DslMarker annotation is not limiting the scope in this scenario and how i can fix it?

Upvotes: 1

Views: 1016

Answers (2)

sandro
sandro

Reputation: 11

You need to use the very same marker.

In your code you are using DivMarker and AMarker.

Contexts marked with a given "label" will hide outer contexts marked with the very same "label". They will not hide outer contexts marked with any other "label".

You might not be fully aware, but in your own answer where you tagged the base class with TagMarker you achieved sharing the same marker on all children classes. That is the reason why that code is effective.

Upvotes: 1

Jan Vladimir Mostert
Jan Vladimir Mostert

Reputation: 12972

It seems annotating the base class, in this case Tag, does the trick, that probably means all those other annotations serve no purpose?

@DslMarker
annotation class TagMarker

@TagMarker
abstract class Tag(val tagName: String, var selfClosing: Boolean = false): Element {

    val children = arrayListOf<Element>()
    val attributes = hashMapOf<String, String>()

enter image description here

Upvotes: 1

Related Questions