Skip to content

Commit

Permalink
Introduce the new ♢ modifier and don't wrap spaces by default (#2001)
Browse files Browse the repository at this point in the history
* Add diamond symbol to docs

* Get all tests to pass

* Replace usages of · with space

* Ignore unsafe wraps
  • Loading branch information
Egorand authored Oct 14, 2024
1 parent b84c462 commit 591a4a3
Show file tree
Hide file tree
Showing 21 changed files with 95 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public fun KmPackage.toFileSpec(
.build()
}

private const val NOT_IMPLEMENTED = "throw·NotImplementedError(\"Stub!\")"
private const val NOT_IMPLEMENTED = "throw NotImplementedError(\"Stub!\")"

private fun KmClass.toTypeSpec(
classInspector: ClassInspector?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ import kotlin.reflect.KClass
* importable, e.g. it's a top-level function or a property declared inside an object, the import
* will be resolved if possible. Arguments for members must be of type [MemberName].
* * `%%` emits a percent sign.
* * `·` emits a space that never wraps. KotlinPoet prefers to wrap lines longer than 100 columns.
* It does this by replacing normal spaces with a newline and indent. Note that spaces in strings
* are never wrapped.
* * `♢` emits a space or a newline, depending on its position on the line. This prefers to wrap
* lines before 100 columns. It does this by replacing normal spaces with a newline and indent.
* Note that spaces in strings are never wrapped.
* * `·` emits a space that never wraps. Equivalent to the regular space (` `).
* * `⇥` increases the indentation level.
* * `⇤` decreases the indentation level.
* * `«` begins a statement. For multiline statements, every line after the first line is
Expand Down Expand Up @@ -430,7 +431,7 @@ public class CodeBlock private constructor(
break
}
}
return "$this·{\n"
return "$this {\n"
}

/**
Expand All @@ -439,7 +440,7 @@ public class CodeBlock private constructor(
*/
public fun nextControlFlow(controlFlow: String, vararg args: Any?): Builder = apply {
unindent()
add("}·$controlFlow·{\n", *args)
add("} $controlFlow {\n", *args)
indent()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ internal class CodeWriter(
if (!first) {
if ((kdoc || comment) && trailingNewline) {
emitIndentation()
out.appendNonWrapping(if (kdoc) " *" else "//")
out.append(if (kdoc) " *" else "//")
}
out.newline()
trailingNewline = true
Expand All @@ -606,9 +606,9 @@ internal class CodeWriter(
if (trailingNewline) {
emitIndentation()
if (kdoc) {
out.appendNonWrapping(" * ")
out.append(" * ")
} else if (comment) {
out.appendNonWrapping("// ")
out.append("// ")
}
}

Expand All @@ -627,7 +627,7 @@ internal class CodeWriter(

private fun emitIndentation() {
for (j in 0..<indentLevel) {
out.appendNonWrapping(indent)
out.append(indent)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public class FileSpec private constructor(
val escapedPackageName = packageName.escapeSegmentsIfNecessary()

if (escapedPackageName.isNotEmpty()) {
codeWriter.emitCode("package·%L\n", escapedPackageName)
codeWriter.emitCode("package %L\n", escapedPackageName)
codeWriter.emit("\n")
}

Expand All @@ -186,7 +186,7 @@ public class FileSpec private constructor(

if (imports.isNotEmpty()) {
for (import in imports) {
codeWriter.emitCode("import·%L", import)
codeWriter.emitCode("import %L", import)
codeWriter.emit("\n")
}
codeWriter.emit("\n")
Expand Down Expand Up @@ -290,7 +290,7 @@ public class FileSpec private constructor(

/** Adds a file-site comment. This is prefixed to the start of the file and different from [addBodyComment]. */
public fun addFileComment(format: String, vararg args: Any): Builder = apply {
comment.add(format.replace(' ', '·'), *args)
comment.add(format, *args)
}

@Deprecated(
Expand Down Expand Up @@ -480,7 +480,7 @@ public class FileSpec private constructor(
check(isScript) {
"addBodyComment() is only allowed in script files"
}
body.add("//·${format.replace(' ', '·')}\n", *args)
body.add("// $format\n", *args)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public class FunSpec private constructor(
codeWriter.emitModifiers(modifiers, implicitModifiers)

if (!isConstructor && !name.isAccessor) {
codeWriter.emitCode("fun·")
codeWriter.emitCode("fun ")
}

if (typeVariables.isNotEmpty()) {
Expand All @@ -120,7 +120,7 @@ public class FunSpec private constructor(
if (asExpressionBody != null) {
codeWriter.emitCode(CodeBlock.of(" = %L", asExpressionBody), ensureTrailingNewline = true)
} else if (!isEmptySetter) {
codeWriter.emitCode("·{\n")
codeWriter.emitCode(" {\n")
codeWriter.indent()
codeWriter.emitCode(body.returnsWithoutLinebreak(), ensureTrailingNewline = true)
codeWriter.unindent()
Expand Down Expand Up @@ -505,7 +505,7 @@ public class FunSpec private constructor(
}

public fun addComment(format: String, vararg args: Any): Builder = apply {
body.add("//·${format.replace(' ', '·')}\n", *args)
body.add("// $format\n", *args)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public data class Import internal constructor(
private val importString = buildString {
append(qualifiedName.escapeSegmentsIfNecessary())
if (alias != null) {
append("·as·${alias.escapeIfNecessary()}")
append(" as ${alias.escapeIfNecessary()}")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public class LambdaTypeName private constructor(
}

if (isSuspending) {
out.emit("suspend·")
out.emit("suspend ")
}

out.emitContextReceivers(contextReceivers, suffix = "·")
out.emitContextReceivers(contextReceivers, suffix = " ")

receiver?.let {
if (it.isAnnotated) {
Expand All @@ -76,7 +76,7 @@ public class LambdaTypeName private constructor(
}

parameters.emit(out)
out.emitCode(if (returnType is LambdaTypeName) "·->·(%T)" else "·->·%T", returnType)
out.emitCode(if (returnType is LambdaTypeName) " -> (%T)" else " -> %T", returnType)

if (isNullable) {
out.emit(")")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ internal class LineWrapper(
var pos = 0
while (pos < s.length) {
when (s[pos]) {
' ' -> {
// Each space starts a new empty segment.
'' -> {
// Each wrapping space starts a new empty segment.
this.indentLevel = indentLevel
this.linePrefix = linePrefix
segments += ""
Expand All @@ -66,7 +66,7 @@ internal class LineWrapper(
pos++
}

'·' -> {
' ', '·' -> {
// Render · as a non-breaking space.
segments[segments.size - 1] += " "
pos++
Expand Down Expand Up @@ -105,8 +105,6 @@ internal class LineWrapper(
}

private fun emitCurrentLine() {
foldUnsafeBreaks()

var start = 0
var columnCount = segments[0].length

Expand Down Expand Up @@ -150,26 +148,7 @@ internal class LineWrapper(
}
}

/**
* Any segment that starts with '+' or '-' can't have a break preceding it. Combine it with the
* preceding segment. Note that this doesn't apply to the first segment.
*/
private fun foldUnsafeBreaks() {
var i = 1
while (i < segments.size) {
val segment = segments[i]
if (UNSAFE_LINE_START.matches(segment)) {
segments[i - 1] = segments[i - 1] + " " + segments[i]
segments.removeAt(i)
if (i > 1) i--
} else {
i++
}
}
}

companion object {
private val UNSAFE_LINE_START = Regex("\\s*[-+].*")
private val SPECIAL_CHARACTERS = " \n·".toCharArray()
private val SPECIAL_CHARACTERS = " \n·♢".toCharArray()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class ParameterSpec private constructor(
codeWriter.emitAnnotations(annotations, inlineAnnotations)
codeWriter.emitModifiers(modifiers)
if (name.isNotEmpty()) codeWriter.emitCode("%N", this)
if (name.isNotEmpty() && includeType) codeWriter.emitCode(":·")
if (name.isNotEmpty() && includeType) codeWriter.emitCode(": ")
if (includeType) codeWriter.emitCode("%T", type)
emitDefaultValue(codeWriter)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public class ParameterizedTypeName internal constructor(
if (typeArguments.isNotEmpty()) {
out.emit("<")
typeArguments.forEachIndexed { index, parameter ->
if (index > 0) out.emit(",·")
if (index > 0) out.emit(", ")
parameter.emitAnnotations(out)
parameter.emit(out)
parameter.emitNullable(out)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ public class PropertySpec private constructor(
codeWriter.emitContextReceivers(contextReceiverTypes, suffix = "\n")
codeWriter.emitAnnotations(annotations, inlineAnnotations)
codeWriter.emitModifiers(propertyModifiers, implicitModifiers)
codeWriter.emitCode(if (mutable) "var·" else "val·")
codeWriter.emitCode(if (mutable) "var " else "val ")
if (typeVariables.isNotEmpty()) {
codeWriter.emitTypeVariables(typeVariables)
codeWriter.emit(" ")
codeWriter.emit("")
}
if (receiverType != null) {
if (receiverType is LambdaTypeName) {
Expand All @@ -91,12 +91,12 @@ public class PropertySpec private constructor(
codeWriter.emitCode("%T.", receiverType)
}
}
codeWriter.emitCode("%N: %T", this, type)
codeWriter.emitCode("%N:%T", this, type)
if (withInitializer && initializer != null) {
if (delegated) {
codeWriter.emit(" by ")
codeWriter.emit("♢by♢")
} else {
codeWriter.emitCode(" = ")
codeWriter.emitCode("♢=♢")
}
val initializerFormat = if (initializer.hasStatements()) "%L" else "«%L»"
codeWriter.emitCode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public class TypeSpec private constructor(
}

if (superTypes.isNotEmpty()) {
val separator = if (wrapSupertypes) ",\n " else ", "
val separator = if (wrapSupertypes) ",\n " else ","
codeWriter.emitCode(superTypes.joinToCode(separator = separator, prefix = " : "))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ private fun String.escapeIfNotJavaIdentifier(): String {
) &&
!alreadyEscaped()
) {
"`$this`".replace(' ', '·')
"`$this`"
} else {
this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public class WildcardTypeName private constructor(

override fun emit(out: CodeWriter): CodeWriter {
return when {
inTypes.size == 1 -> out.emitCode("in·%T", inTypes[0])
inTypes.size == 1 -> out.emitCode("in %T", inTypes[0])
outTypes == STAR.outTypes -> out.emit("*")
else -> out.emitCode("out·%T", outTypes[0])
else -> out.emitCode("out %T", outTypes[0])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,10 +562,8 @@ class CodeBlockTest {
|package com.squareup.tacos
|
|public fun test() {
| if ("Very long string that would wrap the line " ==
| "Very long string that would wrap the line ") {
| } else if ("Long string that would wrap the line 2 " ==
| "Long string that would wrap the line 2 ") {
| if ("Very long string that would wrap the line " == "Very long string that would wrap the line ") {
| } else if ("Long string that would wrap the line 2 " == "Long string that would wrap the line 2 ") {
| }
|}
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1016,8 +1016,7 @@ class KotlinPoetTest {
|
|import kotlin.String
|
|public
| fun functionWithAPrettyLongNameThatWouldCauseWrapping(parameterWithALongNameThatWouldAlsoCauseWrapping: String) {
|public fun functionWithAPrettyLongNameThatWouldCauseWrapping(parameterWithALongNameThatWouldAlsoCauseWrapping: String) {
|}
|
""".trimMargin(),
Expand Down Expand Up @@ -1103,13 +1102,11 @@ class KotlinPoetTest {
|import kotlin.String
|
|/**
| * Builder class for Foo. Allows creating instances of Foo by initializing a subset of their fields,
| * following the Builder pattern.
| * Builder class for Foo. Allows creating instances of Foo by initializing a subset of their fields, following the Builder pattern.
| */
|public class Builder {
| /**
| * The description for the choice, e.g. "Currently unavailable due to high demand. Please try
| * later." May be null.
| * The description for the choice, e.g. "Currently unavailable due to high demand. Please try later." May be null.
| */
| public fun summary_text(summary_text: String?): Builder {
| this.summary_text = summary_text
Expand Down
Loading

0 comments on commit 591a4a3

Please sign in to comment.