Skip to content

Commit

Permalink
Merge pull request #206 from mikepenz/feature/124
Browse files Browse the repository at this point in the history
Introduce syntax highlighting support
  • Loading branch information
mikepenz authored Sep 6, 2024
2 parents 290bda0 + 6caf007 commit 134cc80
Show file tree
Hide file tree
Showing 11 changed files with 363 additions and 25 deletions.
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ Markdown(
```

> [!NOTE]
> 0.21.0 adds JVM support for this dependency via `HTTPUrlConnection` -> however this is expected to be removed in the future.
> 0.21.0 adds JVM support for this dependency via `HTTPUrlConnection` -> however this is expected to be removed in the
> future.
> [!NOTE]
> Please refer to the official coil2 documentation on how to adjust the `ImageLoader`
Expand All @@ -276,6 +277,43 @@ Markdown(
> [!NOTE]
> The `coil3` module does depend on SNAPSHOT builds of coil3
### Syntax Highlighting

The library (introduced with 0.27.0) offers optional support for syntax highlighting via
the [Highlights](https://github.com/SnipMeDev/Highlights) project.
This support is not included in the core, and can be enabled by adding the `multiplatform-markdown-renderer-code`
dependency.

```groovy
implementation("com.mikepenz:multiplatform-markdown-renderer-code:${version}")
```

Once added, the `Markdown` has to be configured to use the alternative code highlighter.

```kotlin
// Use default color scheme
Markdown(
MARKDOWN,
components = markdownComponents(
codeBlock = highlightedCodeBlock,
codeFence = highlightedCodeFence,
)
)

// ADVANCED: Customize Highlights library by defining different theme
val isDarkTheme = isSystemInDarkTheme()
val highlightsBuilder = remember(isDarkTheme) {
Highlights.Builder().theme(SyntaxThemes.atom(darkMode = isDarkTheme))
}
Markdown(
MARKDOWN,
components = markdownComponents(
codeBlock = { MarkdownHighlightedCodeBlock(it.content, it.node, highlightsBuilder) },
codeFence = { MarkdownHighlightedCodeFence(it.content, it.node, highlightsBuilder) },
)
)
```

## Dependency

This project uses JetBrains [markdown](https://github.com/JetBrains/markdown/) Multiplatform
Expand Down
1 change: 1 addition & 0 deletions app-desktop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
implementation(projects.multiplatformMarkdownRenderer)
implementation(projects.multiplatformMarkdownRendererM2)
implementation(projects.multiplatformMarkdownRendererCoil3)
implementation(projects.multiplatformMarkdownRendererCode)

implementation(compose.desktop.currentOs)
implementation(compose.foundation)
Expand Down
16 changes: 15 additions & 1 deletion app-desktop/src/main/kotlin/main.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
Expand All @@ -11,12 +12,17 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.mikepenz.markdown.coil3.Coil3ImageTransformerImpl
import com.mikepenz.markdown.compose.components.markdownComponents
import com.mikepenz.markdown.compose.elements.MarkdownHighlightedCodeBlock
import com.mikepenz.markdown.compose.elements.MarkdownHighlightedCodeFence
import com.mikepenz.markdown.compose.extendedspans.ExtendedSpans
import com.mikepenz.markdown.compose.extendedspans.RoundedCornerSpanPainter
import com.mikepenz.markdown.compose.extendedspans.SquigglyUnderlineSpanPainter
import com.mikepenz.markdown.compose.extendedspans.rememberSquigglyUnderlineAnimator
import com.mikepenz.markdown.m2.Markdown
import com.mikepenz.markdown.model.markdownExtendedSpans
import dev.snipme.highlights.Highlights
import dev.snipme.highlights.model.SyntaxThemes

fun main() = application {
Window(onCloseRequest = ::exitApplication, title = "Markdown Sample") {
Expand All @@ -29,9 +35,17 @@ fun main() = application {
}
) {
val scrollState = rememberScrollState()
val isDarkTheme = isSystemInDarkTheme()
val highlightsBuilder = remember(isDarkTheme) {
Highlights.Builder().theme(SyntaxThemes.atom(darkMode = isDarkTheme))
}

Markdown(
MARKDOWN,
components = markdownComponents(
codeBlock = { MarkdownHighlightedCodeBlock(it.content, it.node, highlightsBuilder) },
codeFence = { MarkdownHighlightedCodeFence(it.content, it.node, highlightsBuilder) },
),
imageTransformer = Coil3ImageTransformerImpl,
extendedSpans = markdownExtendedSpans {
val animator = rememberSquigglyUnderlineAnimator()
Expand Down Expand Up @@ -76,7 +90,7 @@ This is a paragraph with a [link](https://www.jetbrains.com/).
This is a code block:
```kotlin
fun main() {
println("Hello, world!")
println("Hello, world!")
}
```
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ detekt = "1.23.6"
gradleMvnPublish = "0.29.0"
screenshot = "0.0.1-alpha05"
ktor = "3.0.0-beta-2"
highlights = "0.9.1"

[libraries]
androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" }
Expand All @@ -39,6 +40,7 @@ markdown = { module = "org.jetbrains:markdown", version.ref = "markdown" }
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-java = { module = "io.ktor:ktor-client-java", version.ref = "ktor" }
highlights = { module = "dev.snipme:highlights", version.ref = "highlights" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
Expand Down
1 change: 1 addition & 0 deletions multiplatform-markdown-renderer-code/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
174 changes: 174 additions & 0 deletions multiplatform-markdown-renderer-code/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import com.vanniktech.maven.publish.SonatypeHost
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl

plugins {
kotlin("multiplatform")
alias(libs.plugins.androidLibrary)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.composeCompiler)
alias(libs.plugins.dokka)
alias(libs.plugins.mavenPublish)
}

android {
namespace = "com.mikepenz.markdown.code"
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
}

buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
)
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "11"

kotlinOptions {
if (project.findProperty("composeCompilerReports") == "true") {
freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${project.buildDir.absolutePath}/compose_compiler"
)
}
if (project.findProperty("composeCompilerMetrics") == "true") {
freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=${project.buildDir.absolutePath}/compose_compiler"
)
}
}
}
}

kotlin {
applyDefaultHierarchyTemplate()

targets.all {
compilations.all {
compilerOptions.configure {
languageVersion.set(KotlinVersion.KOTLIN_1_9)
apiVersion.set(KotlinVersion.KOTLIN_1_9)
}
}
}
androidTarget {
publishLibraryVariants("release")
}
jvm {
compilations {
all {
kotlinOptions.jvmTarget = "11"
}
}

testRuns["test"].executionTask.configure {
useJUnit {
excludeCategories("org.intellij.markdown.ParserPerformanceTest")
}
}
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
js(IR) {
nodejs()
}
macosX64()
macosArm64()
iosX64()
iosArm64()
iosSimulatorArm64()

sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val fileBasedTest by creating {
dependsOn(commonTest)
}
val jvmTest by getting {
dependsOn(fileBasedTest)
}
val jsTest by getting {
dependsOn(fileBasedTest)
}
val nativeMain by getting {
dependsOn(commonMain)
}
val nativeTest by getting {
dependsOn(fileBasedTest)
}
val nativeSourceSets = listOf(
"macosX64",
"macosArm64",
"ios",
"iosSimulatorArm64"
).map { "${it}Main" }
for (set in nativeSourceSets) {
getByName(set).dependsOn(nativeMain)
}
val nativeTestSourceSets = listOf(
"macosX64",
"macosArm64"
).map { "${it}Test" }
for (set in nativeTestSourceSets) {
getByName(set).dependsOn(nativeTest)
getByName(set).dependsOn(fileBasedTest)
}
}
}

dependencies {
commonMainApi(projects.multiplatformMarkdownRenderer)
commonMainCompileOnly(compose.runtime)
commonMainCompileOnly(compose.ui)
commonMainCompileOnly(compose.foundation)

commonMainApi(libs.highlights)
}

tasks.dokkaHtml.configure {
dokkaSourceSets {
configureEach {
noAndroidSdkLink.set(false)
}
}
}

tasks.create<Jar>("javadocJar") {
dependsOn("dokkaJavadoc")
archiveClassifier.set("javadoc")
from("${layout.buildDirectory}/javadoc")
}

mavenPublishing {
publishToMavenCentral(SonatypeHost.S01)
signAllPublications()
}

publishing {
repositories {
maven {
name = "installLocally"
setUrl("${rootProject.layout.buildDirectory}/localMaven")
}
}
}
3 changes: 3 additions & 0 deletions multiplatform-markdown-renderer-code/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=Multiplatform Markdown Renderer - Code
POM_DESCRIPTION=Kotlin Multiplatform Markdown Renderer. (Android, Desktop, ...) powered by Compose Multiplatform
POM_ARTIFACT_ID=multiplatform-markdown-renderer-code
Loading

0 comments on commit 134cc80

Please sign in to comment.