scalatags icon indicating copy to clipboard operation
scalatags copied to clipboard

Native pretty-print required

Open ghost opened this issue 10 years ago • 14 comments

scala.xml.PrettyPrinter does not generate validate W3 html markup language, and alters the orginal placements of tags.

For example script(src:=""/assets/jquery.min.js"") generates < script src="/assets/jquery.min.js"></script> and is converted to < script src="/assets/jquery.min.js"/>

When in development it is useful to see the structure of the HTML, however by using scala.xml.PrettyPrinter as described in the readme.md the output is changed and becomes invalid.

Would it be possible to create a basic pretty-print within scalatags itself, I don't think you would need to wrap long lines of text, but showing the correct indentation would be enough.

ghost avatar Mar 13 '15 09:03 ghost

Ahh, yes, I ran into this as well. At a minimum, this should perhaps be mentioned in the docs.

stewSquared avatar Apr 07 '15 21:04 stewSquared

I messed around with this today:

scala> import scalatags.PrettyText.all._
import scalatags.PrettyText.all._

scala> val frag = html(
     |   head(
     |     script("some script")
     |   ),
     |   body(
     |     h1("This is my title"),
     |     div(
     |       p("This is my first paragraph"),
     |       p("This is my second paragraph")
     |     )
     |   )
     | )
frag: scalatags.PrettyText.TypedTag[String] = 
<html>
  <head>
    <script>
      some script
    </script>
  </head>
  <body>
    <h1>
      This is my title
    </h1>
    <div>
      <p>
        This is my first paragraph
      </p>
      <p>
        This is my second paragraph
      </p>
    </div>
  </body>
</html>

Basically PrettyText is a cloned Text backend with prettier output. It's just a newline per tag and two spaces indent. I had to add a depth parameter to Frag.writeTo (strb, depth) but otherwise core scalatags is unchanged. I could perhaps make the additional parameter exclusive to PrettyText with some more effort.

What do you think? You've had a few spurious issues caused by pretty printing using scala.xml.

marklister avatar Apr 08 '15 19:04 marklister

For my use case this is perfect.

I just need to see how the HTML is visually rendered (when using complex functions that generate HTML). The above indentation method allows me to do this quickly. I will only use this when in development and switch to the standard Text backend for production.

Regarding the technical aspects of the implementation, if PrettyText is a lot of cloned code this might make maintenance of the code more effort going forward. As both PrettyText and Text might potentially need to be changed. Not sure if PrettyText functionality could be added as a method of Text to reduce any potential code duplication ( caveat... I've not looked at the code in detail )

Thanks again.

ghost avatar Apr 09 '15 11:04 ghost

I'll make a fork and push the branch when my power comes back on... It's certainly not slick but for 100 loc or so , mostly copied, it's a decent result.

marklister avatar Apr 09 '15 12:04 marklister

200 loc or so actually, and I was hoping @lihaoyi would weigh in before I put more effort into this...

marklister avatar Apr 09 '15 12:04 marklister

Sorry, will take a look On Apr 9, 2015 05:13, "mark lister" [email protected] wrote:

200 loc or so actually, and I was hoping @lihaoyi https://github.com/lihaoyi would weigh in before I put more effort into this...

— Reply to this email directly or view it on GitHub https://github.com/lihaoyi/scalatags/issues/76#issuecomment-91211959.

lihaoyi avatar Apr 09 '15 18:04 lihaoyi

My power just came back on but I'd been out drinking in the interrum so my skills are a little impaired :)

Anyway just forked and pushed branch prettytext. Not a pull request as it's pretty rough, just for discussion really.

marklister avatar Apr 09 '15 20:04 marklister

I had much better idea which I'll implement soon. Sorry about the noise.

marklister avatar Apr 10 '15 17:04 marklister

branch prettytext2

It provides an implict class on a Text.TypedTag which has basic structured output. Totally non-invasive to scalatags and controlled by an import. First try and again, for discussion.

Welcome to Scala version 2.11.4 (OpenJDK Server VM, Java 1.7.0_75).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scalatags.Text.all._
import scalatags.Text.all._

scala> import scalatags.text.Pretty._
import scalatags.text.Pretty._

scala> div(
     |   div(id:="ch1Panel", role:="tabpanel", aria.labelledby:="ch1Tab")(
     |     "Chapter 1 content goes here"
     |   ),
     |   div(id:="ch2Panel", role:="tabpanel", aria.labelledby:="ch2Tab")(
     |     "Chapter 2 content goes here"
     |   ),
     |   div(id:="quizPanel", role:="tabpanel", aria.labelledby:="quizTab")(
     |     "Quiz content goes here"
     |   )
     | )
res1: scalatags.Text.TypedTag[String] = <div><div id="ch1Panel" role="tabpanel" aria-labelledby="ch1Tab">Chapter 1 content goes here</div><div id="ch2Panel" role="tabpanel" aria-labelledby="ch2Tab">Chapter 2 content goes here</div><div id="quizPanel" role="tabpanel" aria-labelledby="quizTab">Quiz content goes here</div></div>

scala> res1.pretty
res2: scalatags.text.Pretty.PrettyFrag =
<div>
  <div id="ch1Panel" role="tabpanel" aria-labelledby="ch1Tab">
    Chapter 1 content goes here
  </div>
  <div id="ch2Panel" role="tabpanel" aria-labelledby="ch2Tab">
    Chapter 2 content goes here
  </div>
  <div id="quizPanel" role="tabpanel" aria-labelledby="quizTab">
    Quiz content goes here
  </div>
</div>

marklister avatar Apr 11 '15 14:04 marklister

Want to send a PR? This sounds pretty self-contained and uncontroversial

lihaoyi avatar Apr 15 '15 01:04 lihaoyi

@lihaoyi you can take a look, it's PR #80....

marklister avatar Apr 15 '15 06:04 marklister

Native pretty print is a good choice.

Meanwhile if you need html pretty print, try HtmlCleaner: http://htmlcleaner.sourceforge.net/

One might say that it is overkill, but it works.

Example:

val h = html(
    head(
      script("some script"),
      script(src:="/assets/jquery.min.js")
    ),
    body(
      h1("This is my title"),
      div(
          h1(backgroundColor:="blue", color:="red")("This is my title"),
          div(backgroundColor:="blue", color:="red")(
            p(cls := "contentpara first")(
              "This is my first paragraph"
            ),
            a(opacity:=0.9)(
              p(cls := "contentpara")("Goooogle")
            )
          ),
          "A B C",
        p("Hello World!!!"),
        p("I am ScalaTags!!!"),
        p("onclick".attr:="... do some js")(
          "This is my first paragraph"
        ),
        a("href".attr:="http://www.google.com")(
          p("Goooogle")
        )            
      )
    )
  )

  logger.debug("html = \n" + "<!DOCTYPE html>" + h.render)

  import org.htmlcleaner

  // create an instance of HtmlCleaner
  val cleaner = new org.htmlcleaner.HtmlCleaner();      

  // take default cleaner properties
  val props = cleaner.getProperties();

  val tag = cleaner.clean("<!DOCTYPE html>" + h.render)

  val pretty = new PrettyHtmlSerializer(props, "  ")
  logger.debug("html = \n" + pretty.getAsString(tag))

This will produce:

13:42:24.243 [run-main-1] DEBUG Main$ - html = 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html>
  <head>
    <script>some script</script>
    <script src="/assets/jquery.min.js"></script>
  </head>
  <body>
    <h1>This is my title</h1>
    <div>
      <h1 style="background-color: blue;color: red;">This is my title</h1>
      <div style="background-color: blue;color: red;">
        <p class="contentpara first">This is my first paragraph</p>
        <a style="opacity: 0.9;">
          <p class="contentpara">Goooogle</p>
        </a>
      </div>A B C
      <p>Hello World!!!</p>
      <p>I am ScalaTags!!!</p>
      <p onclick="... do some js">This is my first paragraph</p>
      <a href="http://www.google.com">
        <p>Goooogle</p>
      </a>
    </div>
  </body>
</html>

jk-1 avatar Oct 10 '15 11:10 jk-1

@lihaoyi, please merge

Andrei-Pozolotin avatar Nov 06 '17 14:11 Andrei-Pozolotin