Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root=true

[*.{kt,kts}]
insert_final_newline = true
max_line_length = 140
indent_size = 4
indent_style = space
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.6.0-alpha01

- added experimental support of [Rebugger](https://github.com/theapache64/rebugger)

# 1.5.0

- updated kotlin to 1.8.20 and compose compiler to 1.4.6
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The plugin also highlights composable functions during recomposition
</video>

## requirements:
- v1.5.0: kotlin 1.8.20
- v1.5.0+: kotlin 1.8.20
- v1.4.0: kotlin 1.8.10
- v1.3.0: kotlin 1.8.0
- v1.2.0: kotlin 1.7.20
Expand Down Expand Up @@ -48,6 +48,7 @@ Other plugin options:
May be useful in multi-module project (for example: in app module use "implementation", in other modules "compileOnly")
- `enabled [Boolean]` - when false, plugin don't add any additional code for debug functionality. By default it false for release and true for debug
- `tag [String, default: "RecompositionLog"]` - tag for recomposition logs.
- `useRebugger [Boolean]` - use [Rebugger](https://github.com/theapache64/rebugger) for logging (experimental)

# Features

Expand Down
2 changes: 1 addition & 1 deletion common.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
kotlin_version=1.8.20
version=1.5.0
version=1.6.0-alpha01
group_id=com.welltech
runtime_artifact=recomposition-logger-runtime
annotations_artifact=recomposition-logger-annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ class PluginConfig internal constructor(private val project: Project) {

val runtimeLib = "$group:$runtimeLibArtifact:$version"
val annotationsLib = "$group:$annotationsLibArtifact:$version"

val rebuggerLib = "com.github.theapache64:rebugger:1.0.0-alpha02"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ buildConfig {
buildConfigField("String", "KEY_RECOMPOSITION_LOGS_TAG", "\"recomposition_log_tag\"")
buildConfigField("String", "DEFAULT_RECOMPOSITION_LOGS_TAG", "\"RecompositionLog\"")

buildConfigField("String", "KEY_USE_REBUGGER", "\"use_rebugger\"")
buildConfigField("boolean", "DEFAULT_USE_REBUGGER", "false")

buildConfigField("String", "KEY_LOG_FILE", "\"log_file\"")
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,61 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey
import com.welltech.compiler_plugin.BuildConfig.KEY_RECOMPOSITION_LOGS_TAG
import com.welltech.compiler_plugin.BuildConfig.KEY_LOG_FILE
import com.welltech.compiler_plugin.BuildConfig.KOTLIN_PLUGIN_ID
import com.welltech.compiler_plugin.BuildConfig.KEY_USE_REBUGGER
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import java.io.File

@OptIn(ExperimentalCompilerApi::class)
@AutoService(CommandLineProcessor::class)
class PluginCommandLineProcessor : CommandLineProcessor {
object Keys {
val logFile = KEY_LOG_FILE.toConfigKey<File>()
val logEnabled = KEY_RECOMPOSITION_LOGS_ENABLED.toConfigKey<Boolean>()
val logTag = KEY_RECOMPOSITION_LOGS_TAG.toConfigKey<String>()
private inline fun <T> String.toConfigKey() = CompilerConfigurationKey<T>(this)
}
object Keys {
val logFile = KEY_LOG_FILE.toConfigKey<File>()
val logEnabled = KEY_RECOMPOSITION_LOGS_ENABLED.toConfigKey<Boolean>()
val logTag = KEY_RECOMPOSITION_LOGS_TAG.toConfigKey<String>()
val useRebugger = KEY_USE_REBUGGER.toConfigKey<Boolean>()
private inline fun <T> String.toConfigKey() = CompilerConfigurationKey<T>(this)
}

override val pluginId: String = KOTLIN_PLUGIN_ID
override val pluginId: String = KOTLIN_PLUGIN_ID

override val pluginOptions: Collection<CliOption> = listOf(
CliOption(
optionName = KEY_LOG_FILE,
valueDescription = "absolute file path",
description = "file for printing compiler debug logs",
required = false,
),
CliOption(
optionName = KEY_RECOMPOSITION_LOGS_ENABLED,
valueDescription = "bool <true | false>",
description = "Add logging for @Composable functions",
required = false,
),
CliOption(
optionName = KEY_RECOMPOSITION_LOGS_TAG,
valueDescription = "String",
description = "tag for android.util.Log",
required = false,
),
)
override val pluginOptions: Collection<CliOption> = listOf(
CliOption(
optionName = KEY_LOG_FILE,
valueDescription = "absolute file path",
description = "file for printing compiler debug logs",
required = false,
),
CliOption(
optionName = KEY_RECOMPOSITION_LOGS_ENABLED,
valueDescription = "bool <true | false>",
description = "Add logging for @Composable functions",
required = false,
),
CliOption(
optionName = KEY_RECOMPOSITION_LOGS_TAG,
valueDescription = "String",
description = "tag for android.util.Log",
required = false,
),
CliOption(
optionName = KEY_USE_REBUGGER,
valueDescription = "bool <true | false>",
description = "use rebugger for logging recompositions. More details: https://github.com/theapache64/rebugger",
required = false,
),
)

override fun processOption(
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration
) {
return when (option.optionName) {
KEY_RECOMPOSITION_LOGS_ENABLED -> configuration.put(Keys.logEnabled, value.toBoolean())
KEY_RECOMPOSITION_LOGS_TAG -> configuration.put(Keys.logTag, value)
KEY_LOG_FILE -> configuration.put(Keys.logFile, File(value))
else -> throw IllegalArgumentException("Unexpected config option ${option.optionName}")
override fun processOption(
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration,
) {
return when (option.optionName) {
KEY_RECOMPOSITION_LOGS_ENABLED -> configuration.put(Keys.logEnabled, value.toBoolean())
KEY_RECOMPOSITION_LOGS_TAG -> configuration.put(Keys.logTag, value)
KEY_LOG_FILE -> configuration.put(Keys.logFile, File(value))
KEY_USE_REBUGGER -> configuration.put(Keys.useRebugger, value.toBoolean())
else -> throw IllegalArgumentException("Unexpected config option ${option.optionName}")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,40 @@ class PluginComponentRegistrar : ComponentRegistrar {

override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
configuration: CompilerConfiguration,
) {
val enabled = configuration.get(
PluginCommandLineProcessor.Keys.logEnabled,
BuildConfig.DEFAULT_RECOMPOSITION_LOGS_ENABLED
BuildConfig.DEFAULT_RECOMPOSITION_LOGS_ENABLED,
)
val logTag = configuration.get(
PluginCommandLineProcessor.Keys.logTag,
BuildConfig.DEFAULT_RECOMPOSITION_LOGS_TAG
BuildConfig.DEFAULT_RECOMPOSITION_LOGS_TAG,
)

val logFile = configuration.get(
PluginCommandLineProcessor.Keys.logFile
PluginCommandLineProcessor.Keys.logFile,
)

val logger = when(logFile) {
val useRebugger = configuration.get(
PluginCommandLineProcessor.Keys.useRebugger,
BuildConfig.DEFAULT_USE_REBUGGER,
)

val logger = when (logFile) {
null -> EmptyLogger()
else -> FileLogger(logFile)
}


if (enabled) {
IrGenerationExtension.registerExtension(project, LogsGenerationExtension(tag = logTag))
IrGenerationExtension.registerExtension(project, HighlightGenerationExtension(logger))
IrGenerationExtension.registerExtension(
project = project,
extension = LogsGenerationExtension(tag = logTag, useRebugger = useRebugger),
)
IrGenerationExtension.registerExtension(
project = project,
extension = HighlightGenerationExtension(logger),
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.welltech.compiler_plugin.debug_logger

class EmptyLogger: Logger {
class EmptyLogger : Logger {
override fun logMsg(msg: String) {
// do nothing
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import java.io.File
import java.io.FileOutputStream

class FileLogger(
logFile: File
): Logger {
logFile: File,
) : Logger {

private val file = if (logFile.isDirectory) {
File(logFile, "recomposition-compiler-logs.txt")
Expand All @@ -27,4 +27,4 @@ class FileLogger(
writer.write(msg.toByteArray())
writer.write("\n".toByteArray())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ fun IrFunction.shouldAddLogsAndHighlight(pluginContext: IrPluginContext): Boolea
val isAnonymousFunction = this is IrSimpleFunction && (name.toString() == "<anonymous>")

return body != null
&& hasAnnotation(composableAnnotationName)
&& !isAnonymousFunction
&& !hasAnnotation(disableLogAnnotationName)
&& returnType == pluginContext.irBuiltIns.unitType
}
&& hasAnnotation(composableAnnotationName)
&& !isAnonymousFunction
&& !hasAnnotation(disableLogAnnotationName)
&& returnType == pluginContext.irBuiltIns.unitType
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.welltech.compiler_plugin.debug_logger.Logger
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI

class HighlightGenerationExtension(
private val logger: Logger
private val logger: Logger,
) : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
moduleFragment.transform(HighlightTransformer(pluginContext, logger), null)
Expand All @@ -31,20 +31,21 @@ class HighlightGenerationExtension(
@OptIn(FirIncompatiblePluginAPI::class)
private class HighlightTransformer(
private val pluginContext: IrPluginContext,
private val logger: Logger
private val logger: Logger,
) : IrElementTransformerVoidWithContext() {

private val funHighlight = pluginContext.referenceFunctions(FqName("com.welltech.recomposition_logger_runtime.highlight.highlightRecomposition"))
private val funHighlight =
pluginContext.referenceFunctions(FqName("com.welltech.recomposition_logger_runtime.highlight.highlightRecomposition"))
.single {
val parameters = it.owner.valueParameters
parameters.size == 1
&& parameters[0].type == pluginContext.irBuiltIns.stringType
&& parameters[0].type == pluginContext.irBuiltIns.stringType
}

private val initialModifier = pluginContext.referenceClass(FqName("androidx.compose.ui.Modifier.Companion"))!!

override fun visitFunctionNew(declaration: IrFunction): IrStatement {
if (declaration.shouldAddLogsAndHighlight(pluginContext) ) {
if (declaration.shouldAddLogsAndHighlight(pluginContext)) {
logger.logMsg("visit new composable fun: ${declaration.name}")
DeclarationIrBuilder(pluginContext, declaration.symbol)
.findAndModifyComposeFunctions(declaration.name.toString(), declaration.body!!.statements)
Expand All @@ -67,8 +68,8 @@ private class HighlightTransformer(

private fun IrStatement.isComposableCallWithModifier(): Boolean {
return this is IrCall
&& symbol.owner.hasAnnotation(composableAnnotationName)
&& getModifierArgument() != null
&& symbol.owner.hasAnnotation(composableAnnotationName)
&& getModifierArgument() != null
}

private fun IrBuilderWithScope.addHighlightModifier(funName: String, irCallStatement: IrCall) {
Expand All @@ -91,4 +92,4 @@ private class HighlightTransformer(
it.type.isClassType(FqNameUnsafe("androidx.compose.ui.Modifier"), false)
}
}
}
}
Loading