refactor: merge to BetterAndroid new usage

This commit is contained in:
2025-08-03 23:27:46 +08:00
parent 8990e03e97
commit ee97222bcb
15 changed files with 163 additions and 25 deletions

View File

@@ -39,15 +39,20 @@ fun KSDeclaration.getSimpleNameString(): String {
fun KSClassDeclaration.isSubclassOf(superType: KSType?): Boolean {
if (superType == null) return false
if (this == superType.declaration) return true
superTypes.forEach { parent ->
val resolvedParent = parent.resolve()
// Direct match.
if (resolvedParent == superType) return true
val parentDeclaration = resolvedParent.declaration as? KSClassDeclaration
?: return@forEach
// Recursively check the parent class.
if (parentDeclaration.isSubclassOf(superType)) return true
}; return false
}
return false
}
fun KSClassDeclaration.asType() = asType(emptyList())

View File

@@ -71,6 +71,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
override fun startProcess(resolver: Resolver) {
Processor.init(logger, resolver)
val dependencies = Dependencies(aggregating = true, *resolver.getAllFiles().toList().toTypedArray())
resolver.getSymbolsWithAnnotation(HikageViewSpec.CLASS)
.filterIsInstance<KSClassDeclaration>()
@@ -79,9 +80,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
ksClass.annotations.forEach {
// Get annotation parameters.
val (annotation, declaration) = HikageViewSpec.create(resolver, it, ksClass)
performers += Performer(annotation, declaration)
}
}
resolver.getSymbolsWithAnnotation(HikageViewDeclarationSpec.CLASS)
.filterIsInstance<KSClassDeclaration>()
.distinctBy { it.qualifiedName }
@@ -89,14 +92,17 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
ksClass.annotations.forEach {
// Get annotation parameters.
val (annotation, declaration) = HikageViewDeclarationSpec.create(resolver, it, ksClass)
performers += Performer(annotation, declaration)
}
}
processPerformer(dependencies)
}
private fun processPerformer(dependencies: Dependencies) {
val duplicatedItems = performers.groupBy { it.declaration.key }.filter { it.value.size > 1 }.flatMap { it.value }
require(duplicatedItems.isEmpty()) {
"Discover duplicate @HikageView or @HikageViewDeclaration's class name or alias definitions, " +
"you can re-specify the class name using the `alias` parameter.\n" +
@@ -109,25 +115,30 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
private fun generateCodeFile(dependencies: Dependencies, performer: Performer) {
val classNameSet = performer.declaration.alias ?: performer.declaration.className
val fileName = "_$classNameSet"
val viewClass = performer.declaration.toClassName().let {
val packageName = it.packageName
val simpleName = it.simpleName
val topClassName = if (simpleName.contains(".")) simpleName.split(".")[0] else null
// com.example.MyViewScope
// com.example.MyViewScope.MyView
topClassName?.let { name -> ClassName(packageName, name) } to it
}
val lparamsClass = performer.annotation.lparams?.let {
val packageName = it.packageName.asString()
val subClassName = it.getSimpleNameString()
val simpleName = it.simpleName.asString()
val topClassName = subClassName.replace(".$simpleName", "")
// android.view.ViewGroup
// android.view.ViewGroup.LayoutParams
(if (topClassName != subClassName)
ClassName(packageName, topClassName)
else null) to ClassName(packageName, subClassName)
}
val packageName = "$PACKAGE_NAME_PREFIX.${performer.declaration.packageName}"
val fileSpec = FileSpec.builder(packageName, fileName).apply {
addFileComment(
@@ -140,6 +151,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
**DO NOT EDIT THIS FILE MANUALLY**
""".trimIndent()
)
addAnnotation(
AnnotationSpec.builder(Suppress::class)
.addMember("%S, %S, %S", "unused", "FunctionName", "DEPRECATION")
@@ -150,17 +162,22 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
.addMember("%S", "${classNameSet}Performer")
.build()
)
addImport(DeclaredSymbol.ANDROID_VIEW_PACKAGE_NAME, DeclaredSymbol.ANDROID_VIEW_GROUP_CLASS_NAME)
addImport(DeclaredSymbol.HIKAGE_CORE_PACKAGE_NAME, DeclaredSymbol.HIKAGE_CLASS_NAME)
// Kotlin's import rule is to introduce the parent class that also needs to be introduced at the same time.
// If a child class exists, it needs to import the parent class,
// and kotlinpoet will not perform this operation automatically.
viewClass.first?.let { addImport(it.packageName, it.simpleName) }
lparamsClass?.first?.let { addImport(it.packageName, it.simpleName) }
// May conflict with other [LayoutParams].
lparamsClass?.second?.let { addAliasedImport(it, it.getTypedSimpleName()) }
addAliasedImport(ViewGroupLpClass, ViewGroupLpClass.getTypedSimpleName())
addAliasedImport(HikageLparamClass, HikageLparamClass.getTypedSimpleName())
addFunction(FunSpec.builder(classNameSet).apply {
addKdoc(
"""
@@ -170,10 +187,12 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
@return [${performer.declaration.className}]
""".trimIndent()
)
addAnnotation(HikageableClass)
addModifiers(KModifier.INLINE)
addTypeVariable(TypeVariableName(name = "LP", ViewGroupLpClass).copy(reified = true))
receiver(PerformerClass.parameterizedBy(TypeVariableName("LP")))
addParameter(
ParameterSpec.builder(name = "lparams", HikageLparamClass.copy(nullable = true))
.defaultValue("null")
@@ -203,9 +222,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
)
addStatement("return ViewGroup<${performer.declaration.className}, ${it.simpleName}>(lparams, id, init, performer)")
} ?: addStatement("return View<${performer.declaration.className}>(lparams, id, init)")
returns(viewClass.second)
}.build())
}.build()
// May already exists, no need to generate.
runCatching {
fileSpec.writeTo(codeGenerator, dependencies)
@@ -239,6 +260,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
ksType.declaration.qualifiedName?.asString() != Any::class.qualifiedName
}
var resolvedLparams = lparamsType?.declaration?.getClassDeclaration(resolver)
when {
// If the current view is not a view group but the lparams parameter is declared,
// remove the lparams parameter.
@@ -253,6 +275,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
// set the default type parameter for it.
declaration.isViewGroup && resolvedLparams == null -> resolvedLparams = lparamsDeclaration
}
// Verify layout parameter class.
if (resolvedLparams != null) require(resolvedLparams.isSubclassOf(lparamsDeclaration.asType())) {
val resolvedLparamsName = resolvedLparams.qualifiedName?.asString()
@@ -271,17 +294,21 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val packageName = ksClass.packageName.asString()
val className = ksClass.getSimpleNameString()
val isViewGroup = ksClass.isSubclassOf(viewGroupDeclaration.asType())
var _alias = alias
// If no alias name is set, if the class name contains a subclass,
// replace it with an underscore and use it as an alias name.
if (_alias.isNullOrBlank() && className.contains("."))
_alias = className.replace(".", "_")
_alias = _alias?.takeIf { it.isNotBlank() }
val declaration = ViewDeclaration(packageName, className, _alias, isViewGroup, baseType)
// Verify the legality of the class name.
if (!_alias.isNullOrBlank()) require(ClassDetector.verify(_alias)) {
"Declares @$tagName's alias \"$_alias\" is illegal.\n${declaration.locateDesc}"
}
// [ViewGroup] cannot be new instance.
require(ksClass != viewGroupDeclaration) {
"Declares @$tagName's class must not be a directly \"${DeclaredSymbol.ANDROID_VIEW_GROUP_CLASS}\".\n${declaration.locateDesc}"
@@ -290,6 +317,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
require(ksClass.isSubclassOf(viewDeclaration.asType())) {
"Declares @$tagName's class must be subclass of \"${DeclaredSymbol.ANDROID_VIEW_CLASS}\".\n${declaration.locateDesc}"
}
return declaration
}
}
@@ -332,9 +360,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val alias = annotation.arguments.getOrNull<String>("alias")
val requireInit = annotation.arguments.getOrNull<Boolean>("requireInit") ?: false
val requirePerformer = annotation.arguments.getOrNull<Boolean>("requirePerformer") ?: false
// Solve the actual content of the annotation parameters.
val declaration = Processor.createViewDeclaration(NAME, alias, ksClass)
val resolvedLparams = Processor.resolvedLparamsDeclaration(NAME, resolver, declaration, lparams)
return HikageViewSpec(resolvedLparams, alias, requireInit, requirePerformer) to declaration
}
}
@@ -363,9 +393,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val alias = annotation.arguments.getOrNull<String>("alias")
val requireInit = annotation.arguments.getOrNull<Boolean>("requireInit") ?: false
val requirePerformer = annotation.arguments.getOrNull<Boolean>("requirePerformer") ?: false
// Solve the actual content of the annotation parameters.
val resolvedView = view?.declaration?.getClassDeclaration(resolver) ?: error("Internal error.")
val declaration = Processor.createViewDeclaration(NAME, alias, resolvedView, ksClass)
// Only object classes can be used as view declarations.
require(ksClass.classKind == ClassKind.OBJECT) {
"Declares @$NAME's class must be an object.\n${declaration.locateDesc}"
@@ -373,6 +405,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
require(!ksClass.isCompanionObject) {
"Declares @$NAME's class must not be a companion object.\n${declaration.locateDesc}"
}
val resolvedLparams = Processor.resolvedLparamsDeclaration(NAME, resolver, declaration, lparams)
return HikageViewDeclarationSpec(resolvedView, resolvedLparams, alias, requireInit, requirePerformer) to declaration
}