diff --git a/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/interpreter.kt b/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/interpreter.kt index 6d0f05d5092..e9e4b03c275 100644 --- a/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/interpreter.kt +++ b/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/interpreter.kt @@ -146,12 +146,12 @@ public val interpreter: AnkOps = object : AnkOps { snippets.second.mapIndexed { i, snip -> val result = try { if (snip.isPlaygroundExtension) "" - else engineCache.getOrElse(snip.lang) { + else engineCache.getOrElse(snip.lang.trim { it == '`' }) { throw CompilationException( path = snippets.first, snippet = snip, underlying = IllegalStateException("No engine configured for `${snip.lang}`"), - msg = colored(ANSI_RED, "ΛNK compilation failed [ ${snippets.first} ]") + msg = colored(ANSI_RED, "ΛNK compilation failed - No engine configured for `${snip.lang}` [ ${snippets.first} ]") ) }.eval(snip.code) } catch (e: Exception) { diff --git a/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/main.kt b/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/main.kt index 543b17276d5..8b51a969c29 100644 --- a/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/main.kt +++ b/arrow-libs/ank/arrow-ank/src/main/kotlin/arrow/ank/main.kt @@ -7,12 +7,23 @@ import java.nio.file.Paths public suspend fun main(vararg args: String): Unit = when { args.size > 1 -> { - ank( - Paths.get(args[0]), - Paths.get(args[1]), - args.drop(2), - interpreter - ) + println("####################################################################################################################") + println("####################################################################################################################") + println("####################################################################################################################") + println("####################################################################################################################") + println("####################################################################################################################") + println("####################################################################################################################") + try { + ank( + Paths.get(args[0]), + Paths.get(args[1]), + args.drop(2), + interpreter + ) + } catch (e: Throwable) { + println("####################################################################################################################") + e.printStackTrace() + } } else -> throw IllegalArgumentException("Required first 2 args as directory paths in this order: ") } diff --git a/arrow-libs/ank/settings.gradle b/arrow-libs/ank/settings.gradle index 439114066d0..0bbcb95a285 100644 --- a/arrow-libs/ank/settings.gradle +++ b/arrow-libs/ank/settings.gradle @@ -24,3 +24,6 @@ include 'arrow-fx-coroutines-test' project(":arrow-fx-coroutines").projectDir = file("../fx/arrow-fx-coroutines") project(":arrow-fx-coroutines-test").projectDir = file("../fx/arrow-fx-coroutines-test") + +include 'jekyll' +project(":jekyll").projectDir = file("dokka/jekyll") \ No newline at end of file diff --git a/arrow-libs/build.gradle b/arrow-libs/build.gradle index 8b0505560e8..76d3fd4f8f6 100644 --- a/arrow-libs/build.gradle +++ b/arrow-libs/build.gradle @@ -22,7 +22,7 @@ plugins { apply from: "gradle/$ROOT_PROJECT" task generateDoc(type: Exec) { - commandLine "sh", "gradlew", "dokkaJekyll" + commandLine "sh", "gradlew", "dokkaGfm" } task runValidation(type: Exec) { @@ -38,3 +38,7 @@ task buildDoc { } runValidation.mustRunAfter generateDoc + +apiValidation { + ignoredProjects += ["jekyll"] +} diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt index 69cb2d01ba8..63a8ac74671 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt @@ -71,6 +71,7 @@ import kotlin.jvm.JvmStatic * println(left) * } * ``` + * * Because `Either` is right-biased, it is possible to define a Monad instance for it. * * Since we only ever want the computation to continue in the case of `Right` (as captured by the right-bias nature), @@ -1196,25 +1197,21 @@ public inline fun Either.filterOrElse(predicate: (B) -> Boolean, de * * Example: * - * {: data-executable='true'} - * ```kotlin:ank + * ```kotlin:ank:playground * import arrow.core.* - * import arrow.core.Either.Right * - * Right(12).filterOrOther({ it > 10 }, { -1 }) - * ``` - * - * {: data-executable='true'} - * ```kotlin:ank - * Right(7).filterOrOther({ it > 10 }, { "Value '$it' not greater than 10" }) - * ``` + * suspend fun main(): Unit { + * //sampleStart + * Either.Right(7).filterOrOther({ it == 10 }, { "Value '$it' is not equal to 10" }) + * .let(::println) // Either.Left(Value '7' is not equal to 10") * - * {: data-executable='true'} - * ```kotlin:ank - * import arrow.core.Either.Left + * Either.Right(10).filterOrOther({ it == 10 }, { "Value '$it' is not equal to 10" }) + * .let(::println) // Either.Right(10) * - * val left: Either = Left(12) - * left.filterOrOther({ it > 10 }, { -1 }) + * Either.Left(12).filterOrOther({ str: String -> str.contains("impossible") }, { -1 }) + * .let(::println) // Either.Left(12) + * //sampleEnd + * } * ``` */ public inline fun Either.filterOrOther(predicate: (B) -> Boolean, default: (B) -> A): Either = diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonFatal.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonFatal.kt index 5c79d20f358..4b61d3b7ca9 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonFatal.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonFatal.kt @@ -28,10 +28,10 @@ import kotlin.coroutines.cancellation.CancellationException * val nonFatal: Either = * //sampleStart * try { - * Right(unsafeFunction(1)) + * Either.Right(unsafeFunction(1)) * } catch (t: Throwable) { * if (NonFatal(t)) { - * Left(t) + * Either.Left(t) * } else { * throw t * } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/nonFatalOrThrow.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/nonFatalOrThrow.kt index 5cd8f6bac33..30f8caadca6 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/nonFatalOrThrow.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/nonFatalOrThrow.kt @@ -21,9 +21,9 @@ package arrow.core * val nonFatal: Either = * //sampleStart * try { - * Right(unsafeFunction(1)) + * Either.Right(unsafeFunction(1)) * } catch (t: Throwable) { - * Left(t.nonFatalOrThrow()) + * Either.Left(t.nonFatalOrThrow()) * } * //sampleEnd * println(nonFatal) diff --git a/arrow-libs/core/arrow-meta/arrow-meta-test-models/build.gradle b/arrow-libs/core/arrow-meta/arrow-meta-test-models/build.gradle index a430466409d..dad1db3af53 100644 --- a/arrow-libs/core/arrow-meta/arrow-meta-test-models/build.gradle +++ b/arrow-libs/core/arrow-meta/arrow-meta-test-models/build.gradle @@ -4,8 +4,6 @@ plugins { id "org.jlleitschuh.gradle.ktlint" } -apply from: "../$DOC_CREATION" - dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION" implementation "com.google.auto.service:auto-service:$GOOGLE_AUTO_SERVICE_VERSION" diff --git a/arrow-libs/core/arrow-meta/build.gradle b/arrow-libs/core/arrow-meta/build.gradle index 1b823dceaae..5cc6b0bc587 100644 --- a/arrow-libs/core/arrow-meta/build.gradle +++ b/arrow-libs/core/arrow-meta/build.gradle @@ -7,7 +7,6 @@ plugins { } apply from: "$SUB_PROJECT" -apply from: "$DOC_CREATION" apply from: "$PUBLICATION" dependencies { diff --git a/arrow-libs/core/settings.gradle b/arrow-libs/core/settings.gradle index f857941dca3..8c28aadcee2 100644 --- a/arrow-libs/core/settings.gradle +++ b/arrow-libs/core/settings.gradle @@ -9,3 +9,6 @@ include 'arrow-core' include 'arrow-core-test' include 'arrow-continuations' include 'arrow-core-retrofit' + +include 'jekyll' +project(":jekyll").projectDir = file("dokka/jekyll") \ No newline at end of file diff --git a/arrow-libs/dokka/jekyll/build.gradle b/arrow-libs/dokka/jekyll/build.gradle new file mode 100644 index 00000000000..9cb3c56ebf1 --- /dev/null +++ b/arrow-libs/dokka/jekyll/build.gradle @@ -0,0 +1,12 @@ +plugins { + id "org.jetbrains.kotlin.jvm" + id "org.jlleitschuh.gradle.ktlint" +} + +apply from: "$SUB_PROJECT" + +dependencies { + compileOnly "org.jetbrains.dokka:dokka-core:$DOKKA_VERSION" + implementation "org.jetbrains.dokka:dokka-base:$DOKKA_VERSION" + implementation "org.jetbrains.dokka:gfm-plugin:$DOKKA_VERSION" +} diff --git a/arrow-libs/dokka/jekyll/gradle.properties b/arrow-libs/dokka/jekyll/gradle.properties new file mode 100644 index 00000000000..063f7e883eb --- /dev/null +++ b/arrow-libs/dokka/jekyll/gradle.properties @@ -0,0 +1,4 @@ +# Maven publishing configuration +POM_NAME=Arrow Dokka Jekyll +POM_ARTIFACT_ID=arrow-dokka-jekyll +POM_PACKAGING=jar diff --git a/arrow-libs/dokka/jekyll/src/main/kotlin/com/github/nomisRev/DokkaFenceWorkaroundPlugin.kt b/arrow-libs/dokka/jekyll/src/main/kotlin/com/github/nomisRev/DokkaFenceWorkaroundPlugin.kt new file mode 100644 index 00000000000..d2b0b8ebbab --- /dev/null +++ b/arrow-libs/dokka/jekyll/src/main/kotlin/com/github/nomisRev/DokkaFenceWorkaroundPlugin.kt @@ -0,0 +1,228 @@ +package com.github.nomisRev + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.PackageListCreator +import org.jetbrains.dokka.base.renderers.RootCreator +import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.gfm.GfmPlugin +import org.jetbrains.dokka.gfm.renderer.BriefCommentPreprocessor +import org.jetbrains.dokka.gfm.renderer.CommonmarkRenderer +import org.jetbrains.dokka.model.DisplaySourceSet +import org.jetbrains.dokka.model.doc.CodeBlock +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.toDisplaySourceSets +import org.jetbrains.dokka.pages.ContentCodeBlock +import org.jetbrains.dokka.pages.ContentCodeInline +import org.jetbrains.dokka.pages.ContentDivergentGroup +import org.jetbrains.dokka.pages.ContentDivergentInstance +import org.jetbrains.dokka.pages.ContentGroup +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.ContentTable +import org.jetbrains.dokka.pages.DCI +import org.jetbrains.dokka.pages.PlatformHintedContent +import org.jetbrains.dokka.pages.SimpleAttr +import org.jetbrains.dokka.pages.Style +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.Extension +import org.jetbrains.dokka.plugability.ExtensionPoint +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import org.jetbrains.dokka.renderers.Renderer +import org.jetbrains.dokka.transformers.pages.PageTransformer + +public class DokkaFenceWorkaround : DokkaPlugin() { + public val jekyllPreprocessors: ExtensionPoint by extensionPoint() + + private val dokkaBase by lazy { plugin() } + private val gfmPlugin by lazy { plugin() } + + public val renderer: Extension by extending { + (CoreExtensions.renderer + providing { JekyllRenderer(it) } + override plugin().renderer) + } + + public val comments: Extension by extending { + dokkaBase.commentsToContentConverter with PatchedDocTagToContentConverter() override dokkaBase.docTagToContentConverter + } + + public val rootCreator: Extension by extending { + jekyllPreprocessors with RootCreator + } + + public val briefCommentPreprocessor: Extension by extending { + jekyllPreprocessors with BriefCommentPreprocessor() + } + + public val packageListCreator: Extension by extending { + jekyllPreprocessors providing { + PackageListCreator(it, RecognizedLinkFormat.DokkaJekyll) + } order { after(rootCreator) } + } + + public val locationProvider: Extension by extending { + dokkaBase.locationProviderFactory providing ::DokkaLocationProviderFactory override listOf(gfmPlugin.locationProvider) + } +} + +public class JekyllRenderer(context: DokkaContext) : CommonmarkRenderer(context) { + + override val preprocessors: List = + context.plugin().query { jekyllPreprocessors } + + override fun StringBuilder.buildNewLine() { + append("\n") + } + + public fun StringBuilder.buildParagraph() { + buildNewLine() + buildNewLine() + } + + override fun buildPage(page: ContentPage, content: (StringBuilder, ContentPage) -> Unit): String { + val builder = StringBuilder() + builder.append("---\n") + builder.append("title: ${page.name}\n") + builder.append("---\n") + content(builder, page) + return builder.toString() + } + + override fun StringBuilder.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { + + val distinct = + node.groupDivergentInstances(pageContext, { instance, contentPage, sourceSet -> + instance.before?.let { before -> + buildString { buildContentNode(before, pageContext, sourceSet) } + } ?: "" + }, { instance, contentPage, sourceSet -> + instance.after?.let { after -> + buildString { buildContentNode(after, pageContext, sourceSet) } + } ?: "" + }) + + distinct.values.forEach { entry -> + val (instance, sourceSets) = entry.getInstanceAndSourceSets() + + buildParagraph() + buildSourceSetTags(sourceSets) + buildNewLine() + + instance.before?.let { + buildContentNode( + it, + pageContext, + sourceSets.first() + ) // It's workaround to render content only once + buildParagraph() + } + + entry.groupBy { buildString { buildContentNode(it.first.divergent, pageContext, setOf(it.second)) } } + .values.forEach { innerEntry -> + val (innerInstance, innerSourceSets) = innerEntry.getInstanceAndSourceSets() + if (sourceSets.size > 1) { + buildSourceSetTags(innerSourceSets) + buildNewLine() + } + innerInstance.divergent.build( + this@buildDivergent, + pageContext, + setOf(innerSourceSets.first()) + ) // It's workaround to render content only once + buildParagraph() + } + + instance.after?.let { + buildContentNode( + it, + pageContext, + sourceSets.first() + ) // It's workaround to render content only once + } + + buildParagraph() + } + } + + private fun List>.getInstanceAndSourceSets() = + this.let { Pair(it.first().first, it.map { it.second }.toSet()) } + + override fun StringBuilder.buildPlatformDependent( + content: PlatformHintedContent, + pageContext: ContentPage, + sourceSetRestriction: Set? + ) { + buildPlatformDependentItem(content.inner, content.sourceSets, pageContext) + } + + private fun StringBuilder.buildPlatformDependentItem( + content: ContentNode, + sourceSets: Set, + pageContext: ContentPage, + ) { + if (content is ContentGroup && content.children.firstOrNull { it is ContentTable } != null) { + buildContentNode(content, pageContext, sourceSets) + } else { + val distinct = sourceSets.map { + it to buildString { buildContentNode(content, pageContext, setOf(it)) } + }.groupBy(Pair::second, Pair::first) + + distinct.filter { it.key.isNotBlank() }.forEach { (text, platforms) -> + buildParagraph() + buildSourceSetTags(platforms.toSet()) + buildNewLine() + append(text.trim()) + buildParagraph() + } + } + } + + private fun StringBuilder.buildSourceSetTags(sourceSets: Set) = + sourceSets.forEach { append("""${it.name}""") } + + override fun StringBuilder.buildCodeBlock(code: ContentCodeBlock, pageContext: ContentPage) { + append("```${code.language}\n") + code.children.forEach { it.build(this, pageContext) } + append("\n```") + } + + override fun StringBuilder.buildCodeInline(code: ContentCodeInline, pageContext: ContentPage) { + append('`') + code.children.forEach { it.build(this, pageContext) } + append('`') + } +} + +public class PatchedDocTagToContentConverter : DocTagToContentConverter() { + override fun buildContent( + docTag: DocTag, + dci: DCI, + sourceSets: Set, + styles: Set