Skip to content

Commit

Permalink
Revise typedef handling and generation (#77)
Browse files Browse the repository at this point in the history
* Outsource typedefs into an own structure

* Add new extension method to write the typedef objects

* Add structure to manage or write typedef objects

* Integrate the updated typedef structure into other spec objects, writers

* Add test classes for the typedef structure

* Update given tests to support typedef creation

* Remove typedef as valid function modifier

* Remove empty line

* Remove unused code

* Improve code layout

* Remove out commented code

* Improve new line write

* Add support for multiple cast arguments

* Add test for the multiple cast arguments

* Remove unused import
  • Loading branch information
theEvilReaper authored Jan 12, 2024
1 parent abc2577 commit a03c810
Show file tree
Hide file tree
Showing 18 changed files with 455 additions and 60 deletions.
4 changes: 4 additions & 0 deletions src/main/kotlin/net/theevilreaper/dartpoet/DartFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class DartFile internal constructor(
internal val libImport = DirectiveOrdering.sortDirectives<LibraryDirective>(LibraryDirective::class, directives)
internal val exportDirectives = DirectiveOrdering.sortDirectives<ExportDirective>(ExportDirective::class, directives)
internal val relativeImports = DirectiveOrdering.sortDirectives<RelativeDirective>(RelativeDirective::class, directives)

internal val typeDefs = builder.typeDefs.toImmutableList()
internal val hasTypeDefs = typeDefs.isNotEmpty()

init {
check(name.trim().isNotEmpty()) { "The name of a class can't be empty (ONLY SPACES ARE NOT ALLOWED" }
if (libImport.isNotEmpty()) {
Expand Down
20 changes: 20 additions & 0 deletions src/main/kotlin/net/theevilreaper/dartpoet/DartFileBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.theevilreaper.dartpoet.clazz.ClassSpec
import net.theevilreaper.dartpoet.code.CodeBlock
import net.theevilreaper.dartpoet.extension.ExtensionSpec
import net.theevilreaper.dartpoet.directive.Directive
import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec
import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec
import net.theevilreaper.dartpoet.property.PropertySpec
import net.theevilreaper.dartpoet.util.DEFAULT_INDENT
Expand All @@ -20,6 +21,7 @@ class DartFileBuilder(
internal val annotations: MutableList<AnnotationSpec> = mutableListOf()
internal val extensionStack: MutableList<ExtensionSpec> = mutableListOf()
internal val constants: MutableSet<ConstantPropertySpec> = mutableSetOf()
internal val typeDefs: MutableList<TypeDefSpec> = mutableListOf()
internal var indent = DEFAULT_INDENT

/**
Expand Down Expand Up @@ -71,6 +73,24 @@ class DartFileBuilder(
this.extensionStack += extensions
}

/**
* Add a type definition to the file builder.
* @param typeDef the type definition to add
* @return the current instance of [DartFileBuilder]
*/
fun typeDef(typeDef: TypeDefSpec) = apply {
this.typeDefs += typeDef
}

/**
* Add an array of type definitions to the file builder.
* @param typeDef the type definitions to add
* @return the current instance of [DartFileBuilder]
*/
fun typeDef(vararg typeDef: TypeDefSpec) = apply {
this.typeDefs += typeDef
}

fun type(dartFileSpec: ClassSpec) = apply {
this.specTypes += dartFileSpec
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import net.theevilreaper.dartpoet.function.FunctionSpec
import net.theevilreaper.dartpoet.meta.SpecData
import net.theevilreaper.dartpoet.meta.SpecMethods
import net.theevilreaper.dartpoet.function.constructor.ConstructorSpec
import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec
import net.theevilreaper.dartpoet.property.PropertySpec
import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec
import net.theevilreaper.dartpoet.type.TypeName
Expand Down Expand Up @@ -37,6 +38,7 @@ class ClassBuilder internal constructor(
internal val functionStack: MutableList<FunctionSpec> = mutableListOf()
internal val enumPropertyStack: MutableList<EnumPropertySpec> = mutableListOf()
internal val constantStack: MutableSet<ConstantPropertySpec> = mutableSetOf()
internal val typedefs: MutableList<TypeDefSpec> = mutableListOf()
internal var superClass: TypeName? = null
internal var inheritKeyWord: InheritKeyword? = null
internal var endWithNewLine = false
Expand All @@ -57,6 +59,14 @@ class ClassBuilder internal constructor(
this.constantStack += constants
}

fun typedef(typeDefSpec: TypeDefSpec) = apply {
this.typedefs += typeDefSpec
}

fun typedef(vararg typeDefSpec: TypeDefSpec) = apply {
this.typedefs += typeDefSpec
}

/**
* Add a [EnumPropertySpec] to the spec.
* @param enumPropertySpec the property to add
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ class ClassSpec internal constructor(
internal val superClass = builder.superClass
internal val inheritKeyWord = builder.inheritKeyWord
internal val classModifiers = modifiers.filter { it != WITH }.toImmutableSet()
internal val functions = builder.functionStack.filter { !it.isTypeDef }.toImmutableSet()
internal val typeDefs = builder.typedefs.toImmutableList()
internal val functions = builder.functionStack.toImmutableSet()
internal val properties = builder.propertyStack.toImmutableSet()
internal val constructors = builder.constructorStack.toImmutableSet()
internal val typeDefStack = builder.functionStack.filter { it.isTypeDef }.toImmutableSet()
internal val enumPropertyStack = builder.enumPropertyStack.toImmutableList()
internal var constantStack = builder.constantStack.toImmutableSet()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import net.theevilreaper.dartpoet.extension.ExtensionSpec
import net.theevilreaper.dartpoet.function.FunctionSpec
import net.theevilreaper.dartpoet.function.constructor.ConstructorSpec
import net.theevilreaper.dartpoet.directive.Directive
import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec
import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec
import net.theevilreaper.dartpoet.parameter.ParameterSpec
import net.theevilreaper.dartpoet.property.PropertySpec
Expand Down Expand Up @@ -199,6 +200,20 @@ fun Set<ConstantPropertySpec>.emitConstants(
}
}

fun List<TypeDefSpec>.emitTypeDefs(
codeWriter: CodeWriter,
emitBlock: (TypeDefSpec) -> Unit = { it.write(codeWriter) }
) = with(codeWriter) {
val emitNewLines = size > 1
forEachIndexed { index, typeDefSpec ->
if (index > 0 && emitNewLines) {
emit(NEW_LINE)
}
emitBlock(typeDefSpec)
}
emit(NEW_LINE)
}

fun Set<PropertySpec>.emitProperties(
codeWriter: CodeWriter,
forceNewLines: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ internal class ClassWriter : Writeable<ClassSpec> {
writeAnonymousClass(spec, writer)
return
}
spec.typeDefStack.emitFunctions(writer)

if (spec.typeDefStack.isNotEmpty()) {
writer.emit(NEW_LINE)
}

spec.annotations.emitAnnotations(writer, inLineAnnotations = false)
writeClassHeader(spec, writer)
Expand Down Expand Up @@ -100,13 +95,8 @@ internal class ClassWriter : Writeable<ClassSpec> {
}

private fun writeAnonymousClass(spec: ClassSpec, writer: CodeWriter) {
spec.typeDefStack.emitFunctions(writer) {
it.write(writer)
}

spec.functions.emitFunctions(writer) {
it.write(writer)
}
spec.typeDefs.emitTypeDefs(writer)
spec.functions.emitFunctions(writer)

if (spec.endsWithNewLine) {
writer.emit(NEW_LINE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ internal class DartFileWriter : Writeable<DartFile>, DocumentationAppender {
writer.emit(NEW_LINE)
}

if (spec.hasTypeDefs) {
spec.typeDefs.emitTypeDefs(writer)
}

if (spec.types.isNotEmpty()) {
spec.types.forEach {
classWriter.write(it, writer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@ internal class FunctionWriter : Writeable<FunctionSpec> {
if (spec.hasDocs) {
spec.docs.forEach { writer.emitDoc(it) }
}
if (spec.isTypeDef) {
writeTypeDef(spec, writer)
return
}

if (!spec.isTypeDef && spec.annotation.isNotEmpty()) {
if (spec.annotation.isNotEmpty()) {
spec.annotation.forEach { it.write(writer) }
writer.emit(NEW_LINE)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.theevilreaper.dartpoet.code.writer

import net.theevilreaper.dartpoet.DartModifier
import net.theevilreaper.dartpoet.code.CodeWriter
import net.theevilreaper.dartpoet.code.Writeable
import net.theevilreaper.dartpoet.code.emitParameters
import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec
import net.theevilreaper.dartpoet.util.SEMICOLON

class TypeDefWriter : Writeable<TypeDefSpec> {
override fun write(spec: TypeDefSpec, writer: CodeWriter) {
writer.emit("${DartModifier.TYPEDEF.identifier}·${spec.typeDefName}")
if (spec.typeCasts.isNotEmpty()) {
val typesAsString = spec.typeCasts.joinToString(separator = "") { it.toString() }
writer.emitCode("<%L>", typesAsString)
}
writer.emit("·=·")
writer.emitCode("%T", spec.returnType)

if (spec.name != null) {
writer.emitCode("·%L", spec.name)
}

if (spec.hasParameters) {
writer.emit("(")
spec.parameters.emitParameters(writer, forceNewLines = false, emitBrackets = false, emitSpace = spec.parameters.size > 1)
writer.emit(")")
}
writer.emit(SEMICOLON)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class FunctionBuilder internal constructor(
internal var async: Boolean = false
internal var returnType: TypeName? = null
internal val body: CodeBlock.Builder = CodeBlock.builder()
internal var typedef: Boolean = false
internal var typeCast: TypeName? = null
internal var setter: Boolean = false
internal var getter: Boolean = false
Expand Down Expand Up @@ -94,14 +93,6 @@ class FunctionBuilder internal constructor(
*/
fun typeCast(cast: KClass<*>) = apply { this.typeCast = cast.asTypeName() }

/**
* If the function should be generated as typedef definition.
* @param typeDef true for a typedef
*/
fun typedef(typeDef: Boolean) = apply {
this.typedef = typeDef
}

fun addCode(format: String, vararg args: Any?) = apply {
body.add(format, *args)
}
Expand Down Expand Up @@ -180,12 +171,6 @@ class FunctionBuilder internal constructor(
* @return the created instance
*/
fun build(): FunctionSpec {
// Remove typedef keyword from the list to prevent problems
if (specData.modifiers.contains(DartModifier.TYPEDEF)) {
typedef(true)
specData.modifiers.remove(DartModifier.TYPEDEF)
}

return FunctionSpec(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import net.theevilreaper.dartpoet.util.toImmutableSet
class FunctionSpec(
builder: FunctionBuilder
) {

internal val name = builder.name
internal val returnType: TypeName? = builder.returnType
internal val body: CodeBlock = builder.body.build()
Expand All @@ -33,8 +32,7 @@ class FunctionSpec(
internal var modifiers: Set<DartModifier> = builder.specData.modifiers.also {
hasAllowedModifiers(it, ALLOWED_FUNCTION_MODIFIERS, "function")
}.filter { it != DartModifier.PRIVATE && it != DartModifier.PUBLIC }.toImmutableSet()
internal val isPrivate = builder.specData.modifiers.contains(DartModifier.PRIVATE)
internal val isTypeDef = builder.typedef
internal val isPrivate = builder.specData.modifiers.remove(DartModifier.PRIVATE)
internal val typeCast = builder.typeCast
internal val asSetter = builder.setter
internal val isGetter = builder.getter
Expand All @@ -49,7 +47,6 @@ class FunctionSpec(
}

init {
//check(!isTypeDef && annotation.isNotEmpty()) { "A typedef can't have annotations" }
require(name.trim().isNotEmpty()) { "The name of a function can't be empty" }
require(body.isEmpty() || !modifiers.contains(DartModifier.ABSTRACT)) { "An abstract method can't have a body" }

Expand Down Expand Up @@ -89,7 +86,6 @@ class FunctionSpec(
builder.modifiers(*this.modifiers.toTypedArray())
builder.parameters.addAll(this.parameters)
builder.async = this.isAsync
builder.typedef = this.isTypeDef
builder.typeCast = this.typeCast
builder.setter = this.asSetter
builder.getter = this.isGetter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package net.theevilreaper.dartpoet.function.typedef

import net.theevilreaper.dartpoet.parameter.ParameterSpec
import net.theevilreaper.dartpoet.type.ClassName
import net.theevilreaper.dartpoet.type.TypeName
import net.theevilreaper.dartpoet.type.asTypeName
import kotlin.reflect.KClass

/**
* The builder is used to create a type definition with a specific name and optional type cast.
* After the construction the builder maps the data into a [TypeDefSpec] object.
*
* @property typeDefName the name of the type definition.
* @property typeCasts optional array of type-cast for the type definition.
*/
class TypeDefBuilder internal constructor(
val typeDefName: String,
vararg val typeCasts: TypeName? = emptyArray()
) {
/**
* The name of the type definition.
*/
var name: String? = null

/**
* The return type of the type definition.
*/
var returnType: TypeName? = null

/**
* List of parameters associated with the type definition.
*/
val parameters: MutableList<ParameterSpec> = mutableListOf()

/**
* Sets the name of the type definition.
*
* @param name the name of the type definition.
* @return the current instance of [TypeDefBuilder].
*/
fun name(name: String) = apply {
this.name = name
}

/**
* Adds a parameter to the list of parameters.
*
* @param parameterSpec the parameter specification.
* @return the current instance of [TypeDefBuilder].
*/
fun parameter(parameterSpec: ParameterSpec) = apply {
this.parameters += parameterSpec
}

/**
* Adds multiple parameters to the list of parameters.
*
* @param parameterSpecs the parameter specifications.
* @return the current instance of [TypeDefBuilder].
*/
fun parameters(vararg parameterSpecs: ParameterSpec) = apply {
this.parameters += parameterSpecs
}

/**
* Sets the return type of the type definition.
*
* @param typeName the return type as a [TypeName].
* @return the current instance of [TypeDefBuilder].
*/
fun returns(typeName: TypeName) = apply {
this.returnType = typeName
}

/**
* Sets the return type of the type definition.
*
* @param typeName the return type as a [ClassName].
* @return the current instance of [TypeDefBuilder].
*/
fun returns(typeName: ClassName) = apply {
this.returnType = typeName
}

/**
* Sets the return type of the type definition using a [KClass].
*
* @param typeName the return type as a [KClass].
* @return the current instance of [TypeDefBuilder].
*/
fun returns(typeName: KClass<*>) = apply {
this.returnType = typeName.asTypeName()
}

/**
* Builds and returns an instance of [TypeDefSpec] based on the configuration.
*
* @return an instance of [TypeDefSpec].
*/
fun build(): TypeDefSpec {
return TypeDefSpec(this)
}
}
Loading

0 comments on commit a03c810

Please sign in to comment.