From e9aa68336a141944a096236dd8d793c5bf45eb1d Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 20:11:10 +0100 Subject: [PATCH 01/36] chore: Add initial Test class with main method for demonstration --- modules/core/src/main/kotlin/Test.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 modules/core/src/main/kotlin/Test.kt diff --git a/modules/core/src/main/kotlin/Test.kt b/modules/core/src/main/kotlin/Test.kt new file mode 100644 index 0000000..82a78bc --- /dev/null +++ b/modules/core/src/main/kotlin/Test.kt @@ -0,0 +1,7 @@ +class Test { + companion object { + fun main(args: Array) { + println("Hello, world!") + } + } +} \ No newline at end of file From b1766cebb36075dfb1ae97254eb32883e2e271a2 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 21:49:11 +0100 Subject: [PATCH 02/36] chore: Remove JUnit platform configuration from build.gradle and update Spotless to use leading tabs for indentation --- build.gradle | 4 ---- gradle/spotless.gradle | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 62dec83..d77be8a 100644 --- a/build.gradle +++ b/build.gradle @@ -19,10 +19,6 @@ subprojects { jvmToolchain(23) } - test { - useJUnitPlatform() - } - apply from: rootProject.file('gradle/repositories.gradle') apply from: rootProject.file('gradle/versions.gradle') apply from: rootProject.file('gradle/dependencies.gradle') diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle index 02ae737..15ddc6c 100644 --- a/gradle/spotless.gradle +++ b/gradle/spotless.gradle @@ -5,7 +5,7 @@ spotless { target '*.gradle', '.gitattributes', '.gitignore' trimTrailingWhitespace() - indentWithSpaces(4) + leadingTabsToSpaces() endWithNewline() } From b7384b4f922cabc908ba2ff59ef435e578c872e5 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 21:49:19 +0100 Subject: [PATCH 03/36] feat: Add initial DSL setup with textComponent function and corresponding tests --- .../kyoriadventuredsl/ComponentDsl.kt | 6 ++++++ .../kyoriadventuredsl/ComponentDslTest.kt | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt create mode 100644 modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt new file mode 100644 index 0000000..e427106 --- /dev/null +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt @@ -0,0 +1,6 @@ +package org.eventhorizonlab.kyoriadventuredsl + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.TextComponent + +fun textComponent(builder: TextComponent.Builder.() -> Unit) = Component.text().apply(builder).build() \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt new file mode 100644 index 0000000..99c4d22 --- /dev/null +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt @@ -0,0 +1,19 @@ +package org.eventhorizonlab.kyoriadventuredsl + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import net.kyori.adventure.text.format.NamedTextColor + +class ComponentDslTest : + StringSpec({ + "can build a simple coloured text" { + val c = + textComponent { + content("Hello World") + color(NamedTextColor.AQUA) + } + + c.content() shouldBe "Hello World" + c.color() shouldBe NamedTextColor.AQUA + } + }) \ No newline at end of file From fcc6385ad5327699a256978170f4f997f9a6c298 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 21:49:22 +0100 Subject: [PATCH 04/36] feat: Add kotest-assertions dependency for enhanced testing capabilities --- gradle/dependencies.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 0bd1c2c..6774916 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -7,12 +7,14 @@ configurations { } def dep = [ - "adventure-api": "net.kyori:adventure-api:${versions.'adventure-api'}", - "kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}" + "adventure-api" : "net.kyori:adventure-api:${versions.'adventure-api'}", + "kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}", + "kotest-assertions": "io.kotest:kotest-assertions-core:${versions.kotest}", ] dependencies { implementation dep."adventure-api" testImplementation dep.kotest + testImplementation dep."kotest-assertions" } \ No newline at end of file From bc6f9930607a9aafac1482cd63155ba388e9712f Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 21:49:31 +0100 Subject: [PATCH 05/36] chore: Remove initial Test.kt --- modules/core/src/main/kotlin/Test.kt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 modules/core/src/main/kotlin/Test.kt diff --git a/modules/core/src/main/kotlin/Test.kt b/modules/core/src/main/kotlin/Test.kt deleted file mode 100644 index 82a78bc..0000000 --- a/modules/core/src/main/kotlin/Test.kt +++ /dev/null @@ -1,7 +0,0 @@ -class Test { - companion object { - fun main(args: Array) { - println("Hello, world!") - } - } -} \ No newline at end of file From e02e179fb931b32e4f616e5328c6986eac6c9358 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 21:56:55 +0100 Subject: [PATCH 06/36] feat: Add GitHub Actions workflow for building and running Kotest tests --- .github/workflows/build.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c59d138 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,29 @@ +name: Build & Kotest +on: + push: + pull_request: + types: + - opened + - synchronize +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + + - name: Executable Gradle + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlw build + + - name: Run Kotest tests + run: ./gradlew kotest From 7d92d4341d13d43081fd2fb4c3567151cb826577 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:01:58 +0100 Subject: [PATCH 07/36] feat: Add test for textComponent function to verify builder instance --- .../kyoriadventuredsl/ComponentDslTest.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt index 99c4d22..a468d15 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt @@ -1,11 +1,20 @@ package org.eventhorizonlab.kyoriadventuredsl import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.should import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.beInstanceOf +import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor class ComponentDslTest : StringSpec({ + "textComponent this is TextComponent.Builder" { + textComponent { + this should beInstanceOf() + } + } + "can build a simple coloured text" { val c = textComponent { From 9a8fa2e20e272ddc7ecce7d10917b6ca48bb1c08 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:02:33 +0100 Subject: [PATCH 08/36] feat: Update GitHub Actions workflow to trigger on master branch --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c59d138..649a943 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,8 @@ name: Build & Kotest on: push: + branches: + - master pull_request: types: - opened @@ -23,7 +25,7 @@ jobs: run: chmod +x gradlew - name: Build with Gradle - run: ./gradlw build + run: ./gradlew build - name: Run Kotest tests run: ./gradlew kotest From a3b4938d9ca5c93e90886b29ceb6bb3543b9ee08 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:04:40 +0100 Subject: [PATCH 09/36] feat: Update GitHub Actions workflow to fetch full history --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 649a943..1747042 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,6 +13,8 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@v5 + with: + fetch-depth: 0 - name: Set up JDK 21 uses: actions/setup-java@v5 From 0e7a61622cc00b29ecb68aeb522526ea3683b42f Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:08:19 +0100 Subject: [PATCH 10/36] feat: Update Java version in GitHub Actions workflow to 24 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1747042..7a7e533 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '21' + java-version: '24' cache: 'gradle' - name: Executable Gradle From 6a8f917dff15c4e783c47e36b2cb90229388a1eb Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:30:57 +0100 Subject: [PATCH 11/36] feat: Upgrade JVM toolchain to version 24 and enhance GitHub Actions workflow with test report publishing --- .github/workflows/build.yml | 19 ++++++++++++++++++- build.gradle | 2 +- gradle/dependencies.gradle | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a7e533..bf366f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,13 +10,16 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write steps: - name: Checkout Repository uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Set up JDK 21 + - name: Set up JDK uses: actions/setup-java@v5 with: distribution: 'temurin' @@ -31,3 +34,17 @@ jobs: - name: Run Kotest tests run: ./gradlew kotest + + - name: Publish Test Report + uses: mikepenz/action-junit-report@v5 + with: + check_name: 'Kotest Test Report' + report_paths: '**/jvmKotest/TEST-*.xml' + fail_on_failure: 'true' + require_tests: 'true' + detailed_summary: 'true' + flaky_summary: 'true' + include_time_in_summary: 'true' + comment: 'true' + updateComment: 'true' + diff --git a/build.gradle b/build.gradle index d77be8a..c6bd46e 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ subprojects { apply plugin: 'org.jlleitschuh.gradle.ktlint' kotlin { - jvmToolchain(23) + jvmToolchain(24) } apply from: rootProject.file('gradle/repositories.gradle') diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 6774916..8f3648e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -9,7 +9,7 @@ configurations { def dep = [ "adventure-api" : "net.kyori:adventure-api:${versions.'adventure-api'}", "kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}", - "kotest-assertions": "io.kotest:kotest-assertions-core:${versions.kotest}", + "kotest-assertions": "io.kotest:kotest-assertions-core:${versions.kotest}" ] dependencies { From 4b8897ab403a4eb6524ff28e1e3b73f2224f89d0 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:32:41 +0100 Subject: [PATCH 12/36] feat: Add assertion to verify TextComponent instance in ComponentDslTest --- .../org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt index a468d15..b57c7cf 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt @@ -22,6 +22,7 @@ class ComponentDslTest : color(NamedTextColor.AQUA) } + c should beInstanceOf() c.content() shouldBe "Hello World" c.color() shouldBe NamedTextColor.AQUA } From 2fc31c048321cee89c5b8f99bbd2f029e341c36e Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:34:50 +0100 Subject: [PATCH 13/36] feat: Include passed tests in summary for GitHub Actions workflow --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf366f8..ce7467f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,4 +47,5 @@ jobs: include_time_in_summary: 'true' comment: 'true' updateComment: 'true' + include_passed: 'true' From 83d4e93092c20d1816d874135ab3da5473d1e5de Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 22:52:06 +0100 Subject: [PATCH 14/36] feat: Update project version to 0.0.1-PRE-ALPHA and modify test report check name in workflow --- .github/workflows/build.yml | 4 +++- build.gradle | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce7467f..cc02892 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: - name: Publish Test Report uses: mikepenz/action-junit-report@v5 with: - check_name: 'Kotest Test Report' + check_name: 'Test Report' report_paths: '**/jvmKotest/TEST-*.xml' fail_on_failure: 'true' require_tests: 'true' @@ -48,4 +48,6 @@ jobs: comment: 'true' updateComment: 'true' include_passed: 'true' + annotate_notice: 'true' + diff --git a/build.gradle b/build.gradle index c6bd46e..7b284ac 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { } group = 'org.eventhorizonlab.kyoriadventuredsl' -version = '1.0-SNAPSHOT' +version = '0.0.1-PRE-ALPHA' subprojects { apply plugin: 'org.jetbrains.kotlin.jvm' From 6c5b7c78a8d8d6adf7baab57de49aecfc3b7bfe0 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 23:18:40 +0100 Subject: [PATCH 15/36] feat: Configure Kotest with JUnit XML reporting and update test dependencies --- build.gradle | 8 ++++++++ gradle/dependencies.gradle | 10 +++++++--- .../kyoriadventuredsl/ProjectConfig.kt | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt diff --git a/build.gradle b/build.gradle index 7b284ac..4e936e1 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,14 @@ subprojects { destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs")) } + tasks.withType(Test).configureEach { + useJUnitPlatform() + reports { + junitXml.required.set(false) + } + systemProperty("gradle.build.dir", rootProject.layout.buildDirectory.asFile.get().absolutePath) + } + apply from: rootProject.file('gradle/spotless.gradle') } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 8f3648e..4f48cc7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -7,9 +7,11 @@ configurations { } def dep = [ - "adventure-api" : "net.kyori:adventure-api:${versions.'adventure-api'}", - "kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}", - "kotest-assertions": "io.kotest:kotest-assertions-core:${versions.kotest}" + "adventure-api" : "net.kyori:adventure-api:${versions.'adventure-api'}", + "kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}", + "kotest-assertions" : "io.kotest:kotest-assertions-core:${versions.kotest}", + "kotest-extensions-junitxml": "io.kotest:kotest-extensions-junitxml:${versions.kotest}", + "kotest-runner-junit5" : "io.kotest:kotest-runner-junit5:${versions.kotest}" ] dependencies { @@ -17,4 +19,6 @@ dependencies { testImplementation dep.kotest testImplementation dep."kotest-assertions" + testImplementation dep."kotest-extensions-junitxml" + testImplementation dep."kotest-runner-junit5" } \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt new file mode 100644 index 0000000..f500394 --- /dev/null +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt @@ -0,0 +1,16 @@ +package org.eventhorizonlab.kyoriadventuredsl + +import io.kotest.core.config.AbstractProjectConfig +import io.kotest.extensions.junitxml.JunitXmlReporter + +class ProjectConfig : AbstractProjectConfig() { + override val extensions + get() = + listOf( + JunitXmlReporter( + includeContainers = true, + useTestPathAsName = true, + outputDir = "test-results/kotest" + ) + ) +} \ No newline at end of file From 0b4bcf5ba4c7dc0abe7d5f71c3605974bd3090a8 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 23:20:35 +0100 Subject: [PATCH 16/36] feat: Remove unnecessary annotation notice from GitHub Actions workflow --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc02892..274e28f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,6 +48,5 @@ jobs: comment: 'true' updateComment: 'true' include_passed: 'true' - annotate_notice: 'true' From 4b7a200f95309503b9a1f58f7ded6b7a4e218713 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Thu, 28 Aug 2025 23:57:00 +0100 Subject: [PATCH 17/36] feat: Remove JUnit XML reporting configuration and unused Kotest dependencies --- build.gradle | 8 -------- gradle/dependencies.gradle | 6 +----- .../kyoriadventuredsl/ProjectConfig.kt | 16 ---------------- 3 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt diff --git a/build.gradle b/build.gradle index 4e936e1..7b284ac 100644 --- a/build.gradle +++ b/build.gradle @@ -29,14 +29,6 @@ subprojects { destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs")) } - tasks.withType(Test).configureEach { - useJUnitPlatform() - reports { - junitXml.required.set(false) - } - systemProperty("gradle.build.dir", rootProject.layout.buildDirectory.asFile.get().absolutePath) - } - apply from: rootProject.file('gradle/spotless.gradle') } diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 4f48cc7..3c294af 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -9,9 +9,7 @@ configurations { def dep = [ "adventure-api" : "net.kyori:adventure-api:${versions.'adventure-api'}", "kotest" : "io.kotest:kotest-framework-engine:${versions.kotest}", - "kotest-assertions" : "io.kotest:kotest-assertions-core:${versions.kotest}", - "kotest-extensions-junitxml": "io.kotest:kotest-extensions-junitxml:${versions.kotest}", - "kotest-runner-junit5" : "io.kotest:kotest-runner-junit5:${versions.kotest}" + "kotest-assertions" : "io.kotest:kotest-assertions-core:${versions.kotest}" ] dependencies { @@ -19,6 +17,4 @@ dependencies { testImplementation dep.kotest testImplementation dep."kotest-assertions" - testImplementation dep."kotest-extensions-junitxml" - testImplementation dep."kotest-runner-junit5" } \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt deleted file mode 100644 index f500394..0000000 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ProjectConfig.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.eventhorizonlab.kyoriadventuredsl - -import io.kotest.core.config.AbstractProjectConfig -import io.kotest.extensions.junitxml.JunitXmlReporter - -class ProjectConfig : AbstractProjectConfig() { - override val extensions - get() = - listOf( - JunitXmlReporter( - includeContainers = true, - useTestPathAsName = true, - outputDir = "test-results/kotest" - ) - ) -} \ No newline at end of file From 78cc90ce60bfa04dee15e46c58e44b3ef9e9f157 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 18:16:46 +0100 Subject: [PATCH 18/36] feat: Add spotlessCheck dependency to check task --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 7b284ac..d8ff10b 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,10 @@ subprojects { destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs")) } + tasks.named("check") { + dependsOn("spotlessCheck") + } + apply from: rootProject.file('gradle/spotless.gradle') } From 10ede3314ae6c43ed4e666dd6571faef14ac7ee2 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 18:43:10 +0100 Subject: [PATCH 19/36] feat: Implement TextComponentScope DSL for building text components --- .../kyoriadventuredsl/ComponentDsl.kt | 6 -- .../kyoriadventuredsl/TextComponentScope.kt | 39 +++++++++++ .../kyoriadventuredsl/ComponentDslTest.kt | 29 --------- .../TextComponentScopeTest.kt | 64 +++++++++++++++++++ 4 files changed, 103 insertions(+), 35 deletions(-) delete mode 100644 modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt create mode 100644 modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt delete mode 100644 modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt create mode 100644 modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt deleted file mode 100644 index e427106..0000000 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDsl.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.eventhorizonlab.kyoriadventuredsl - -import net.kyori.adventure.text.Component -import net.kyori.adventure.text.TextComponent - -fun textComponent(builder: TextComponent.Builder.() -> Unit) = Component.text().apply(builder).build() \ No newline at end of file diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt new file mode 100644 index 0000000..a08a7a5 --- /dev/null +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt @@ -0,0 +1,39 @@ +package org.eventhorizonlab.kyoriadventuredsl + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextDecoration + +@DslMarker +annotation class AdventureDsl + +@AdventureDsl +class TextComponentScope internal constructor( + private val builder: TextComponent.Builder +) { + fun content(content: String) { + builder.content(content) + } + + fun color(color: NamedTextColor) { + builder.color(color) + } + + fun decorate(vararg decorations: TextDecoration) { + decorations.forEach { builder.decoration(it, true) } + } + + fun text(init: TextComponentScope.() -> Unit) { + val childBuilder = Component.text() + TextComponentScope(childBuilder).init() + builder.append(childBuilder.build()) + } + + internal fun build() = builder.build() +} + +fun textComponent(init: TextComponentScope.() -> Unit): TextComponent { + val builder = Component.text() + return TextComponentScope(builder).apply(init).build() +} \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt deleted file mode 100644 index b57c7cf..0000000 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/ComponentDslTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.eventhorizonlab.kyoriadventuredsl - -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.should -import io.kotest.matchers.shouldBe -import io.kotest.matchers.types.beInstanceOf -import net.kyori.adventure.text.TextComponent -import net.kyori.adventure.text.format.NamedTextColor - -class ComponentDslTest : - StringSpec({ - "textComponent this is TextComponent.Builder" { - textComponent { - this should beInstanceOf() - } - } - - "can build a simple coloured text" { - val c = - textComponent { - content("Hello World") - color(NamedTextColor.AQUA) - } - - c should beInstanceOf() - c.content() shouldBe "Hello World" - c.color() shouldBe NamedTextColor.AQUA - } - }) \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt new file mode 100644 index 0000000..10ca8e6 --- /dev/null +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt @@ -0,0 +1,64 @@ +package org.eventhorizonlab.kyoriadventuredsl + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.instanceOf +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextDecoration + +class TextComponentScopeTest : + StringSpec({ + "builds a simple text component with content" { + val content = "Hello World!" + val component = + textComponent { + content(content) + } + + component.content() shouldBe content + component.color() shouldBe null + component.decorations().forEach { (_, state) -> state shouldBe TextDecoration.State.NOT_SET } + } + + "applies a color to the text" { + val color = NamedTextColor.RED + val component = + textComponent { + color(color) + } + + component.color() shouldBe color + } + + "applies multiple decorations" { + val decorations = arrayOf(TextDecoration.BOLD, TextDecoration.ITALIC) + val component = + textComponent { + decorate(*decorations) + } + + decorations.forEach { + component.hasDecoration(it) shouldBe true + component.decoration(it) shouldBe TextDecoration.State.TRUE + } + } + + "supports nested text components" { + val component = + textComponent { + content("Parent") + text { + content(" Child") + color(NamedTextColor.GREEN) + } + } + + component.children().size shouldBe 1 + val child = component.children().first() + child shouldBe instanceOf() + val textComponent = child as TextComponent + textComponent.content() shouldBe " Child" + textComponent.color() shouldBe NamedTextColor.GREEN + } + }) \ No newline at end of file From 4dc28520e71a610bc05d65baf251939eb4f49ffb Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 18:46:35 +0100 Subject: [PATCH 20/36] feat: Disable test task in build configuration --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index d8ff10b..160884e 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,10 @@ subprojects { dependsOn("spotlessCheck") } + tasks.named("test") { + enabled = false + } + apply from: rootProject.file('gradle/spotless.gradle') } From a7df750d437ae1545e607d3866a468fe48666dc1 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 18:58:09 +0100 Subject: [PATCH 21/36] feat: Rename TextComponentScope to DefaultTextComponentScope and introduce interface --- ...tScope.kt => DefaultTextComponentScope.kt} | 21 ++++++++++--------- .../api/TextComponentScope.kt | 13 ++++++++++++ ...st.kt => DefaultTextComponentScopeTest.kt} | 2 +- 3 files changed, 25 insertions(+), 11 deletions(-) rename modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/{TextComponentScope.kt => DefaultTextComponentScope.kt} (55%) create mode 100644 modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt rename modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/{TextComponentScopeTest.kt => DefaultTextComponentScopeTest.kt} (98%) diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScope.kt similarity index 55% rename from modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt rename to modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScope.kt index a08a7a5..1187a08 100644 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScope.kt +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScope.kt @@ -4,36 +4,37 @@ import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration +import org.eventhorizonlab.kyoriadventuredsl.api.TextComponentScope @DslMarker -annotation class AdventureDsl +internal annotation class AdventureDsl @AdventureDsl -class TextComponentScope internal constructor( +internal class DefaultTextComponentScope( private val builder: TextComponent.Builder -) { - fun content(content: String) { +) : TextComponentScope { + override fun content(content: String) { builder.content(content) } - fun color(color: NamedTextColor) { + override fun color(color: NamedTextColor) { builder.color(color) } - fun decorate(vararg decorations: TextDecoration) { + override fun decorate(vararg decorations: TextDecoration) { decorations.forEach { builder.decoration(it, true) } } - fun text(init: TextComponentScope.() -> Unit) { + override fun text(init: TextComponentScope.() -> Unit) { val childBuilder = Component.text() - TextComponentScope(childBuilder).init() + DefaultTextComponentScope(childBuilder).init() builder.append(childBuilder.build()) } - internal fun build() = builder.build() + fun build() = builder.build() } fun textComponent(init: TextComponentScope.() -> Unit): TextComponent { val builder = Component.text() - return TextComponentScope(builder).apply(init).build() + return DefaultTextComponentScope(builder).apply(init).build() } \ No newline at end of file diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt new file mode 100644 index 0000000..4afc989 --- /dev/null +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt @@ -0,0 +1,13 @@ +package org.eventhorizonlab.kyoriadventuredsl.api + +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextDecoration +import org.eventhorizonlab.kyoriadventuredsl.AdventureDsl + +@AdventureDsl +interface TextComponentScope { + fun content(content: String) + fun color(color: NamedTextColor) + fun decorate(vararg decorations: TextDecoration) + fun text(init: TextComponentScope.() -> Unit) +} \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScopeTest.kt similarity index 98% rename from modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt rename to modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScopeTest.kt index 10ca8e6..28f509e 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/TextComponentScopeTest.kt +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScopeTest.kt @@ -7,7 +7,7 @@ import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration -class TextComponentScopeTest : +class DefaultTextComponentScopeTest : StringSpec({ "builds a simple text component with content" { val content = "Hello World!" From 63a3a0f55e2bd489384289ac114878453c55daaa Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 18:59:54 +0100 Subject: [PATCH 22/36] feat: Update check task to depend on spotlessApply and enhance TextComponentScope interface --- build.gradle | 2 +- .../kyoriadventuredsl/api/TextComponentScope.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 160884e..7bc3e63 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ subprojects { } tasks.named("check") { - dependsOn("spotlessCheck") + dependsOn("spotlessApply") } tasks.named("test") { diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt index 4afc989..a60d85c 100644 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt @@ -7,7 +7,10 @@ import org.eventhorizonlab.kyoriadventuredsl.AdventureDsl @AdventureDsl interface TextComponentScope { fun content(content: String) + fun color(color: NamedTextColor) + fun decorate(vararg decorations: TextDecoration) + fun text(init: TextComponentScope.() -> Unit) } \ No newline at end of file From c8df29e0cee1726be9b5338193188e19378aa495 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 19:00:26 +0100 Subject: [PATCH 23/36] feat: Add code quality check step using spotlessCheck in build process --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 274e28f..63f4714 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,9 @@ jobs: - name: Executable Gradle run: chmod +x gradlew + - name: Check Code Quality + run: ./gradlew spotlessCheck + - name: Build with Gradle run: ./gradlew build From 21503c00b2e02092b7e1bcb6e1599478a88c43b6 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 19:48:57 +0100 Subject: [PATCH 24/36] feat: Set up initial DSL structure for TextComponentScope and core-api integration --- .../core/api/TextComponentScope.kt | 36 +++++++++++++++++++ modules/core/build.gradle | 3 ++ .../api/TextComponentScope.kt | 16 --------- .../TextComponentScope.kt} | 22 ++++++------ ...zonlab.core.api.TextComponentScope$Factory | 1 + .../TextComponentScopeTest.kt} | 4 +-- settings.gradle | 6 ++-- 7 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt create mode 100644 modules/core/build.gradle delete mode 100644 modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt rename modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/{DefaultTextComponentScope.kt => core/TextComponentScope.kt} (62%) create mode 100644 modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory rename modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/{DefaultTextComponentScopeTest.kt => core/TextComponentScopeTest.kt} (96%) diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt new file mode 100644 index 0000000..b64a5f4 --- /dev/null +++ b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt @@ -0,0 +1,36 @@ +package org.eventhorizonlab.core.api + +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextDecoration +import java.util.* + +@DslMarker +internal annotation class AdventureDsl + +@AdventureDsl +interface TextComponentScope { + fun content(content: String) + + fun color(color: NamedTextColor) + + fun decorate(vararg decorations: TextDecoration) + + fun text(init: TextComponentScope.() -> Unit) + + interface Factory { + fun create(init: TextComponentScope.() -> Unit): TextComponent + } +} + +fun textComponent(init: TextComponentScope.() -> Unit): TextComponent { + val loader = ServiceLoader.load(TextComponentScope.Factory::class.java) + val apiClassLoader = TextComponentScope::class.java.classLoader + + val factory = loader.firstOrNull { + it.javaClass.classLoader != apiClassLoader + } ?: loader.firstOrNull() + ?: error("No TextComponentScope.Factory implementation found") + + return factory.create(init) +} diff --git a/modules/core/build.gradle b/modules/core/build.gradle new file mode 100644 index 0000000..ef6f5aa --- /dev/null +++ b/modules/core/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation(project(":core-api")) +} \ No newline at end of file diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt deleted file mode 100644 index a60d85c..0000000 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/api/TextComponentScope.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.eventhorizonlab.kyoriadventuredsl.api - -import net.kyori.adventure.text.format.NamedTextColor -import net.kyori.adventure.text.format.TextDecoration -import org.eventhorizonlab.kyoriadventuredsl.AdventureDsl - -@AdventureDsl -interface TextComponentScope { - fun content(content: String) - - fun color(color: NamedTextColor) - - fun decorate(vararg decorations: TextDecoration) - - fun text(init: TextComponentScope.() -> Unit) -} \ No newline at end of file diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt similarity index 62% rename from modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScope.kt rename to modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt index 1187a08..31b6ba3 100644 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScope.kt +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt @@ -1,16 +1,12 @@ -package org.eventhorizonlab.kyoriadventuredsl +package org.eventhorizonlab.kyoriadventuredsl.core import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration -import org.eventhorizonlab.kyoriadventuredsl.api.TextComponentScope +import org.eventhorizonlab.core.api.TextComponentScope -@DslMarker -internal annotation class AdventureDsl - -@AdventureDsl -internal class DefaultTextComponentScope( +internal class TextComponentScope( private val builder: TextComponent.Builder ) : TextComponentScope { override fun content(content: String) { @@ -27,14 +23,16 @@ internal class DefaultTextComponentScope( override fun text(init: TextComponentScope.() -> Unit) { val childBuilder = Component.text() - DefaultTextComponentScope(childBuilder).init() + TextComponentScope(childBuilder).init() builder.append(childBuilder.build()) } fun build() = builder.build() -} -fun textComponent(init: TextComponentScope.() -> Unit): TextComponent { - val builder = Component.text() - return DefaultTextComponentScope(builder).apply(init).build() + class Factory : TextComponentScope.Factory { + override fun create(init: TextComponentScope.() -> Unit): TextComponent { + val builder = Component.text() + return TextComponentScope(builder).apply(init).build() + } + } } \ No newline at end of file diff --git a/modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory b/modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory new file mode 100644 index 0000000..feb9ac4 --- /dev/null +++ b/modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory @@ -0,0 +1 @@ +org.eventhorizonlab.kyoriadventuredsl.core.TextComponentScope$Factory \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScopeTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt similarity index 96% rename from modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScopeTest.kt rename to modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt index 28f509e..9a0e11f 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/DefaultTextComponentScopeTest.kt +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt @@ -1,4 +1,4 @@ -package org.eventhorizonlab.kyoriadventuredsl +package org.eventhorizonlab.kyoriadventuredsl.core import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe @@ -7,7 +7,7 @@ import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration -class DefaultTextComponentScopeTest : +class TextComponentScopeTest : StringSpec({ "builds a simple text component with content" { val content = "Hello World!" diff --git a/settings.gradle b/settings.gradle index 6005e81..5fd2d2d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,5 +11,7 @@ plugins { rootProject.name = 'KyoriAdventureDSL' -include 'core' -project(':core').projectDir = file('modules/core') +['core', 'core-api'].forEach { + include it + project(":$it").projectDir = file("modules/$it") +} \ No newline at end of file From 71aafdac18e48afd097bb8f7d26c4fc5674a5c43 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 19:49:27 +0100 Subject: [PATCH 25/36] feat: Refactor TextComponentScope factory loading logic for clarity --- .../eventhorizonlab/core/api/TextComponentScope.kt | 11 ++++++----- modules/core/build.gradle | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt index b64a5f4..39cf59f 100644 --- a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt +++ b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt @@ -27,10 +27,11 @@ fun textComponent(init: TextComponentScope.() -> Unit): TextComponent { val loader = ServiceLoader.load(TextComponentScope.Factory::class.java) val apiClassLoader = TextComponentScope::class.java.classLoader - val factory = loader.firstOrNull { - it.javaClass.classLoader != apiClassLoader - } ?: loader.firstOrNull() - ?: error("No TextComponentScope.Factory implementation found") + val factory = + loader.firstOrNull { + it.javaClass.classLoader != apiClassLoader + } ?: loader.firstOrNull() + ?: error("No TextComponentScope.Factory implementation found") return factory.create(init) -} +} \ No newline at end of file diff --git a/modules/core/build.gradle b/modules/core/build.gradle index ef6f5aa..44e0efc 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -1,3 +1,3 @@ dependencies { implementation(project(":core-api")) -} \ No newline at end of file +} From 98c90d2bab8374042e83bcf0c44e1071636b42aa Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 20:18:27 +0100 Subject: [PATCH 26/36] feat: Implement service file generation and introduce ServiceContract/ServiceProvider annotations --- .../core/api/TextComponentScope.kt | 2 + .../core/api/annotations/ServiceScheme.kt | 19 ++++ modules/core/build.gradle | 92 +++++++++++++++++++ .../core/TextComponentScope.kt | 2 + ...zonlab.core.api.TextComponentScope$Factory | 1 - .../core/TextComponentScopeTest.kt | 16 +++- 6 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt delete mode 100644 modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt index 39cf59f..6572014 100644 --- a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt +++ b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt @@ -3,6 +3,7 @@ package org.eventhorizonlab.core.api import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration +import org.eventhorizonlab.core.api.annotations.ServiceContract import java.util.* @DslMarker @@ -18,6 +19,7 @@ interface TextComponentScope { fun text(init: TextComponentScope.() -> Unit) + @ServiceContract interface Factory { fun create(init: TextComponentScope.() -> Unit): TextComponent } diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt new file mode 100644 index 0000000..48146aa --- /dev/null +++ b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt @@ -0,0 +1,19 @@ +package org.eventhorizonlab.core.api.annotations + +import kotlin.reflect.KClass + +/** + * Marks a class as a ServiceLoader provider for the given contract + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class ServiceProvider( + val value: KClass<*> +) + +/** + * Marks an interface as a ServiceLoader contract + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class ServiceContract \ No newline at end of file diff --git a/modules/core/build.gradle b/modules/core/build.gradle index 44e0efc..b9d64e0 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -1,3 +1,95 @@ dependencies { implementation(project(":core-api")) } + +tasks.register('generateServiceFiles') { + group = 'build setup' + description = 'Scans for @ServiceContract and @ServiceProvider across modules and generates META-INF/services entries' + + // Ensure compiled classes exist before scanning + dependsOn ':core-api:compileKotlin', ':core-api:compileJava', + ':core:compileKotlin', ':core:compileJava' + + doLast { + // 1️⃣ Merge runtime classpaths for both modules so all deps are visible + def runtimeClasspath = project(':core').sourceSets.main.runtimeClasspath + + project(':core-api').sourceSets.main.runtimeClasspath + + // 2️⃣ Build a loader that can see compiled classes + all dependencies + def loader = new URLClassLoader( + runtimeClasspath.files.collect { it.toURI().toURL() } as URL[], + Thread.currentThread().contextClassLoader + ) + + // 3️⃣ Load annotation classes from core-api + def serviceContractAnn = loader.loadClass('org.eventhorizonlab.core.api.annotations.ServiceContract') + def serviceProviderAnn = loader.loadClass('org.eventhorizonlab.core.api.annotations.ServiceProvider') + + // 4️⃣ Gather all compiled class dirs for scanning + def classDirs = [ + project(':core-api').layout.buildDirectory.dir("classes/kotlin/main").get().asFile, + project(':core').layout.buildDirectory.dir("classes/kotlin/main").get().asFile + ] + + // 5️⃣ Scan all .class files in both dirs + def allClasses = [] as List + classDirs.each { dir -> + if (dir.exists()) { + dir.eachFileRecurse { file -> + if (file.isFile() && file.name.endsWith('.class')) { + def relPath = dir.toPath().relativize(file.toPath()).toString() + def className = relPath.replace(File.separatorChar, '.' as char).replaceAll(/\.class$/, '') + allClasses << className + } + } + } + } + + // 6️⃣ Separate contracts and providers + def contracts = [] as Set + def providers = [:].withDefault { [] as List } + + allClasses.each { String className -> + def clazz = loader.loadClass(className) + + if (clazz.isInterface() && clazz.getAnnotation(serviceContractAnn)) { + contracts << clazz + } + + def providerAnn = clazz.getAnnotation(serviceProviderAnn) + if (providerAnn) { + def target = providerAnn.value() + providers[target] << clazz + } + } + + // 7️⃣ Where to write META-INF/services (in core’s resources) + def resourcesDir = project(':core').layout.buildDirectory.dir("resources/main").get().asFile + + // 8️⃣ Generate service files for each contract + contracts.each { contract -> + def impls = providers[contract] ?: [] + if (impls.isEmpty()) { + throw new GradleException("No @ServiceProvider found for contract ${contract.name}") + } + + def serviceFile = new File(resourcesDir, "META-INF/services/${contract.name}") + serviceFile.parentFile.mkdirs() + serviceFile.text = impls.collect { it.name }.join('\n') + + println "✅ Generated service file for ${contract.name} with ${impls.size()} provider(s)" + } + + // 9️⃣ Fail if any provider targets a non-contract + providers.each { target, impls -> + if (!contracts.contains(target)) { + throw new GradleException("@ServiceProvider target ${target.name} is not annotated with @ServiceContract") + } + } + } +} + +// Hook into processResources in :core so files are packaged +project(':core').tasks.named('processResources') { + dependsOn 'generateServiceFiles' +} diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt index 31b6ba3..ecf390d 100644 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt @@ -5,6 +5,7 @@ import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration import org.eventhorizonlab.core.api.TextComponentScope +import org.eventhorizonlab.core.api.annotations.ServiceProvider internal class TextComponentScope( private val builder: TextComponent.Builder @@ -29,6 +30,7 @@ internal class TextComponentScope( fun build() = builder.build() + @ServiceProvider(TextComponentScope.Factory::class) class Factory : TextComponentScope.Factory { override fun create(init: TextComponentScope.() -> Unit): TextComponent { val builder = Component.text() diff --git a/modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory b/modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory deleted file mode 100644 index feb9ac4..0000000 --- a/modules/core/src/main/resources/META-INF/services/org.eventhorizonlab.core.api.TextComponentScope$Factory +++ /dev/null @@ -1 +0,0 @@ -org.eventhorizonlab.kyoriadventuredsl.core.TextComponentScope$Factory \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt index 9a0e11f..518d425 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt @@ -9,22 +9,28 @@ import net.kyori.adventure.text.format.TextDecoration class TextComponentScopeTest : StringSpec({ + + // Use the concrete Factory to create components + val factory = TextComponentScope.Factory() + "builds a simple text component with content" { val content = "Hello World!" val component = - textComponent { + factory.create { content(content) } component.content() shouldBe content component.color() shouldBe null - component.decorations().forEach { (_, state) -> state shouldBe TextDecoration.State.NOT_SET } + component.decorations().forEach { (_, state) -> + state shouldBe TextDecoration.State.NOT_SET + } } "applies a color to the text" { val color = NamedTextColor.RED val component = - textComponent { + factory.create { color(color) } @@ -34,7 +40,7 @@ class TextComponentScopeTest : "applies multiple decorations" { val decorations = arrayOf(TextDecoration.BOLD, TextDecoration.ITALIC) val component = - textComponent { + factory.create { decorate(*decorations) } @@ -46,7 +52,7 @@ class TextComponentScopeTest : "supports nested text components" { val component = - textComponent { + factory.create { content("Parent") text { content(" Child") From 4b505d9ddd165fed6cf38ae5d9092a5904dfba09 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 20:29:18 +0100 Subject: [PATCH 27/36] feat: Add task to generate service files for @ServiceContract and @ServiceProvider annotations --- modules/core/build.gradle | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/core/build.gradle b/modules/core/build.gradle index b9d64e0..efede8a 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -2,6 +2,8 @@ dependencies { implementation(project(":core-api")) } +def generatedResourcesDir = layout.buildDirectory.dir("generated-resources/main").get().asFile + tasks.register('generateServiceFiles') { group = 'build setup' description = 'Scans for @ServiceContract and @ServiceProvider across modules and generates META-INF/services entries' @@ -10,6 +12,8 @@ tasks.register('generateServiceFiles') { dependsOn ':core-api:compileKotlin', ':core-api:compileJava', ':core:compileKotlin', ':core:compileJava' + outputs.dir(generatedResourcesDir) + doLast { // 1️⃣ Merge runtime classpaths for both modules so all deps are visible def runtimeClasspath = project(':core').sourceSets.main.runtimeClasspath + @@ -63,24 +67,21 @@ tasks.register('generateServiceFiles') { } } - // 7️⃣ Where to write META-INF/services (in core’s resources) - def resourcesDir = project(':core').layout.buildDirectory.dir("resources/main").get().asFile - - // 8️⃣ Generate service files for each contract + // 7️⃣ Write META-INF/services into generated-resources dir contracts.each { contract -> def impls = providers[contract] ?: [] if (impls.isEmpty()) { throw new GradleException("No @ServiceProvider found for contract ${contract.name}") } - def serviceFile = new File(resourcesDir, "META-INF/services/${contract.name}") + def serviceFile = new File(generatedResourcesDir, "META-INF/services/${contract.name}") serviceFile.parentFile.mkdirs() serviceFile.text = impls.collect { it.name }.join('\n') println "✅ Generated service file for ${contract.name} with ${impls.size()} provider(s)" } - // 9️⃣ Fail if any provider targets a non-contract + // 8️⃣ Fail if any provider targets a non-contract providers.each { target, impls -> if (!contracts.contains(target)) { throw new GradleException("@ServiceProvider target ${target.name} is not annotated with @ServiceContract") @@ -89,7 +90,10 @@ tasks.register('generateServiceFiles') { } } -// Hook into processResources in :core so files are packaged -project(':core').tasks.named('processResources') { +// Tell processResources to include generated service files +tasks.named('processResources', Copy) { + from(generatedResourcesDir) { + include "META-INF/services/**" + } dependsOn 'generateServiceFiles' } From 779e4eddaefac3769beea2168ca0a9005549c7aa Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 20:59:08 +0100 Subject: [PATCH 28/36] feat: Add initial setup for e2e testing and update dependencies --- e2e/build.gradle | 8 +++++ .../kotlin/core/api/ServiceLoaderE2ETest.kt | 33 +++++++++++++++++++ .../core/api/annotations/ServiceScheme.kt | 2 +- settings.gradle | 4 ++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 e2e/build.gradle create mode 100644 e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt diff --git a/e2e/build.gradle b/e2e/build.gradle new file mode 100644 index 0000000..3a358ca --- /dev/null +++ b/e2e/build.gradle @@ -0,0 +1,8 @@ +dependencies { + testImplementation(project(":core-api")) + testRuntimeOnly(project(":core")) +} + +project(":e2e").tasks.named("kotest") { + dependsOn(":core:jar") +} diff --git a/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt b/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt new file mode 100644 index 0000000..781a91b --- /dev/null +++ b/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt @@ -0,0 +1,33 @@ +package core.api + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.instanceOf +import net.kyori.adventure.text.format.NamedTextColor +import org.eventhorizonlab.core.api.TextComponentScope +import java.util.* + +class ServiceLoaderE2ETest : + StringSpec({ + "ServiceLoader should discover and use TextComponentScope.Factory" { + val loader = ServiceLoader.load(TextComponentScope.Factory::class.java) + val factories = loader.toList() + + factories.size shouldBe 1 + + val factory = factories.first() + + factory shouldBe instanceOf() + factory.javaClass.classLoader shouldBe TextComponentScope::class.java.classLoader + factory.javaClass.isInterface shouldBe false + + val component = + factory.create { + content("Hello E2E") + color(NamedTextColor.RED) + } + + component.content() shouldBe "Hello E2E" + component.color() shouldBe NamedTextColor.RED + } + }) \ No newline at end of file diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt index 48146aa..c7dd4bb 100644 --- a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt +++ b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt @@ -16,4 +16,4 @@ annotation class ServiceProvider( */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) -annotation class ServiceContract \ No newline at end of file +internal annotation class ServiceContract \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 5fd2d2d..8aa27e6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,4 +14,6 @@ rootProject.name = 'KyoriAdventureDSL' ['core', 'core-api'].forEach { include it project(":$it").projectDir = file("modules/$it") -} \ No newline at end of file +} + +include 'e2e' \ No newline at end of file From 86902c52805fb3389145a8db686414d46fe4f8d4 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 21:02:29 +0100 Subject: [PATCH 29/36] feat: Enhance ServiceLoaderE2ETest with factory component assertions and textComponent usage --- e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt b/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt index 781a91b..18b05ea 100644 --- a/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt +++ b/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt @@ -5,6 +5,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.types.instanceOf import net.kyori.adventure.text.format.NamedTextColor import org.eventhorizonlab.core.api.TextComponentScope +import org.eventhorizonlab.core.api.textComponent import java.util.* class ServiceLoaderE2ETest : @@ -21,12 +22,21 @@ class ServiceLoaderE2ETest : factory.javaClass.classLoader shouldBe TextComponentScope::class.java.classLoader factory.javaClass.isInterface shouldBe false - val component = + val factoryComponent = factory.create { content("Hello E2E") color(NamedTextColor.RED) } + factoryComponent.content() shouldBe "Hello E2E" + factoryComponent.color() shouldBe NamedTextColor.RED + + val component = + textComponent { + content("Hello E2E") + color(NamedTextColor.RED) + } + component.content() shouldBe "Hello E2E" component.color() shouldBe NamedTextColor.RED } From 9a0af7274b855b5c6e387b386e48fa275190cc44 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 22:48:36 +0100 Subject: [PATCH 30/36] feat: Remove service file generation task and update dependencies for annotation processing --- modules/core/build.gradle | 97 +-------------------------------------- 1 file changed, 2 insertions(+), 95 deletions(-) diff --git a/modules/core/build.gradle b/modules/core/build.gradle index efede8a..b98227e 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -1,99 +1,6 @@ dependencies { implementation(project(":core-api")) -} - -def generatedResourcesDir = layout.buildDirectory.dir("generated-resources/main").get().asFile - -tasks.register('generateServiceFiles') { - group = 'build setup' - description = 'Scans for @ServiceContract and @ServiceProvider across modules and generates META-INF/services entries' - - // Ensure compiled classes exist before scanning - dependsOn ':core-api:compileKotlin', ':core-api:compileJava', - ':core:compileKotlin', ':core:compileJava' - - outputs.dir(generatedResourcesDir) - - doLast { - // 1️⃣ Merge runtime classpaths for both modules so all deps are visible - def runtimeClasspath = project(':core').sourceSets.main.runtimeClasspath + - project(':core-api').sourceSets.main.runtimeClasspath - - // 2️⃣ Build a loader that can see compiled classes + all dependencies - def loader = new URLClassLoader( - runtimeClasspath.files.collect { it.toURI().toURL() } as URL[], - Thread.currentThread().contextClassLoader - ) - - // 3️⃣ Load annotation classes from core-api - def serviceContractAnn = loader.loadClass('org.eventhorizonlab.core.api.annotations.ServiceContract') - def serviceProviderAnn = loader.loadClass('org.eventhorizonlab.core.api.annotations.ServiceProvider') - - // 4️⃣ Gather all compiled class dirs for scanning - def classDirs = [ - project(':core-api').layout.buildDirectory.dir("classes/kotlin/main").get().asFile, - project(':core').layout.buildDirectory.dir("classes/kotlin/main").get().asFile - ] - - // 5️⃣ Scan all .class files in both dirs - def allClasses = [] as List - classDirs.each { dir -> - if (dir.exists()) { - dir.eachFileRecurse { file -> - if (file.isFile() && file.name.endsWith('.class')) { - def relPath = dir.toPath().relativize(file.toPath()).toString() - def className = relPath.replace(File.separatorChar, '.' as char).replaceAll(/\.class$/, '') - allClasses << className - } - } - } - } - - // 6️⃣ Separate contracts and providers - def contracts = [] as Set - def providers = [:].withDefault { [] as List } - - allClasses.each { String className -> - def clazz = loader.loadClass(className) - - if (clazz.isInterface() && clazz.getAnnotation(serviceContractAnn)) { - contracts << clazz - } - - def providerAnn = clazz.getAnnotation(serviceProviderAnn) - if (providerAnn) { - def target = providerAnn.value() - providers[target] << clazz - } - } - - // 7️⃣ Write META-INF/services into generated-resources dir - contracts.each { contract -> - def impls = providers[contract] ?: [] - if (impls.isEmpty()) { - throw new GradleException("No @ServiceProvider found for contract ${contract.name}") - } - - def serviceFile = new File(generatedResourcesDir, "META-INF/services/${contract.name}") - serviceFile.parentFile.mkdirs() - serviceFile.text = impls.collect { it.name }.join('\n') - - println "✅ Generated service file for ${contract.name} with ${impls.size()} provider(s)" - } - - // 8️⃣ Fail if any provider targets a non-contract - providers.each { target, impls -> - if (!contracts.contains(target)) { - throw new GradleException("@ServiceProvider target ${target.name} is not annotated with @ServiceContract") - } - } - } -} -// Tell processResources to include generated service files -tasks.named('processResources', Copy) { - from(generatedResourcesDir) { - include "META-INF/services/**" - } - dependsOn 'generateServiceFiles' + compileOnly "org.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" + annotationProcessor "org.eventhorizonlab:spi-tooling-processor:1.0.0-SNAPSHOT" } From ac48e219bf6ea3728c57bf3079918115b2c6bb7b Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sat, 30 Aug 2025 23:20:38 +0100 Subject: [PATCH 31/36] feat: Update DSL setup with Kotlin KAPT and rename TextComponentScope to DefaultTextComponentScope --- modules/core-api/build.gradle | 3 +++ .../core/api/TextComponentScope.kt | 2 +- .../core/api/annotations/ServiceScheme.kt | 19 ------------------- modules/core/build.gradle | 6 +++++- ...tScope.kt => DefaultTextComponentScope.kt} | 8 ++++---- .../core/TextComponentScopeTest.kt | 2 +- 6 files changed, 14 insertions(+), 26 deletions(-) create mode 100644 modules/core-api/build.gradle delete mode 100644 modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt rename modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/{TextComponentScope.kt => DefaultTextComponentScope.kt} (83%) diff --git a/modules/core-api/build.gradle b/modules/core-api/build.gradle new file mode 100644 index 0000000..48dff32 --- /dev/null +++ b/modules/core-api/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compileOnly "org.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" +} diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt index 6572014..45f7664 100644 --- a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt +++ b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt @@ -3,7 +3,7 @@ package org.eventhorizonlab.core.api import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration -import org.eventhorizonlab.core.api.annotations.ServiceContract +import org.eventhorizonlab.spi.ServiceContract import java.util.* @DslMarker diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt b/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt deleted file mode 100644 index c7dd4bb..0000000 --- a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/annotations/ServiceScheme.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.eventhorizonlab.core.api.annotations - -import kotlin.reflect.KClass - -/** - * Marks a class as a ServiceLoader provider for the given contract - */ -@Target(AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -annotation class ServiceProvider( - val value: KClass<*> -) - -/** - * Marks an interface as a ServiceLoader contract - */ -@Target(AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -internal annotation class ServiceContract \ No newline at end of file diff --git a/modules/core/build.gradle b/modules/core/build.gradle index b98227e..381a22e 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -1,6 +1,10 @@ +plugins { + id 'org.jetbrains.kotlin.kapt' +} + dependencies { implementation(project(":core-api")) compileOnly "org.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" - annotationProcessor "org.eventhorizonlab:spi-tooling-processor:1.0.0-SNAPSHOT" + kapt "org.eventhorizonlab:spi-tooling-processor:1.0.0-SNAPSHOT" } diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt similarity index 83% rename from modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt rename to modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt index ecf390d..ff38118 100644 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScope.kt +++ b/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt @@ -5,9 +5,9 @@ import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration import org.eventhorizonlab.core.api.TextComponentScope -import org.eventhorizonlab.core.api.annotations.ServiceProvider +import org.eventhorizonlab.spi.ServiceProvider -internal class TextComponentScope( +internal class DefaultTextComponentScope( private val builder: TextComponent.Builder ) : TextComponentScope { override fun content(content: String) { @@ -24,7 +24,7 @@ internal class TextComponentScope( override fun text(init: TextComponentScope.() -> Unit) { val childBuilder = Component.text() - TextComponentScope(childBuilder).init() + DefaultTextComponentScope(childBuilder).init() builder.append(childBuilder.build()) } @@ -34,7 +34,7 @@ internal class TextComponentScope( class Factory : TextComponentScope.Factory { override fun create(init: TextComponentScope.() -> Unit): TextComponent { val builder = Component.text() - return TextComponentScope(builder).apply(init).build() + return DefaultTextComponentScope(builder).apply(init).build() } } } \ No newline at end of file diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt index 518d425..659de7c 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt +++ b/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt @@ -11,7 +11,7 @@ class TextComponentScopeTest : StringSpec({ // Use the concrete Factory to create components - val factory = TextComponentScope.Factory() + val factory = DefaultTextComponentScope.Factory() "builds a simple text component with content" { val content = "Hello World!" From 40f8317a20c0685a07176266ded131c9e3d1e916 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sun, 31 Aug 2025 00:12:06 +0100 Subject: [PATCH 32/36] feat: Update package structure and dependencies for DSL setup --- build.gradle | 2 +- .../kyoriadventuredsl}/api/ServiceLoaderE2ETest.kt | 6 +++--- modules/core-api/build.gradle | 2 +- .../github}/eventhorizonlab/core/api/TextComponentScope.kt | 4 ++-- modules/core/build.gradle | 4 ++-- .../kyoriadventuredsl/core/DefaultTextComponentScope.kt | 6 +++--- .../kyoriadventuredsl/core/TextComponentScopeTest.kt | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) rename e2e/src/test/kotlin/{core => com/github/eventhorizonlab/kyoriadventuredsl}/api/ServiceLoaderE2ETest.kt (88%) rename modules/core-api/src/main/kotlin/{org => com/github}/eventhorizonlab/core/api/TextComponentScope.kt (91%) rename modules/core/src/main/kotlin/{org => com/github}/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt (87%) rename modules/core/src/test/kotlin/{org => com/github}/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt (97%) diff --git a/build.gradle b/build.gradle index 7bc3e63..dbeba17 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'idea' } -group = 'org.eventhorizonlab.kyoriadventuredsl' +group = 'com.github.eventhorizonlab' version = '0.0.1-PRE-ALPHA' subprojects { diff --git a/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt b/e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt similarity index 88% rename from e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt rename to e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt index 18b05ea..0af95a7 100644 --- a/e2e/src/test/kotlin/core/api/ServiceLoaderE2ETest.kt +++ b/e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt @@ -1,11 +1,11 @@ -package core.api +package com.github.eventhorizonlab.kyoriadventuredsl.api +import com.github.eventhorizonlab.core.api.TextComponentScope +import com.github.eventhorizonlab.core.api.textComponent import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.types.instanceOf import net.kyori.adventure.text.format.NamedTextColor -import org.eventhorizonlab.core.api.TextComponentScope -import org.eventhorizonlab.core.api.textComponent import java.util.* class ServiceLoaderE2ETest : diff --git a/modules/core-api/build.gradle b/modules/core-api/build.gradle index 48dff32..e639ed7 100644 --- a/modules/core-api/build.gradle +++ b/modules/core-api/build.gradle @@ -1,3 +1,3 @@ dependencies { - compileOnly "org.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" + compileOnly "com.github.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" } diff --git a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt b/modules/core-api/src/main/kotlin/com/github/eventhorizonlab/core/api/TextComponentScope.kt similarity index 91% rename from modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt rename to modules/core-api/src/main/kotlin/com/github/eventhorizonlab/core/api/TextComponentScope.kt index 45f7664..5b7d397 100644 --- a/modules/core-api/src/main/kotlin/org/eventhorizonlab/core/api/TextComponentScope.kt +++ b/modules/core-api/src/main/kotlin/com/github/eventhorizonlab/core/api/TextComponentScope.kt @@ -1,9 +1,9 @@ -package org.eventhorizonlab.core.api +package com.github.eventhorizonlab.core.api +import com.github.eventhorizonlab.spi.ServiceContract import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration -import org.eventhorizonlab.spi.ServiceContract import java.util.* @DslMarker diff --git a/modules/core/build.gradle b/modules/core/build.gradle index 381a22e..416422b 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -5,6 +5,6 @@ plugins { dependencies { implementation(project(":core-api")) - compileOnly "org.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" - kapt "org.eventhorizonlab:spi-tooling-processor:1.0.0-SNAPSHOT" + compileOnly "com.github.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" + kapt "com.github.eventhorizonlab:spi-tooling-processor:1.0.0-SNAPSHOT" } diff --git a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt b/modules/core/src/main/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt similarity index 87% rename from modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt rename to modules/core/src/main/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt index ff38118..be73eb6 100644 --- a/modules/core/src/main/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt +++ b/modules/core/src/main/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/core/DefaultTextComponentScope.kt @@ -1,11 +1,11 @@ -package org.eventhorizonlab.kyoriadventuredsl.core +package com.github.eventhorizonlab.kyoriadventuredsl.core +import com.github.eventhorizonlab.core.api.TextComponentScope +import com.github.eventhorizonlab.spi.ServiceProvider import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.TextDecoration -import org.eventhorizonlab.core.api.TextComponentScope -import org.eventhorizonlab.spi.ServiceProvider internal class DefaultTextComponentScope( private val builder: TextComponent.Builder diff --git a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt b/modules/core/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt similarity index 97% rename from modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt rename to modules/core/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt index 659de7c..4eb1694 100644 --- a/modules/core/src/test/kotlin/org/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt +++ b/modules/core/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/core/TextComponentScopeTest.kt @@ -1,4 +1,4 @@ -package org.eventhorizonlab.kyoriadventuredsl.core +package com.github.eventhorizonlab.kyoriadventuredsl.core import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe From 9e9812eca26ce6a660ca4605d1520534b2f57ceb Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sun, 31 Aug 2025 00:42:40 +0100 Subject: [PATCH 33/36] feat: Update dependencies for spi-tooling annotations and processor --- modules/core-api/build.gradle | 2 +- modules/core/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core-api/build.gradle b/modules/core-api/build.gradle index e639ed7..1c8b321 100644 --- a/modules/core-api/build.gradle +++ b/modules/core-api/build.gradle @@ -1,3 +1,3 @@ dependencies { - compileOnly "com.github.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" + compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.0" } diff --git a/modules/core/build.gradle b/modules/core/build.gradle index 416422b..f9c033a 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -5,6 +5,6 @@ plugins { dependencies { implementation(project(":core-api")) - compileOnly "com.github.eventhorizonlab:spi-tooling-annotations:1.0.0-SNAPSHOT" - kapt "com.github.eventhorizonlab:spi-tooling-processor:1.0.0-SNAPSHOT" + compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.0" + kapt "io.github.eventhorizonlab:spi-tooling-processor:0.1.0" } From 74cf67d53b5058be68e0fa9a8e57b740e0f8ca48 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sun, 31 Aug 2025 02:42:53 +0100 Subject: [PATCH 34/36] feat: Update spi-tooling annotations and processor dependencies to version 0.1.16 --- modules/core-api/build.gradle | 2 +- modules/core/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core-api/build.gradle b/modules/core-api/build.gradle index 1c8b321..45e029a 100644 --- a/modules/core-api/build.gradle +++ b/modules/core-api/build.gradle @@ -1,3 +1,3 @@ dependencies { - compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.0" + compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.16" } diff --git a/modules/core/build.gradle b/modules/core/build.gradle index f9c033a..8f1a486 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -5,6 +5,6 @@ plugins { dependencies { implementation(project(":core-api")) - compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.0" - kapt "io.github.eventhorizonlab:spi-tooling-processor:0.1.0" + compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.16" + kapt "io.github.eventhorizonlab:spi-tooling-processor:0.1.16" } From c15138e9bd612e73c6af07e691f8b2ef1b7489e9 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sun, 31 Aug 2025 03:39:06 +0100 Subject: [PATCH 35/36] feat: Update build configuration and dependencies for improved DSL setup --- .github/workflows/build.yml | 8 ++++++++ e2e/build.gradle | 4 ++-- .../kyoriadventuredsl/api/ServiceLoaderE2ETest.kt | 6 +++++- gradle/repositories.gradle | 10 ++++++++++ gradle/versions.json | 3 ++- modules/core-api/build.gradle | 4 +++- modules/core/build.gradle | 6 ++++-- 7 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63f4714..5984599 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,9 +7,17 @@ on: types: - opened - synchronize + +permissions: + packages: read + jobs: build: runs-on: ubuntu-latest + env: + ORG_GRADLE_PROJECT_gprUse: 'true' + ORG_GRADLE_PROJECT_gprUser: ${{ github.actor }} + ORG_GRADLE_PROJECT_gprToken: ${{ secrets.GITHUB_TOKEN }} permissions: checks: write pull-requests: write diff --git a/e2e/build.gradle b/e2e/build.gradle index 3a358ca..5a5d44e 100644 --- a/e2e/build.gradle +++ b/e2e/build.gradle @@ -1,8 +1,8 @@ dependencies { testImplementation(project(":core-api")) - testRuntimeOnly(project(":core")) + testImplementation(files(project(":core").tasks.named("jar").map { it.archiveFile })) } -project(":e2e").tasks.named("kotest") { +tasks.named("kotest") { dependsOn(":core:jar") } diff --git a/e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt b/e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt index 0af95a7..1717be9 100644 --- a/e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt +++ b/e2e/src/test/kotlin/com/github/eventhorizonlab/kyoriadventuredsl/api/ServiceLoaderE2ETest.kt @@ -11,7 +11,11 @@ import java.util.* class ServiceLoaderE2ETest : StringSpec({ "ServiceLoader should discover and use TextComponentScope.Factory" { - val loader = ServiceLoader.load(TextComponentScope.Factory::class.java) + val loader = + ServiceLoader.load( + TextComponentScope.Factory::class.java, + Thread.currentThread().contextClassLoader + ) val factories = loader.toList() factories.size shouldBe 1 diff --git a/gradle/repositories.gradle b/gradle/repositories.gradle index dd36c19..c3f9086 100644 --- a/gradle/repositories.gradle +++ b/gradle/repositories.gradle @@ -1,4 +1,14 @@ repositories { mavenLocal() mavenCentral() + if (project.findProperty("gprUse") != null) { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/EventHorizonLab/SPI-Tooling") + credentials { + username = project.findProperty("gprUser") as String + password = project.findProperty("gprToken") as String + } + } + } } \ No newline at end of file diff --git a/gradle/versions.json b/gradle/versions.json index 5cc9dd1..7ebdcbf 100644 --- a/gradle/versions.json +++ b/gradle/versions.json @@ -1,4 +1,5 @@ { "kotest": "6.0.1", - "adventure-api": "4.24.0" + "adventure-api": "4.24.0", + "spi": "0.1.17" } \ No newline at end of file diff --git a/modules/core-api/build.gradle b/modules/core-api/build.gradle index 45e029a..efa7fdc 100644 --- a/modules/core-api/build.gradle +++ b/modules/core-api/build.gradle @@ -1,3 +1,5 @@ +apply from: rootProject.file('gradle/versions.gradle') + dependencies { - compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.16" + compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:${versions.spi}" } diff --git a/modules/core/build.gradle b/modules/core/build.gradle index 8f1a486..ec28d65 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -2,9 +2,11 @@ plugins { id 'org.jetbrains.kotlin.kapt' } +apply from: rootProject.file('gradle/versions.gradle') + dependencies { implementation(project(":core-api")) - compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:0.1.16" - kapt "io.github.eventhorizonlab:spi-tooling-processor:0.1.16" + compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:${versions.spi}" + kapt "io.github.eventhorizonlab:spi-tooling-processor:${versions.spi}" } From 3d6684959e296f2d4edb599954799c8483b46dd1 Mon Sep 17 00:00:00 2001 From: Liam Martin Date: Sun, 31 Aug 2025 15:07:07 +0100 Subject: [PATCH 36/36] feat: Remove versions.gradle dependency and add Sonatype snapshots repository --- gradle/repositories.gradle | 3 +++ modules/core-api/build.gradle | 2 -- modules/core/build.gradle | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gradle/repositories.gradle b/gradle/repositories.gradle index c3f9086..715e5b3 100644 --- a/gradle/repositories.gradle +++ b/gradle/repositories.gradle @@ -1,6 +1,9 @@ repositories { mavenLocal() mavenCentral() + maven { + url = uri("https://central.sonatype.com/repository/maven-snapshots/") + } if (project.findProperty("gprUse") != null) { maven { name = "GitHubPackages" diff --git a/modules/core-api/build.gradle b/modules/core-api/build.gradle index efa7fdc..e27e05e 100644 --- a/modules/core-api/build.gradle +++ b/modules/core-api/build.gradle @@ -1,5 +1,3 @@ -apply from: rootProject.file('gradle/versions.gradle') - dependencies { compileOnly "io.github.eventhorizonlab:spi-tooling-annotations:${versions.spi}" } diff --git a/modules/core/build.gradle b/modules/core/build.gradle index ec28d65..f7c8cf1 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -2,8 +2,6 @@ plugins { id 'org.jetbrains.kotlin.kapt' } -apply from: rootProject.file('gradle/versions.gradle') - dependencies { implementation(project(":core-api"))