-
Notifications
You must be signed in to change notification settings - Fork 44
/
templates.scala
170 lines (137 loc) · 6.68 KB
/
templates.scala
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package laika.ast
import laika.internal.rewrite.ReferenceResolver.CursorKeys
import laika.parse.SourceCursor
/** The base type for all inline elements that
* can be found in a template.
*/
trait TemplateSpan extends Span
/** A container of other TemplateSpan elements.
*/
trait TemplateSpanContainer extends ElementContainer[TemplateSpan] with RewritableContainer {
type Self <: TemplateSpanContainer
/** Rewrites all template span children of this container based on the specified rules.
*
* Concrete types are expected to support rewriting at least for all standard block, span and template span
* elements they contain, plus optionally for any other elements that have custom support for rewriting.
*/
def rewriteTemplateSpans(rules: RewriteRule[TemplateSpan]): Self = rewriteChildren(
RewriteRules.forTemplates(rules)
)
def rewriteChildren(rules: RewriteRules): Self = withContent(rules.rewriteTemplateSpans(content))
/** Creates a copy of this instance with the specified new content.
*
* Implementation note: This method exists to deal with the fact that there is no polymorphic copy method
* and trades a small bit of boilerplate for avoiding the compile time hit of using shapeless for this.
*/
def withContent(newContent: Seq[TemplateSpan]): Self
}
/** Common methods for simple template span containers (without additional parameters). */
trait TemplateSpanContainerCompanion {
type ContainerType
/** Creates an empty instance */
def empty: ContainerType = createSpanContainer(Nil)
/** Create an instance only containing a single TemplateString span */
def apply(text: String, texts: String*): ContainerType = createSpanContainer(
(text +: texts).map(TemplateString(_))
)
/** Create an instance containing a one or more spans */
def apply(span: TemplateSpan, spans: TemplateSpan*): ContainerType = createSpanContainer(
span +: spans.toList
)
protected def createSpanContainer(spans: Seq[TemplateSpan]): ContainerType
}
/** Wraps a generic element that otherwise could not be placed directly into
* a template document tree. Useful when custom tags which are placed inside
* a template produce non-template tree elements.
*/
case class TemplateElement(element: Element, indent: Int = 0, options: Options = Options.empty)
extends TemplateSpan with ElementTraversal
with RewritableContainer {
type Self = TemplateElement
def rewriteChildren(rules: RewriteRules): TemplateElement =
copy(element = rules.rewriteElement(element))
def withOptions(options: Options): TemplateElement = copy(options = options)
}
/** A generic container element containing a list of template spans. Can be used where a sequence
* of spans must be inserted in a place where a single element is required by the API.
* Usually renderers do not treat the container as a special element and render its children
* as s sub flow of the parent container.
*/
case class TemplateSpanSequence(content: Seq[TemplateSpan], options: Options = Options.empty)
extends TemplateSpan with TemplateSpanContainer {
type Self = TemplateSpanSequence
def withContent(newContent: Seq[TemplateSpan]): TemplateSpanSequence = copy(content = newContent)
def withOptions(options: Options): TemplateSpanSequence = copy(options = options)
}
object TemplateSpanSequence extends TemplateSpanContainerCompanion {
type ContainerType = TemplateSpanSequence
/** Create an instance containing one or more spans, translating them to TemplateSpans */
def adapt(spans: Seq[Span]): ContainerType = {
val templateSpans = spans.map {
case ts: TemplateSpan => ts
case span => TemplateElement(span)
}
createSpanContainer(templateSpans)
}
/** Create an instance containing one or more spans, translating them to TemplateSpans */
def adapt(span: Span, spans: Span*): ContainerType = adapt(span +: spans.toList)
protected def createSpanContainer(spans: Seq[TemplateSpan]): ContainerType = TemplateSpanSequence(
spans
)
}
/** A simple string element, representing the parts of a template
* that are not detected as special markup constructs and treated as raw text.
*/
case class TemplateString(content: String, options: Options = Options.empty) extends TemplateSpan
with TextContainer {
type Self = TemplateString
def withOptions(options: Options): TemplateString = copy(options = options)
}
/** The root element of a template document tree.
*/
case class TemplateRoot(content: Seq[TemplateSpan], options: Options = Options.empty) extends Block
with TemplateSpan with TemplateSpanContainer {
type Self = TemplateRoot
def withContent(newContent: Seq[TemplateSpan]): TemplateRoot = copy(content = newContent)
def withOptions(options: Options): TemplateRoot = copy(options = options)
}
/** Companion with a fallback instance for setups without a default template */
object TemplateRoot extends TemplateSpanContainerCompanion {
type ContainerType = TemplateRoot
protected def createSpanContainer(spans: Seq[TemplateSpan]): ContainerType = TemplateRoot(spans)
/** A fallback instance that can be used when no user-specified template
* is available. It simply inserts the content of the parsed markup document
* without any surrounding decoration.
*/
val fallback: TemplateRoot = TemplateRoot(
TemplateContextReference(CursorKeys.documentContent, required = true, SourceCursor.Generated)
)
}
/** The root element of a document tree (originating from text markup) inside a template.
* Usually created by a template reference like `\${cursor.currentDocument.content}`.
*/
case class EmbeddedRoot(content: Seq[Block], indent: Int = 0, options: Options = Options.empty)
extends TemplateSpan with BlockContainer {
type Self = EmbeddedRoot
def withContent(newContent: Seq[Block]): EmbeddedRoot = copy(content = newContent)
def withOptions(options: Options): EmbeddedRoot = copy(options = options)
}
object EmbeddedRoot extends BlockContainerCompanion {
type ContainerType = EmbeddedRoot
override protected def createBlockContainer(blocks: Seq[Block]) = EmbeddedRoot(blocks)
}