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

How to print curly brace if it doesn't fit? #628

Open
steinybot opened this issue Jun 6, 2024 · 5 comments
Open

How to print curly brace if it doesn't fit? #628

steinybot opened this issue Jun 6, 2024 · 5 comments

Comments

@steinybot
Copy link

I would like to do:

def foo = bar

if it fits, otherwise do:

def foo = {
  bar
}

Is this possible?

Seems like I need FlatAlt("{" +: Doc.hardLine, doc) but there is no way to create a custom FlatAlt.

@steinybot
Copy link
Author

Ohhh I can use Doc.fill.

Doc.fill("{" +: Doc.line, Seq(Doc.text("def foo ="), Doc.text("bar")))

Man this stuff takes me ages to get my head around. I've literally been staring at it for hours.

@steinybot steinybot reopened this Jun 6, 2024
@steinybot
Copy link
Author

Wait no I'm being dumb. That will always add the {. Damn it.

@steinybot
Copy link
Author

Well this does it although it uses private stuff. Is there a better way?

package org.typelevel.paiges

import org.typelevel.paiges.Doc.FlatAlt

object Docx {

    implicit class DocOps(val doc: Doc) extends AnyVal {

        def orEmpty: Doc =
            flatAlt(doc, Doc.empty)

        def bracketIfLong(left: Doc, leftSep: Doc, rightSep: Doc, right: Doc, indent: Int = 2): Doc =
            left + (((leftSep + Doc.hardLine).orEmpty + doc).nested(indent) + (Doc.hardLine + rightSep).orEmpty + right).grouped
    }

    def bracketIfLong(left: Doc, leftSep: Doc, doc: Doc, rightSep: Doc, right: Doc, indent: Int = 2): Doc =
        doc.bracketIfLong(left, leftSep, rightSep, right, indent)

    def flatAlt(default: Doc, whenFlat: Doc): Doc =
        if (default == whenFlat) default
        else FlatAlt(default, whenFlat)
}
import org.scalatest.freespec.AnyFreeSpec
import org.typelevel.paiges.Doc
import org.typelevel.paiges.Docx

class MethodBuilderTest extends AnyFreeSpec {

    "MethodBuilder" - {
        "should add {} when the body is too long" in {
            val sig = Doc.text("def foo = ")
            val body = Doc.text("howlongcanyougo")
            val doc = Docx.bracketIfLong(sig, Doc.char('{'), body, Doc.char('}'), Doc.empty)
            val result = doc.render(24)
            val expected =
                """def foo = {
                  |  howlongcanyougo
                  |}""".stripMargin
            assert(result == expected)
        }
        "should not add {} when the body fits" in {
            val sig = Doc.text("def foo = ")
            val body = Doc.text("howlongcanyougo")
            val doc = Docx.bracketIfLong(sig, Doc.char('{'), body, Doc.char('}'), Doc.empty)
            val result = doc.render(25)
            val expected = """def foo = howlongcanyougo""".stripMargin
            assert(result == expected)
        }
    }
}

@johnynek
Copy link
Collaborator

johnynek commented Jun 6, 2024

This is a good solution.

Flatly was not directly exposed because it can break the invariants. It only makes sense for certain pairs of Docs. For instance if you flatten the resulting Doc should have lines as long or longer, not shorter.

That said, I think you .orEmpty combinator is safe and doesn't break invariants.

However, composing two such as you have done should only be done with a .grouped (the whole thing flattens or not at all) so a comment should be made around the method.

If you would make a PR I'd be happy to review and I think we could merge it.

@steinybot
Copy link
Author

Flatly was not directly exposed because it can break the invariants. It only makes sense for certain pairs of Docs. For instance if you flatten the resulting Doc should have lines as long or longer, not shorter.

orEmpty in general would break the invariant width(default) <= width(whenFlat) wouldn't it?

So then I think bracketIfLong would be safe to use orEmpty if and only if width(leftSep) <= width(doc).

However, composing two such as you have done should only be done with a .grouped (the whole thing flattens or not at all) so a comment should be made around the method.
Sorry, what should be grouped?

Are you suggesting a PR for just orEmpty or bracketIfLong too?

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

No branches or pull requests

2 participants