diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d6aace..a771f1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: branches: [ main ] pull_request: +env: + ORG_GRADLE_PROJECT_version: ${{ github.sha }} + jobs: build: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c1142d..0233f1e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: release: types: [published] +env: + ORG_GRADLE_PROJECT_version: ${{ github.event.release.tag_name }} + ORG_GRADLE_PROJECT_githubRepository: ${{ github.repository }} + jobs: release: name: Release Artefacts @@ -30,14 +34,11 @@ jobs: run: ./gradlew clean dependencies - name: Assemble Artefacts run: ./gradlew assemble - env: - ORG_GRADLE_PROJECT_githubRepository: ${{ github.repository }} - name: Release Artefacts run: ./gradlew release env: ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PACKAGE_SIGNING_KEY }} ORG_GRADLE_PROJECT_signingKeyPassword: ${{ secrets.PACKAGE_SIGNING_KEY_PASSWORD }} - ORG_GRADLE_PROJECT_githubRepository: ${{ github.repository }} ORG_GRADLE_PROJECT_githubToken: ${{ secrets.RELEASE_GITHUB_TOKEN }} ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.OSSRH_USERNAME }} ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.OSSRH_PASSWORD }} @@ -66,9 +67,7 @@ jobs: - name: Prepare run: ./gradlew clean dependencies - name: Generate Documentation - run: ./gradlew dokkaHtml - env: - ORG_GRADLE_PROJECT_githubRepository: ${{ github.repository }} + run: ./gradlew dokkaGenerate - name: Publish Documentation uses: peaceiris/actions-gh-pages@v3.9.3 with: diff --git a/build.gradle.kts b/build.gradle.kts index 9321b79..4cb6773 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,21 +1,19 @@ import io.github.gradlenexus.publishplugin.NexusRepository import org.gradle.api.JavaVersion.VERSION_1_8 -import org.jetbrains.dokka.gradle.DokkaTask -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { - kotlin("jvm") version "1.9.25" - id("com.palantir.git-version") version "3.2.0" - id("org.jetbrains.dokka") version "1.9.20" + kotlin("jvm") version "2.1.10" + id("org.jetbrains.dokka-javadoc") version "2.0.0" + id("org.jetbrains.dokka") version "2.0.0" `maven-publish` signing id("io.github.gradle-nexus.publish-plugin") version "1.3.0" - idea } group = "de.joshuagleitze" -version = if (isSnapshot) versionDetails.gitHash else versionDetails.lastTag.drop("v") -status = if (isSnapshot) "snapshot" else "release" +version = if (version == "unspecified") "local" else version +status = if (version == "local") "snapshot" else "release" repositories { mavenCentral() @@ -30,19 +28,13 @@ dependencies { testImplementation(kotlin("reflect", KotlinVersion.CURRENT.toString())) } + testRuntimeOnly(name = "junit-platform-launcher", group = "org.junit.platform", version = "1.12.1") testRuntimeOnly(name = "junit-jupiter-engine", group = "org.junit.jupiter", version = "5.7.2") } -idea { - module { - isDownloadSources = true - isDownloadJavadoc = true - } -} - -tasks.withType { +tasks.withType().configureEach { useJUnitPlatform() - reports.junitXml.required.set(true) + reports.junitXml.required = true } java { @@ -50,9 +42,9 @@ java { targetCompatibility = VERSION_1_8 } -tasks.withType { - kotlinOptions { - jvmTarget = "1.8" +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_1_8 } } @@ -62,28 +54,28 @@ val githubRepository: String? by project val githubOwner = githubRepository?.split("/")?.get(0) val githubToken: String? by project -val sourcesJar by tasks.creating(Jar::class) { +val sourcesJar by tasks.registering(Jar::class) { group = "build" description = "Assembles the source code into a jar" archiveClassifier.set("sources") from(sourceSets.main.get().allSource) } -tasks.withType { - dokkaSourceSets.named("main") { +dokka { + dokkaSourceSets.main { sourceLink { - localDirectory.set(file("src/main/kotlin")) - remoteUrl.set(uri("https://github.com/$githubRepository/blob/main/src/main/kotlin").toURL()) - remoteLineSuffix.set("#L") + localDirectory = file("src/main/kotlin") + remoteUrl = uri("https://github.com/$githubRepository/blob/main/src/main/kotlin") + remoteLineSuffix = "#L" } } } -val dokkaJar by tasks.creating(Jar::class) { +val dokkaJar by tasks.registering(Jar::class) { group = "build" description = "Assembles the Kotlin docs with Dokka" archiveClassifier.set("javadoc") - from(tasks.named("dokkaJavadoc")) + from(tasks.named("dokkaGeneratePublicationJavadoc")) } artifacts { @@ -166,15 +158,12 @@ signing { val closeAndReleaseStagingRepository by project.tasks closeAndReleaseStagingRepository.mustRunAfter(mavenCentral.publishTask) -task("release") { +val release by tasks.registering { group = "release" description = "Releases the project to Maven Central" dependsOn(githubPackages.publishTask, mavenCentral.publishTask, closeAndReleaseStagingRepository) } -val Project.isSnapshot get() = versionDetails.commitDistance != 0 fun String.drop(prefix: String) = if (this.startsWith(prefix)) this.drop(prefix.length) else this - -val Project.versionDetails get() = (this.extra["versionDetails"] as groovy.lang.Closure<*>)() as com.palantir.gradle.gitversion.VersionDetails val ArtifactRepository.publishTask get() = tasks["publishAllPublicationsTo${this.name}Repository"] val NexusRepository.publishTask get() = "publishTo${this.name.replaceFirstChar { it.titlecase() }}" diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a36cb3f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.parallel=true +org.gradle.configuration-cache=true +org.gradle.configuration-cache.parallel=true + +org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true \ No newline at end of file diff --git a/src/main/kotlin/BaseStringNotation.kt b/src/main/kotlin/BaseStringNotation.kt index ec3a477..455a883 100644 --- a/src/main/kotlin/BaseStringNotation.kt +++ b/src/main/kotlin/BaseStringNotation.kt @@ -35,5 +35,5 @@ abstract class BaseStringNotation(private val splitAt: Regex): StringNotation { .foldIndexed(StringBuffer()) { index, existing, part -> existing.append(printBeforePart(index, part)).append(part) } .toString() - override fun toString() = this::class.java.simpleName!! + override fun toString(): String = this::class.java.simpleName } diff --git a/src/main/kotlin/JavaNotations.kt b/src/main/kotlin/JavaNotations.kt index 6bc72fe..4d2e4a5 100644 --- a/src/main/kotlin/JavaNotations.kt +++ b/src/main/kotlin/JavaNotations.kt @@ -11,7 +11,7 @@ import javax.lang.model.SourceVersion * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected * using [SourceVersion.isKeyword]. */ -object JavaTypeName: StringNotation by UpperCamelCase { +data object JavaTypeName : StringNotation by UpperCamelCase { override fun print(word: Word) = UpperCamelCase.print( Word(word.parts.mapIndexed { index, wordPart -> if (index == 0) wordPart.keepOnlyJavaIdentifierChars() @@ -19,8 +19,6 @@ object JavaTypeName: StringNotation by UpperCamelCase { }) ) .neutralizeJavaReservedKeywords() - - override fun toString() = this::class.java.simpleName!! } /** @@ -30,8 +28,8 @@ object JavaTypeName: StringNotation by UpperCamelCase { * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected * using [SourceVersion.isKeyword]. */ -object JavaMemberName: BaseStringNotation(camelCaseSplitRegex) { - override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) +object JavaMemberName : BaseStringNotation(camelCaseSplitRegex) { + override fun transformPartAfterParse(index: Int, part: String) = part.lowercase(Locale.ROOT) override fun print(word: Word) = word.parts .foldIndexed(StringBuffer()) { index, existing, part -> @@ -39,8 +37,8 @@ object JavaMemberName: BaseStringNotation(camelCaseSplitRegex) { if (index == 0) part.keepOnlyJavaIdentifierChars() else part.keepOnlyJavaIdentifierContinuationChars() val nextPart = - if (existing.contains(Regex("[a-zA-Z]"))) filteredPart.toFirstUpperOtherLowerCase() - else filteredPart.toLowerCase() + if (existing.contains(Regex("[a-zA-Z]"))) filteredPart.firstUpperThenLowerCase() + else filteredPart.lowercase(Locale.getDefault()) existing.append(printBeforePart(index, nextPart)).append(nextPart) }.toString().makeValidJavaIdentifier() } @@ -54,10 +52,10 @@ object JavaMemberName: BaseStringNotation(camelCaseSplitRegex) { * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected * using [SourceVersion.isKeyword]. */ -object JavaPackagePart: BaseStringNotation(Regex("_|${camelCaseSplitRegex.pattern}")) { - override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) +object JavaPackagePart : BaseStringNotation(Regex("_|${camelCaseSplitRegex.pattern}")) { + override fun transformPartAfterParse(index: Int, part: String) = part.lowercase(Locale.ROOT) - override fun transformPartToPrint(index: Int, part: String) = part.toLowerCase(Locale.ROOT) + override fun transformPartToPrint(index: Int, part: String) = part.lowercase(Locale.ROOT) override fun print(word: Word) = super.print(word).makeValidJavaIdentifier() } @@ -69,8 +67,8 @@ object JavaPackagePart: BaseStringNotation(Regex("_|${camelCaseSplitRegex.patter * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected * using [SourceVersion.isKeyword]. */ -object JavaPackageName: BaseStringNotation(Regex("\\.")) { - override fun transformPartToPrint(index: Int, part: String) = part.toLowerCase(Locale.ROOT).makeValidJavaIdentifier() +object JavaPackageName : BaseStringNotation(Regex("\\.")) { + override fun transformPartToPrint(index: Int, part: String) = part.lowercase(Locale.ROOT).makeValidJavaIdentifier() override fun printBeforeInnerPart(index: Int, part: String) = "." } @@ -82,10 +80,8 @@ object JavaPackageName: BaseStringNotation(Regex("\\.")) { * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected * using [SourceVersion.isKeyword]. */ -object JavaConstantName: StringNotation by ScreamingSnakeCase { +data object JavaConstantName : StringNotation by ScreamingSnakeCase { override fun print(word: Word) = ScreamingSnakeCase.print(word).makeValidJavaIdentifier() - - override fun toString() = this::class.java.simpleName!! } private fun String.makeValidJavaIdentifier() = this.keepOnlyJavaIdentifierChars().neutralizeJavaReservedKeywords() @@ -95,10 +91,15 @@ private fun String.keepOnlyJavaIdentifierChars() = this.chars() .keepOnlyJavaIdentifierContinuationChars() .collectToString() -private fun String.keepOnlyJavaIdentifierContinuationChars() = this.chars().keepOnlyJavaIdentifierContinuationChars().collectToString() +private fun String.keepOnlyJavaIdentifierContinuationChars() = + this.chars().keepOnlyJavaIdentifierContinuationChars().collectToString() + private fun IntStream.keepOnlyJavaIdentifierContinuationChars() = this.filter { Character.isJavaIdentifierPart(it) } private fun IntStream.collectToString() = - this.collect({ StringBuilder() }, { left, right -> left.appendCodePoint(right) }, { left, right -> left.append(right) }) + this.collect( + { StringBuilder() }, + { left, right -> left.appendCodePoint(right) }, + { left, right -> left.append(right) }) .toString() private fun String.neutralizeJavaReservedKeywords() = when { diff --git a/src/main/kotlin/Notations.kt b/src/main/kotlin/Notations.kt index b34a255..456008d 100644 --- a/src/main/kotlin/Notations.kt +++ b/src/main/kotlin/Notations.kt @@ -9,10 +9,10 @@ internal val camelCaseSplitRegex = Regex("(?<=.)(?=\\p{Lu})") * * @see JavaTypeName */ -object UpperCamelCase: BaseStringNotation(camelCaseSplitRegex) { - override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) +object UpperCamelCase : BaseStringNotation(camelCaseSplitRegex) { + override fun transformPartAfterParse(index: Int, part: String) = part.lowercase(Locale.ROOT) - public override fun transformPartToPrint(index: Int, part: String) = part.toFirstUpperOtherLowerCase() + public override fun transformPartToPrint(index: Int, part: String) = part.firstUpperThenLowerCase() } /** @@ -20,21 +20,22 @@ object UpperCamelCase: BaseStringNotation(camelCaseSplitRegex) { * * @see JavaMemberName */ -object LowerCamelCase: BaseStringNotation(camelCaseSplitRegex) { - override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) +object LowerCamelCase : BaseStringNotation(camelCaseSplitRegex) { + override fun transformPartAfterParse(index: Int, part: String) = part.lowercase(Locale.ROOT) - override fun transformPartToPrint(index: Int, part: String) = if (index == 0) part.toLowerCase() else part.toFirstUpperOtherLowerCase() + override fun transformPartToPrint(index: Int, part: String) = + if (index == 0) part.lowercase(Locale.ROOT) else part.firstUpperThenLowerCase() } /** * The `SCREAMING_SNAKE_CASE` notation. */ -object ScreamingSnakeCase: BaseStringNotation(Regex("_")) { - override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) +object ScreamingSnakeCase : BaseStringNotation(Regex("_")) { + override fun transformPartAfterParse(index: Int, part: String) = part.lowercase(Locale.ROOT) override fun printBeforeInnerPart(index: Int, part: String) = "_" - override fun transformPartToPrint(index: Int, part: String) = part.toUpperCase() + override fun transformPartToPrint(index: Int, part: String) = part.uppercase(Locale.ROOT) } /** @@ -49,9 +50,10 @@ object SnakeCase: BaseStringNotation(Regex("_")) { * one or more characters of whitespace as a [part][Word.parts]. [Printing][StringNotation.print] will print the parts separated by one * space. */ -object NormalWords: BaseStringNotation(Regex("[\\s]+")) { +object NormalWords: BaseStringNotation(Regex("\\s+")) { override fun printBeforeInnerPart(index: Int, part: String) = " " } -internal fun String.toFirstUpperOtherLowerCase() = if (isNotEmpty()) this[0].toUpperCase() + substring(1).toLowerCase() else this +internal fun String.firstUpperThenLowerCase() = + if (isNotEmpty()) this[0].uppercaseChar() + substring(1).lowercase(Locale.ROOT) else this diff --git a/src/test/kotlin/WordTest.kt b/src/test/kotlin/WordTest.kt index 4405daa..21092e5 100644 --- a/src/test/kotlin/WordTest.kt +++ b/src/test/kotlin/WordTest.kt @@ -1,10 +1,6 @@ package de.joshuagleitze.stringnotation -import ch.tutteli.atrium.api.fluent.en_GB.asIterable -import ch.tutteli.atrium.api.fluent.en_GB.containsExactly -import ch.tutteli.atrium.api.fluent.en_GB.feature -import ch.tutteli.atrium.api.fluent.en_GB.notToBe -import ch.tutteli.atrium.api.fluent.en_GB.toBe +import ch.tutteli.atrium.api.fluent.en_GB.* import ch.tutteli.atrium.api.verbs.expect import org.junit.jupiter.api.Test @@ -55,7 +51,7 @@ class WordTest { @Test fun `allows to map parts`() { expect(Word("a", "b", "c")) - .feature(Word::mapParts, String::toUpperCase) + .feature(Word::mapParts, String::uppercase) .toBe(Word("A", "B", "C")) }