1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
package dotty.tools
package dottydoc
package staticsite
import dotc.config.Printers.dottydoc
import com.vladsch.flexmark.html.HtmlRenderer
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.ext.front.matter.AbstractYamlFrontMatterVisitor
import java.util.{ Map => JMap, List => JList }
case class IllegalFrontMatter(message: String) extends Exception(message)
trait Page {
import scala.collection.JavaConverters._
def includes: Map[String, String]
def pageContent: String
def params: Map[String, AnyRef]
def yaml: Map[String, AnyRef] = {
if (_yaml eq null) initFields()
_yaml
}
def html: String = {
if (_html eq null) initFields()
_html
}
def firstParagraph: String = {
if (_html eq null) initFields()
val sb = new StringBuilder
var pos = 0
// to handle nested paragraphs in non markdown code
var open = 0
while (pos < _html.length - 4) {
val str = _html.substring(pos, pos + 4)
val lstr = str.toLowerCase
sb append str.head
pos += 1
if (lstr.contains("<p>"))
open += 1
else if (lstr == "</p>") {
open -= 1
if (open == 0) {
pos = Int.MaxValue
sb append "/p>"
}
}
}
sb.toString
}
protected[this] var _yaml: Map[String, AnyRef /* String | JList[String] */] = _
protected[this] var _html: String = _
protected[this] def initFields() = {
val md = Parser.builder(Site.markdownOptions).build.parse(pageContent)
val yamlCollector = new AbstractYamlFrontMatterVisitor()
yamlCollector.visit(md)
_yaml = updatedYaml {
yamlCollector
.getData().asScala
.mapValues {
case xs if xs.size == 1 =>
val str = xs.get(0)
if (str.length > 0 && str.head == '"' && str.last == '"')
str.substring(1, str.length - 1)
else str
case xs => xs
}
.toMap
}
// YAML must start with "---" and end in either "---" or "..."
val withoutYaml =
if (pageContent.startsWith("---\n")) {
val str =
pageContent.lines
.drop(1)
.dropWhile(line => line != "---" && line != "...")
.drop(1).mkString("\n")
if (str.isEmpty) throw IllegalFrontMatter(pageContent)
else str
}
else pageContent
// make accessible via "{{ page.title }}" in templates
val page = Map("page" -> _yaml.asJava)
_html = LiquidTemplate(withoutYaml).render(params ++ page, includes)
}
/** Takes "page" from `params` map in case this is a second expansion, and
* removes "layout" from the parameters if it exists. We don't want to
* preserve the layout from the previously expanded template
*/
private def updatedYaml(newYaml: Map[String, AnyRef]): Map[String, AnyRef] =
params
.get("page")
.flatMap {
case page: Map[String, AnyRef] @unchecked =>
Some(page - "layout" ++ newYaml)
case _ => None
}
.getOrElse(newYaml)
}
class HtmlPage(fileContents: => String, val params: Map[String, AnyRef], val includes: Map[String, String]) extends Page {
lazy val pageContent = fileContents
}
class MarkdownPage(fileContents: => String, val params: Map[String, AnyRef], val includes: Map[String, String]) extends Page {
lazy val pageContent = fileContents
override protected[this] def initFields() = {
super.initFields()
val md = Parser.builder(Site.markdownOptions).build.parse(_html)
// fix markdown linking
MarkdownLinkVisitor(md)
_html = HtmlRenderer
.builder(Site.markdownOptions)
.escapeHtml(false)
.build()
.render(md)
}
}
|