Scala で markdown パースするだけではなく、出力を少しいじる
前回 knockoff なるライブラリを紹介したのですが、ちょこっと内容を変更してみます。 knockoff でパースした型というのは
def knockoff( source : java.lang.CharSequence ) : Seq[Block] = {
なんて定義になっており、要は Block と呼ばれる解析データのシーケンスであると。 であれば、その型が分かれば必然的に出力結果を弄れるわけで、ソースを追いかける。
trait Block { def position : Position } case class Paragraph( spans : Seq[Span], position : Position ) extends Block case class Header( level : Int, spans : Seq[Span], position : Position ) extends Block case class LinkDefinition( id : String, url : String, title : Option[String], position : Position ) extends Block case class Blockquote( children : Seq[Block], position : Position ) extends Block case class CodeBlock( text : Text, position : Position ) extends Block case class HorizontalRule( position : Position ) extends Block case class OrderedItem( children : Seq[Block], position : Position ) extends Block case class UnorderedItem( children : Seq[Block], position : Position ) extends Block case class HTMLBlock( html: String, position: Position ) extends Block case class OrderedList( items : Seq[OrderedItem] ) extends Block { lazy val position = if ( items.isEmpty ) NoPosition else items.head.position } case class UnorderedList( items : Seq[UnorderedItem] ) extends Block { lazy val position = if ( items.isEmpty ) NoPosition else items.head.position }
おぅふ、そのままですね。 ということで、Header を事前に全て引っこ抜いて、索引を作りたいと思ったら
import java.io.PrintWriter import scala.io.{Codec, Source} import com.tristanhunt.knockoff.DefaultDiscounter._ import com.tristanhunt.knockoff._ import scala.util.parsing.input.{NoPosition, Position} object Sample extends App { // ベースとなるデータ val source = Source.fromFile("sample.txt")(Codec.UTF8) var content = knockoff(source.getLines().mkString("\n")) // Header 定義をリンクに変換する var counter = 0 def mkLi(num:Int, h:Header) = { val Header(l, s, p) = h """<li><a href="#%d">%s</a></li>""".format(num, s.head.asInstanceOf[Text].content) } // Header を抜き出して、リンクのリストに変換する val headers = content.filter(_.isInstanceOf[Header]).map(_.asInstanceOf[Header]) .zipWithIndex.map { case (header, num) => mkLi(num + 1, header) } val headerList = HTMLBlock("""<ul>%s</ul>""".format(headers.mkString), NoPosition) // Header 内のテキストに id を付与する def convertLinkSpan(s:Span) = { s match { case Text(content) => { counter += 1 HTMLSpan("""<span id="%d">%s</span>""".format(counter, content)) } case s => s } } // ヘッダに id を振る content = content.map(v => { v match { case Header(l, s, p) => Header(l, s.map(convertLinkSpan), p) case v => v } }) val converted = new PrintWriter("output.html") converted.println("<html><body>") // リンク converted.println(toXHTML(headerList +: content)) converted.println("</body></html>") converted.flush() converted.close() }
てな感じで書くと
<html><body> <ul><li><a href="#1">PlayFramework をインストールする</a></li><li><a href="#2">Playプロジェクトを作成する</a></li></ul><h1><span id="1">PlayFramework をインストールする</span></h1><p>Playframework 2.1.1 をインストールする手順です </p><ol><li>Java6 以上を入れます(この詳細はJavaの物を探してください) </li><li><a href="http://www.playframework.com/:title">Play公式</a>から2.1.1 をダウンロードしてきます </li><li>適当なディレクトリへ解凍し、パスを通します </li></ol><p>基本的にはこれだけです。 </p><pre><code>wget http://downloads.typesafe.com/play/2.1.1/play-2.1.1.zip unzip play-2.1.1.zip sudo mv play-2.1.1 /usr/local/ ... (以下略)