Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composition vs. newlines #1754

Open
fejesjoco opened this issue Dec 2, 2023 · 0 comments
Open

Composition vs. newlines #1754

fejesjoco opened this issue Dec 2, 2023 · 0 comments

Comments

@fejesjoco
Copy link
Contributor

This is just a cosmetic issue but somehow bothering me a lot.

CodeBlock's can be built in two ways: builder methods like addStatement() and beginControlFlow(), and strings in add(). When we later compose these CodeBlock's, the behavior of newlines can be surprising.

Consider the following code:

  val smallS = buildCodeBlock {
    add(
      """
        stmt1()
        stmt2()
      """.trimIndent()
    )
  }
  val smallB = buildCodeBlock {
    addStatement("stmt1()")
    addStatement("stmt2()")
  }

  val compositionStoS = buildCodeBlock {
    add(
      """
        if (whatever) {
          ⇥%L⇤
        }
      """.trimIndent(),
      smallS
    )
  }
  val compositionStoB = buildCodeBlock {
    beginControlFlow("if (whatever)")
    add(smallS)
    endControlFlow()
  }
  val compositionBtoS = buildCodeBlock {
    add(
      """
        if (whatever) {
          ⇥%L⇤
        }
      """.trimIndent(),
      smallB
    )
  }
  val compositionBtoB = buildCodeBlock {
    beginControlFlow("if (whatever)")
    add(smallB)
    endControlFlow()
  }

  FileSpec.builder("com.test", "HelloWorld")
    .addType(
      TypeSpec.classBuilder("HelloWorld")
        .addFunction(FunSpec.builder("helloStoS").addCode(compositionStoS).build())
        .addFunction(FunSpec.builder("helloStoB").addCode(compositionStoB).build())
        .addFunction(FunSpec.builder("helloBtoS").addCode(compositionBtoS).build())
        .addFunction(FunSpec.builder("helloBtoB").addCode(compositionBtoB).build())
        .build()
    )
    .build()
    .writeTo(System.out)

This prints:

package com.test

public class HelloWorld {
  public fun helloStoS() {
    if (whatever) {
      stmt1()
      stmt2()
    }
  }

  public fun helloStoB() {
    if (whatever) {
      stmt1()
      stmt2()}
  }

  public fun helloBtoS() {
    if (whatever) {
      stmt1()
      stmt2()

    }
  }

  public fun helloBtoB() {
    if (whatever) {
      stmt1()
      stmt2()
    }
  }
}

The first and last case looks good, the second case misses a newline, and the third case has an extra newline.

The reason for that is simple. In the small blocks, when built from strings (smallS), they don't end with newlines (but we can add them if we want), and with the builder methods (smallB) a newline is added automatically (and can't easily be taken away). In the composed blocks it's actually the opposite, if built from strings, then a newline is part of the format strings (and can't easily be taken away), but with the add builder method the newline is not printed (we can add it manually if needed).

The bigger of the two issues is newlines that are added but can't be taken away. I'm okay with doing + "\n" where applicable.

I think a reasonable solution would be if small code blocks never had a trailing newline (so smallB would generate the same as smallS), and then during composition the developer could choose to add a newline between elements or not. There could be more sophisticated solutions, like automatically adding newlines, possibly by modelling if a CodeBlock forms a standalone statement (always renders with newlines around it) or a non-self-contained piece of code (that is always glued without separators to its surroundings), or automatically collapsing newlines around composition boundaries, etc., but that might be overkill. This was easier with Java where every had to end with ; or { or } so there was no question about being self-contained or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants