Skip to content

Commit

Permalink
Revise parameter generation (#78)
Browse files Browse the repository at this point in the history
* Add abstract keyword to the allowed keywords for functions

* Add class which allows parameter filtering

* Add second writeInitBlock which takes a spec object

* Remove bracket parameters from the emitParameters method

* Update writer structure

* Update writer structure

* Improve parameter write process for functions

* Improve parameter sorting

* Improve variable handling

* Add new test cases

* Remove this. call

* Improve parameter generation

* Add new test cases

* Fix wrong changes from the latest merge

* Update parameter generation in the typedefs

* Add and update tests

* Remove unused code

* Remove leading space
  • Loading branch information
theEvilReaper authored Jan 15, 2024
1 parent b0c52ff commit 5026264
Show file tree
Hide file tree
Showing 22 changed files with 535 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class CodeWriter constructor(
for (line in s.split('\n')) {
// Emit a newline character. Make sure blank lines in KDoc & comments look good.
if (!first) {
if ( comment && trailingNewline) {
if (comment && trailingNewline) {
emitIndentation()
out.appendNonWrapping(DOCUMENTATION_CHAR)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,9 @@ internal fun Set<ConstructorSpec>.emitConstructors(
internal fun List<ParameterSpec>.emitParameters(
codeWriter: CodeWriter,
forceNewLines: Boolean = false,
emitBrackets: Boolean = true,
emitSpace: Boolean = true,
emitBlock: (ParameterSpec) -> Unit = { it.write(codeWriter) }
) = with(codeWriter) {
if (emitBrackets) {
emit("(")
}
if (isNotEmpty()) {
val emitComma = size > 1
forEachIndexed { index, parameter ->
Expand All @@ -136,9 +132,6 @@ internal fun List<ParameterSpec>.emitParameters(
}
}
}
if (emitBrackets) {
emit(")")
}
}

internal fun List<ExtensionSpec>.emitExtensions(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package net.theevilreaper.dartpoet.code

/**
* An internal interface for appending initializer blocks to a [CodeWriter] for a given type [T].
* Initializer blocks contain data that should be emitted into a [CodeWriter] instance.
* This interface provides methods to write initializer blocks for a [CodeBlock] or an object of type [T].
*
* @param T the type of object for which initializer blocks are appended.
* @author theEvilReaper
* @since 1.0.0
*/
internal interface InitializerAppender {
internal interface InitializerAppender<T: Any> {

/**
* When a spec object contains data for an initializer block it should be emitted into a writer instance.
Expand All @@ -17,4 +21,15 @@ internal interface InitializerAppender {
writer.emit("·=·")
writer.emitCode(initBlock, isConstantContext)
}
}

/**
* When a spec object contains data for an initializer block it should be emitted into a writer instance.
* @param spec the spec object which contains the initializer block
* @param writer the [CodeWriter] instance to write the code
* @param isConstantContext a flag indicating whether the context is constant (default is true).
* @throws UnsupportedOperationException if this method is called and not implemented.
*/
fun writeInitBlock(spec: T, writer: CodeWriter, isConstantContext: Boolean = true) {
throw UnsupportedOperationException("Not implemented yet")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec
import net.theevilreaper.dartpoet.util.SEMICOLON
import net.theevilreaper.dartpoet.util.SPACE

internal class ConstantPropertyWriter : Writeable<ConstantPropertySpec>, InitializerAppender, VariableAppender {
internal class ConstantPropertyWriter : Writeable<ConstantPropertySpec>, InitializerAppender<PropertyWriter>, VariableAppender {

override fun write(spec: ConstantPropertySpec, writer: CodeWriter) {
val modifiersAsString = spec.modifiers.joinToString(separator = SPACE, postfix = SPACE) { it.identifier }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal class ConstructorWriter : Writeable<ConstructorSpec>, DocumentationAppe

writer.emit("(")

spec.parameters.emitParameters(writer, emitBrackets = false)
spec.parameters.emitParameters(writer)

if (spec.hasNamedParameters) {
if (spec.parameters.isNotEmpty()) {
Expand All @@ -46,7 +46,7 @@ internal class ConstructorWriter : Writeable<ConstructorSpec>, DocumentationAppe
writer.emit(NEW_LINE)
writer.indent()

spec.requiredAndNamedParameters.emitParameters(writer, emitBrackets = false, emitSpace = false, forceNewLines = true) {
spec.requiredAndNamedParameters.emitParameters(writer, emitSpace = false, forceNewLines = true) {
it.write(writer)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import net.theevilreaper.dartpoet.code.CodeWriter
import net.theevilreaper.dartpoet.code.Writeable
import net.theevilreaper.dartpoet.code.emitParameters
import net.theevilreaper.dartpoet.function.FunctionSpec
import net.theevilreaper.dartpoet.parameter.ParameterSpec
import net.theevilreaper.dartpoet.util.*
import net.theevilreaper.dartpoet.util.EMPTY_STRING
import net.theevilreaper.dartpoet.util.NEW_LINE
import net.theevilreaper.dartpoet.util.SEMICOLON
Expand Down Expand Up @@ -68,19 +70,11 @@ internal class FunctionWriter : Writeable<FunctionSpec> {
writer.emit("·=>·")
writer.emitCode(spec.body.returnsWithoutLinebreak(), ensureTrailingNewline = false)
} else {
spec.parameters.emitParameters(writer)
writeParameters(spec, writer)
writeBody(spec, writer)
}
}

private fun writeAsyncDeclaration(writer: CodeWriter) {
writer.emitCode("Future<%L>", VOID.identifier)
}

private fun writeAsyncTypeDeclaration(spec: FunctionSpec, writer: CodeWriter) {
writer.emitCode("Future<%T>·", spec.returnType)
}

private fun writeBody(spec: FunctionSpec, writer: CodeWriter) {
if (spec.body.isEmpty()) {
writer.emit(SEMICOLON)
Expand All @@ -102,19 +96,57 @@ internal class FunctionWriter : Writeable<FunctionSpec> {
}
}

private fun writeTypeDef(spec: FunctionSpec, codeWriter: CodeWriter) {
codeWriter.emit("${TYPEDEF.identifier}·")
codeWriter.emit("${spec.name}·")
codeWriter.emit("")
codeWriter.emit("${spec.returnType}")
spec.parameters.emitParameters(
codeWriter,
emitBrackets = spec.parameters.isNotEmpty(),
forceNewLines = false
) {
it.write(codeWriter)
private fun writeParameters(spec: FunctionSpec, codeWriter: CodeWriter) {
if (!spec.hasParameters) {
codeWriter.emit("()")
return
}
codeWriter.emit("(")
spec.normalParameter.emitParameters(codeWriter, forceNewLines = false)

if (spec.normalParameter.isNotEmpty() && (spec.hasAdditionalParameters || spec.parametersWithDefaults.isNotEmpty())) {
codeWriter.emit(", ")
}

if (spec.hasAdditionalParameters) {
emitRequiredAndNamedParameter(spec, codeWriter)
}

if (spec.parametersWithDefaults.isNotEmpty()) {
codeWriter.emit("[")
spec.parametersWithDefaults.emitParameters(codeWriter, forceNewLines = false)
codeWriter.emit("]")
}

codeWriter.emit(")")
}

private fun emitRequiredAndNamedParameter(spec: FunctionSpec, codeWriter: CodeWriter) {
codeWriter.emit("$CURLY_OPEN")

val namedRequired = spec.namedParameter.filter { it.isRequired && !it.hasInitializer }.toImmutableList()

writeParameters(namedRequired, spec.normalParameter.isNotEmpty(), codeWriter)
writeParameters(spec.requiredParameter, namedRequired.isNotEmpty(), codeWriter)

val test =
spec.namedParameter.minus(namedRequired).filter { it.isNullable || it.hasInitializer }.toImmutableList()

writeParameters(test, spec.requiredParameter.isNotEmpty() || namedRequired.isNotEmpty(), codeWriter)

codeWriter.emit("$CURLY_CLOSE")
}

private fun writeParameters(
parameters: List<ParameterSpec>,
emitSpaceComma: Boolean = false,
codeWriter: CodeWriter
) {
if (parameters.isEmpty()) return
if (emitSpaceComma) {
codeWriter.emit(", ")
}
codeWriter.emit(SEMICOLON)
parameters.emitParameters(codeWriter, forceNewLines = false)
}

private val RETURN_EXPRESSION_BODY_PREFIX_SPACE = CodeBlock.of("return ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import net.theevilreaper.dartpoet.parameter.ParameterSpec
* @version 1.0.0
* @since 1.0.0
*/
internal class ParameterWriter : Writeable<ParameterSpec>, InitializerAppender {
internal class ParameterWriter : Writeable<ParameterSpec>, InitializerAppender<ParameterSpec> {

/**
* The method contains the main logic to write a [ParameterSpec] to code.
Expand All @@ -21,16 +21,24 @@ internal class ParameterWriter : Writeable<ParameterSpec>, InitializerAppender {
override fun write(spec: ParameterSpec, writer: CodeWriter) {
spec.annotations.emitAnnotations(writer, endWithNewLine = false)

if (spec.isRequired && (!spec.isNamed || !spec.hasInitializer)) {
writer.emit("${DartModifier.REQUIRED.identifier}·")
}

if (spec.type != null) {
writer.emitCode("%T", spec.type)
} else {
if (spec.isRequired) {
writer.emit("${DartModifier.REQUIRED.identifier}·")
}
writer.emit("this.")
}
writer.emit(if (spec.isNullable) "" else if (spec.type != null) "·" else "")
val emitNullable = if (spec.isNullable) "" else if (spec.type != null) "·" else ""
writer.emit(emitNullable)
writer.emit(spec.name)
writeInitBlock(spec.initializer ?: CodeBlock.EMPTY, writer)
writeInitBlock(spec, writer)
}

override fun writeInitBlock(spec: ParameterSpec, writer: CodeWriter, isConstantContext: Boolean) {
val initBlock = spec.initializer ?: CodeBlock.EMPTY
if (initBlock.isEmpty()) return
if (spec.isNamed && !spec.hasInitializer) return
writer.emit("·=·")
writer.emitCode(initBlock, isConstantContext)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import net.theevilreaper.dartpoet.util.SPACE
* @author theEvilReaper
* @since 1.0.0
*/
internal class PropertyWriter : Writeable<PropertySpec>, VariableAppender, DocumentationAppender, InitializerAppender {
internal class PropertyWriter : Writeable<PropertySpec>, VariableAppender, DocumentationAppender, InitializerAppender<PropertySpec> {

/**
* Writes the given [PropertySpec] into the [CodeWriter].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ 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.parameter.ParameterSpec
import net.theevilreaper.dartpoet.util.SEMICOLON
import net.theevilreaper.dartpoet.util.toImmutableList

class TypeDefWriter : Writeable<TypeDefSpec> {

override fun write(spec: TypeDefSpec, writer: CodeWriter) {
writer.emit("${DartModifier.TYPEDEF.identifier}·${spec.typeDefName}")
if (spec.typeCasts.isNotEmpty()) {
Expand All @@ -21,11 +24,53 @@ class TypeDefWriter : Writeable<TypeDefSpec> {
writer.emitCode("·%L", spec.name)
}

emitParameters(spec, writer)
writer.emit(SEMICOLON)
}

private fun emitParameters(spec: TypeDefSpec, codeWriter: CodeWriter) {
if (spec.hasParameters) {
writer.emit("(")
spec.parameters.emitParameters(writer, forceNewLines = false, emitBrackets = false, emitSpace = spec.parameters.size > 1)
writer.emit(")")
codeWriter.emit("(")
callParameterWrite(codeWriter, spec.normalParameter) { it.isNotEmpty() }
if (spec.hasAdditionalParameters) {
if (spec.hasParameters) {
codeWriter.emit(",")
}
codeWriter.emit("·{")
val namedRequired = spec.namedParameter.filter { it.isRequired && !it.hasInitializer }.toImmutableList()

callParameterWrite(codeWriter, namedRequired) { it.isNotEmpty() }
callParameterWrite(codeWriter, spec.requiredParameter) { it.isNotEmpty() }

if (namedRequired.isNotEmpty()) {
codeWriter.emitCode("")
}

val test =
spec.namedParameter.minus(namedRequired).filter { it.isNullable || it.hasInitializer }.toImmutableList()
callParameterWrite(codeWriter, test) { it.isNotEmpty() }
codeWriter.emit("}")
}

if (spec.parametersWithDefaults.isNotEmpty()) {
if (spec.hasParameters) {
codeWriter.emit(", ")
}
codeWriter.emit("[")
callParameterWrite(codeWriter, spec.parametersWithDefaults) { it.isNotEmpty() }
codeWriter.emit("]")
}

codeWriter.emit(")")
}
writer.emit(SEMICOLON)
}

private inline fun callParameterWrite(
writer: CodeWriter,
parameters: List<ParameterSpec>,
crossinline predicate: (List<ParameterSpec>) -> Boolean,
) {
if (!predicate.invoke(parameters)) return
parameters.emitParameters(writer, forceNewLines = false, emitSpace = parameters.size > 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,22 @@ class FunctionSpec(
internal val name = builder.name
internal val returnType: TypeName? = builder.returnType
internal val body: CodeBlock = builder.body.build()
internal val parameters: List<ParameterSpec> = builder.parameters.toImmutableList()
private val parameters: List<ParameterSpec> = builder.parameters.toImmutableList()
internal val isAsync: Boolean = builder.async
internal val annotation: Set<AnnotationSpec> = builder.specData.annotations.toImmutableSet()
internal var modifiers: Set<DartModifier> = builder.specData.modifiers.also {
internal val modifiers: Set<DartModifier> = builder.specData.modifiers.also {
hasAllowedModifiers(it, ALLOWED_FUNCTION_MODIFIERS, "function")
}.filter { it != DartModifier.PRIVATE && it != DartModifier.PUBLIC }.toImmutableSet()
internal val parametersWithDefaults =
ParameterFilter.filterParameter(parameters) { !it.isRequired && it.hasInitializer }
internal val requiredParameter =
ParameterFilter.filterParameter(parameters) { it.isRequired && !it.isNamed && !it.hasInitializer }
internal val namedParameter = ParameterFilter.filterParameter(parameters) { it.isNamed }
internal val normalParameter =
parameters.minus(parametersWithDefaults).minus(requiredParameter).minus(namedParameter).toImmutableList()
internal val hasParameters = parameters.isNotEmpty()
internal val hasAdditionalParameters = requiredParameter.isNotEmpty() || namedParameter.isNotEmpty()

internal val isPrivate = builder.specData.modifiers.remove(DartModifier.PRIVATE)
internal val typeCast = builder.typeCast
internal val asSetter = builder.setter
Expand All @@ -40,12 +50,6 @@ class FunctionSpec(
internal val docs = builder.docs
internal val hasDocs = builder.docs.isNotEmpty()

private val namedParameters: Set<ParameterSpec> = if (parameters.isEmpty()) {
setOf()
} else {
parameters.filter { it.isNamed }.toSet()
}

init {
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 All @@ -58,6 +62,10 @@ class FunctionSpec(
throw IllegalArgumentException("Lambda can only be used with a body")
}

if (requiredParameter.isNotEmpty() && parametersWithDefaults.isNotEmpty()) {
throw IllegalArgumentException("A function can't have required and optional parameters")
}

//require (isFactory && returnType == null && !isNullable) { "A void function can't be nullable" }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import net.theevilreaper.dartpoet.code.writer.TypeDefWriter
import net.theevilreaper.dartpoet.type.ClassName
import net.theevilreaper.dartpoet.type.TypeName
import net.theevilreaper.dartpoet.type.asTypeName
import net.theevilreaper.dartpoet.util.ParameterFilter
import net.theevilreaper.dartpoet.util.toImmutableList
import kotlin.reflect.KClass

Expand All @@ -24,7 +25,15 @@ class TypeDefSpec(
internal val typeCasts = builder.typeCasts
internal val returnType = builder.returnType ?: Void::class.asTypeName()
internal val parameters = builder.parameters.toImmutableList()
internal val hasParameters = parameters.isNotEmpty()
internal val parametersWithDefaults =
ParameterFilter.filterParameter(parameters) { !it.isRequired && it.hasInitializer }
internal val requiredParameter =
ParameterFilter.filterParameter(parameters) { it.isRequired && !it.isNamed && !it.hasInitializer }
internal val namedParameter = ParameterFilter.filterParameter(parameters) { it.isNamed }
internal val normalParameter =
parameters.minus(parametersWithDefaults).minus(requiredParameter).minus(namedParameter).toImmutableList()
internal val hasAdditionalParameters = requiredParameter.isNotEmpty() || namedParameter.isNotEmpty()
internal val hasParameters = normalParameter.isNotEmpty()

/**
* Performs some checks to avoid invalid data.
Expand Down
Loading

0 comments on commit 5026264

Please sign in to comment.