Skip to content

Commit

Permalink
Add a ksp plugin for generating source code string of Gallery sample
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanlorng committed Feb 25, 2024
1 parent aa39846 commit e0c2c90
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 1 deletion.
Empty file added gallery-processor/.gitignore
Empty file.
22 changes: 22 additions & 0 deletions gallery-processor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import com.konyaco.fluent.plugin.build.BuildConfig

plugins {
alias(libs.plugins.kotlin.multiplatform)
}

group = BuildConfig.group
version = "gallery-processor"

kotlin {
jvm()
sourceSets {
val jvmMain by getting {
dependencies {
implementation(libs.squareup.kotlinpoet)
implementation("com.google.devtools.ksp:symbol-processing-api:${libs.versions.ksp.get()}")
implementation("com.google.devtools.ksp:symbol-processing:${libs.versions.ksp.get()}")
implementation(kotlin("compiler"))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.konyaco.fluent.gallery.processor

import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.symbol.impl.kotlin.KSFunctionDeclarationImpl
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.PropertySpec
import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets

class SampleCodeProcessor(private val logger: KSPLogger, private val codeGenerator: CodeGenerator) : SymbolProcessor {

private val annotationPackage = "com.konyaco.fluent.gallery.annotation"
private val sampleAnnotation = "Sample"

private val sampleCodeFunctions = mutableMapOf<String, MutableList<KSFunctionDeclaration>>()

private val visitor = FindFunctionVisitor {
if (it.annotations.any { annotation ->
annotation.shortName.asString() == sampleAnnotation &&
annotation.annotationType.resolve().declaration.packageName.asString() == annotationPackage
}
) {
val list = sampleCodeFunctions[it.packageName.asString()] ?: mutableListOf<KSFunctionDeclaration>().apply {
sampleCodeFunctions[it.packageName.asString()] = this
}
list.add(it)
}
}

override fun process(resolver: Resolver): List<KSAnnotated> {
resolver.getAllFiles().forEach { it.accept(visitor, Unit) }
return emptyList()
}

override fun finish() {
super.finish()
val fileName = "_SampleCodeString"
sampleCodeFunctions.forEach { (packageName, functions) ->
if (functions.isNotEmpty()) {
val sourceFile = FileSpec.builder(packageName, fileName)
val sourceFileList = mutableListOf<KSFile>()
functions.forEach { func ->
func.containingFile?.let { sourceFileList.add(it) }
if (func is KSFunctionDeclarationImpl) {
val funcName = func.simpleName.asString()
sourceFile.addProperty(
PropertySpec.builder(
"sourceCodeOf${funcName.first().uppercase()}${funcName.substring(1)}",
String::class
)
.addModifiers(KModifier.INTERNAL)
.getter(
FunSpec.getterBuilder()
.addStatement("return %S", func.ktFunction.text)
.build()
)
.build()
)
}
}
val file = codeGenerator.createNewFile(
Dependencies(true, *(sourceFileList).toTypedArray()),
packageName,
fileName
)
OutputStreamWriter(file, StandardCharsets.UTF_8).use(sourceFile.build()::writeTo)

}
}
}
}

class SampleCodeProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return SampleCodeProcessor(environment.logger, environment.codeGenerator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.konyaco.fluent.gallery.processor

import com.google.devtools.ksp.getDeclaredFunctions
import com.google.devtools.ksp.symbol.*

internal class FindFunctionVisitor(
private val onNode:(node: KSFunctionDeclaration) -> Unit
) : KSVisitorVoid() {

override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
super.visitClassDeclaration(classDeclaration, data)
classDeclaration.getDeclaredFunctions().forEach { it.accept(this, Unit) }
}

override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
onNode(function)
}

override fun visitFile(file: KSFile, data: Unit) {
file.declarations.forEach { it.accept(this, Unit) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.konyaco.fluent.gallery.processor.SampleCodeProcessorProvider
15 changes: 15 additions & 0 deletions gallery/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.compose)
alias(libs.plugins.android.application)
id("com.google.devtools.ksp") version libs.versions.ksp.get()
}

kotlin {
Expand All @@ -19,6 +20,7 @@ kotlin {
implementation(project(":fluent-icons-extended"))
implementation(compose("org.jetbrains.compose.ui:ui-util"))
}
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}
val commonTest by getting {
dependencies {
Expand Down Expand Up @@ -90,4 +92,17 @@ compose.desktop {
packageVersion = "1.0.0"
}
}
}

dependencies {
val processor = project(":gallery-processor")
add("kspCommonMainMetadata", processor)
}

// workaround for KSP only in Common Main.
// https://github.com/google/ksp/issues/567
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().all {
if (name != "kspCommonMainKotlinMetadata") {
dependsOn("kspCommonMainKotlinMetadata")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.konyaco.fluent.gallery.annotation

@Target(AnnotationTarget.FUNCTION)
annotation class Sample()
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ import com.konyaco.fluent.LocalContentColor
import com.konyaco.fluent.background.Layer
import com.konyaco.fluent.component.*
import com.konyaco.fluent.gallery.LocalStore
import com.konyaco.fluent.gallery.annotation.Sample
import com.konyaco.fluent.icons.Icons
import com.konyaco.fluent.icons.regular.*


@Composable
fun HomeScreen() {
var displayDialog by remember { mutableStateOf(false) }
val density = LocalDensity.current
var scale by remember(density) { mutableStateOf(density.density) }
val store = LocalStore.current
var showSourceCode by remember {
mutableStateOf(false)
}

Layer(
modifier = Modifier.padding(top = 16.dp).fillMaxSize()
Expand Down Expand Up @@ -179,8 +182,27 @@ fun HomeScreen() {
}
}
)
Button(
onClick = {
showSourceCode = true
}
) {
Text("Show source code of Buttons")
}
}

Dialog(
visible = showSourceCode,
title = "Source Code of Buttons",
cancelButtonText = "Cancel",
confirmButtonText = "Confirm",
onConfirm = { showSourceCode = false },
onCancel = { showSourceCode = false },
content = {
Text(text = sourceCodeOfButtons, modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()))
}
)

Dialog(
title = "This is an example dialog",
visible = displayDialog,
Expand Down Expand Up @@ -323,6 +345,7 @@ private fun Controls() {
RadioButton(selectedRadio == 1, onClick = { selectedRadio = 1 }, label = "With Label")
}

@Sample
@Composable
private fun Buttons() {
var text by remember { mutableStateOf("Hello World") }
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[versions]
kotlin = "1.9.0"
ksp = "1.9.0-1.0.13"
compose = "1.5.0"
androidGradlePlugin = "7.4.2"
androidBuildTools = "27.2.0-alpha16"
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ includeBuild("build-plugin")
include("fluent", "fluent-icons-core", "fluent-icons-extended")
include("fluent-icons-generator")
include("gallery")
include("gallery-processor")

0 comments on commit e0c2c90

Please sign in to comment.