Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add support of <code> blocks inside of <pre> blocks, implement bc. bl…

…ocks
  • Loading branch information...
commit a1c224748d3374fe33360c93d4ad3a28db078e44 1 parent b23601f
Roman Kashitsyn roman-kashitsyn authored
50 src/main/scala/net/liftmodules/textile/TextileParser.scala
@@ -441,15 +441,20 @@ object TextileParser {
441 441 */
442 442 lazy val preEndOfLine: Parser[Textile] = (('\r' ~ '\n') | '\n') ^^^ CharBlock("\n")
443 443
  444 + lazy val codeBlock : Parser[Textile] = accept("<code") ~> rep(tag_attr) ~ (rep(' ') ~> '>' ~>
  445 + rep(not(accept("</code")) ~> (preEndOfLine | charBlock))) <~ closingTag("code") ^^ {
  446 + case attrs ~ elms => HTML("code", reduceCharBlocks(elms), attrs)
  447 + }
444 448
445 449 /**
446   - * a &lt;pre&gt; block. Just send text of through, unmolested.
  450 + * a &lt;pre&gt; block. Try recognize nested &lt;code&gt; block or leave content untouched
447 451 */
448 452 lazy val preBlock: Parser[Textile] =
449   - beginlS ~> accept("<pre") ~> rep(' ') ~> '>' ~>
450   - rep(not(accept("</pre")) ~> (preEndOfLine | charBlock )) <~
451   - accept("</pre") <~ rep(' ') <~ '>' <~ rep(' ') <~ '\n' ^^ {
452   - case elms => Pre(reduceCharBlocks(elms), Nil)
  453 + beginlS ~> accept("<pre") ~> rep(tag_attr) ~ (rep(' ') ~> '>' ~>
  454 + (codeBlock | rep(not(accept("</pre")) ~> (preEndOfLine | charBlock)))) <~
  455 + closingTag("pre") <~ rep(' ') <~ '\n' ^^ {
  456 + case attrs ~ (code: HTML) => Pre(code :: Nil, attrs)
  457 + case attrs ~ (elms: List[Textile]) => Pre(reduceCharBlocks(elms), attrs)
453 458 }
454 459
455 460 /**
@@ -700,6 +705,12 @@ object TextileParser {
700 705 lazy val blockquote : Parser[Textile] = (beginl ~> accept("bq") ~> rep(para_attribute)) ~ (accept(". ") ~> rep1(not_blank_line) <~ blankLine) ^^ {
701 706 case attrs ~ ln => BlockQuote(reduceCharBlocks(ln), attrs)}
702 707
  708 + lazy val codeWithoutBlankLines : Parser[List[Textile]] = rep(rep1(charBlock) ~ preEndOfLine) ^^ {
  709 + lines => lines.flatMap { case chars ~ endl => reduceCharBlocks(chars :+ endl)}}
  710 +
  711 + lazy val blockCode : Parser[Textile] = (beginl ~> accept("bc") ~> rep(para_attribute)) ~ (accept(". ") ~> codeWithoutBlankLines <~ blankLine) ^^ {
  712 + case attrs ~ blocks => BlockCode(blocks, attrs)
  713 + }
703 714
704 715 lazy val footnote : Parser[Textile] = (beginl ~> accept("fn") ~> num) ~ rep(para_attribute) ~ (accept(". ") ~> rep1(not_blank_line) <~ blankLine) ^^ {
705 716 case dr ~ attrs ~ ln => Footnote(reduceCharBlocks(ln), attrs, dr)}
@@ -735,7 +746,7 @@ object TextileParser {
735 746 case td ~ el => TableElement(reduceCharBlocks(el), isHeader, td.map(_.attrs) getOrElse Nil)}
736 747
737 748 lazy val paragraph : Parser[Textile] =
738   - preBlock | footnote | table | bullet(0, false) | bullet(0, true) | blockquote | head_line | blankPara | normPara
  749 + preBlock | footnote | table | bullet(0, false) | bullet(0, true) | blockquote | blockCode | head_line | blankPara | normPara
739 750 }
740 751
741 752
@@ -965,17 +976,28 @@ object TextileParser {
965 976 }
966 977 }
967 978
968   - val validAttributes = List(/*"class", */ "title", /*"style", "dir",*/ "lang")
  979 + case class BlockCode(elems : List[Textile], attrs : List[Attribute]) extends ATextile(elems, attrs) {
  980 + override def toHtml : NodeSeq = {
  981 + val attributes = fromStyle(attrs)
  982 + val contents = flattenAndDropLastEOL(elems)
  983 + val code = XmlElem(null, "code", attributes, TopScope, contents : _*)
  984 + XmlElem(null, "pre", attributes, TopScope, code : _*) ++ Text("\n")
  985 + }
  986 + }
  987 +
  988 + private val validAttributes = Set("title", "lang")
  989 + private val attrExtensions = Map("code" -> Set("class"), "pre" -> Set("class"))
  990 + private def validHtmlAttr(tag: String, in: Attribute): Boolean = in match {
  991 + case AnyAttribute(name, _) => validAttributes.contains(name) || attrExtensions.contains(tag) && attrExtensions(tag).contains(name)
  992 + case _ => false
  993 + }
969 994
970 995 case class HTML(tag : String, elems : List[Textile], attrs : List[Attribute]) extends ATextile(elems, attrs) {
971   - def validAttr(in: Attribute): Boolean = in match {
972   - case AnyAttribute(name, _) => validAttributes.contains(name)
973   - case _ => false
  996 + override def toHtml : NodeSeq = {
  997 + XmlElem(null, tag, fromStyle(attrs.filter(validHtmlAttr(tag, _))), TopScope, flattenAndDropLastEOL(elems) : _*)
974 998 }
  999 + }
975 1000
976   - override def toHtml : NodeSeq = {
977   - XmlElem(null, tag, fromStyle(attrs.filter(validAttr)), TopScope, flattenAndDropLastEOL(elems) : _*)
978   - }}
979 1001 case class FootnoteDef(num : String) extends ATextile(null, Nil) {
980 1002 override def toHtml : NodeSeq = {
981 1003 XmlElem(null, "sup", Null, TopScope,
@@ -1071,7 +1093,7 @@ object TextileParser {
1071 1093 }
1072 1094
1073 1095 case class Pre(elems : List[Textile], attrs : List[Attribute]) extends ATextile(elems, attrs) {
1074   - override def toHtml : NodeSeq = XmlElem(null, "pre", fromStyle(attrs), TopScope, flattenAndDropLastEOL(elems) : _*) ++ Text("\n")
  1096 + override def toHtml : NodeSeq = XmlElem(null, "pre", fromStyle(attrs.filter(validHtmlAttr("pre", _))), TopScope, flattenAndDropLastEOL(elems) : _*) ++ Text("\n")
1075 1097 }
1076 1098
1077 1099 case class Anchor(elems : List[Textile], href : String, alt : String, attrs : List[Attribute], disableLinks: Boolean) extends ATextile(elems, attrs) {
41 src/test/scala/net/liftmodules/textile/TextileSpec.scala
@@ -66,6 +66,47 @@ class TextileSpec extends Specification {
66 66 toHtml("*foo* __bar__") must ==/(<p><strong>foo</strong> <i>bar</i></p> )
67 67 }
68 68
  69 + "deal with blockquotes" in {
  70 + toHtml("bq. foo bar") must ==/(<blockquote><p>foo bar</p></blockquote>)
  71 + }
  72 +
  73 + "not trait 'bq' characters without a dot as blockquote" in {
  74 + toHtml("bq bw be") must ==/(<p>bq bw be</p>)
  75 + }
  76 +
  77 + "deal with preformatted blocks" in {
  78 + toHtml("<pre>\n foo\n bar\n</pre>") must ==/(<pre>{"\n foo\n bar\n"}</pre>)
  79 + }
  80 +
  81 + "deal with preformatted blocks with attributes" in {
  82 + toHtml("<pre class='foo'>foo bar</pre>") must ==/(<pre class="foo">foo bar</pre>)
  83 + toHtml("<pre onclick='runXss()'>foo bar</pre>") must ==/(<pre>foo bar</pre>)
  84 + }
  85 +
  86 + "deal with short blocks of code" in {
  87 + val res = toHtml("bc. var a = 0\nvar b = a + 1\n\nvar c = 3")
  88 + res must ==/(<pre><code>{"var a = 0\nvar b = a + 1"}</code></pre><p>var c = 3</p>)
  89 + }
  90 +
  91 + "deal with short blocks of code with attributes" in {
  92 + val styledRes = toHtml("bc(haskell). main = getLine >> putStrLn")
  93 + styledRes must ==/(<pre class="haskell"><code class="haskell">main = getLine >> putStrLn</code></pre>)
  94 + }
  95 +
  96 + "deal with simple paragraphs and code blocks interchanged" in {
  97 + val res = toHtml("p. Para1\n\nbc. Code block\n\np. Para2")
  98 + res must ==/(<p>Para1</p><pre><code>Code block</code></pre><p>Para2</p>)
  99 + }
  100 +
  101 + "deal with code block embedded into pre block" in {
  102 + toHtml("<pre><code class=\"haskell\" >let x = 5</code></pre>") must ==/(<pre><code class="haskell">let x = 5</code></pre>)
  103 + toHtml("<pre><code>\nx = 5\n</code></pre>") must ==/(<pre><code>{"\nx = 5\n"}</code></pre>)
  104 + }
  105 +
  106 + "not trait 'bc' characters without a dot as block of code" in {
  107 + toHtml("bc bv bb") must ==/(<p>bc bv bb</p>)
  108 + }
  109 +
69 110 "attributes in link quotes" in {
70 111 val res = toHtml(""""(cms-woof) click here":# """)
71 112 res must ==/(<p><a href="javascript://" class="cms-woof"> click here</a> </p>)

0 comments on commit a1c2247

Please sign in to comment.
Something went wrong with that request. Please try again.