-
Notifications
You must be signed in to change notification settings - Fork 44
/
spans.scala
225 lines (191 loc) · 8.14 KB
/
spans.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
* 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.parse.code.CodeCategory
/** A simple text element.
*/
case class Text(content: String, options: Options = Options.empty) extends Span with TextContainer {
type Self = Text
def withOptions(options: Options): Text = copy(options = options)
}
/** A span of emphasized inline elements that may contain nested spans.
*/
case class Emphasized(content: Seq[Span], options: Options = Options.empty) extends Span
with SpanContainer {
type Self = Emphasized
def withContent(newContent: Seq[Span]): Emphasized = copy(content = newContent)
def withOptions(options: Options): Emphasized = copy(options = options)
}
object Emphasized extends SpanContainerCompanion {
type ContainerType = Emphasized
protected def createSpanContainer(spans: Seq[Span]): Emphasized = Emphasized(spans)
}
/** A span of strong inline elements that may contain nested spans.
*/
case class Strong(content: Seq[Span], options: Options = Options.empty) extends Span
with SpanContainer {
type Self = Strong
def withContent(newContent: Seq[Span]): Strong = copy(content = newContent)
def withOptions(options: Options): Strong = copy(options = options)
}
object Strong extends SpanContainerCompanion {
type ContainerType = Strong
protected def createSpanContainer(spans: Seq[Span]): Strong = Strong(spans)
}
/** A span containing plain, unparsed text.
*/
case class Literal(content: String, options: Options = Options.empty) extends Span
with TextContainer {
type Self = Literal
def withOptions(options: Options): Literal = copy(options = options)
}
/** A span of program code. The content is a sequence of spans to support
* the integration of syntax highlighting systems. Without this support
* the sequence will only consist of a single `Text` element.
*/
case class InlineCode(language: String, content: Seq[Span], options: Options = Options.empty)
extends Span
with SpanContainer {
type Self = InlineCode
def withContent(newContent: Seq[Span]): InlineCode = copy(content = newContent)
def withOptions(options: Options): InlineCode = copy(options = options)
}
/** A span representing deleted inline elements that may contain nested spans.
*/
case class Deleted(content: Seq[Span], options: Options = Options.empty) extends Span
with SpanContainer {
type Self = Deleted
def withContent(newContent: Seq[Span]): Deleted = copy(content = newContent)
def withOptions(options: Options): Deleted = copy(options = options)
}
object Deleted extends SpanContainerCompanion {
type ContainerType = Deleted
protected def createSpanContainer(spans: Seq[Span]): Deleted = Deleted(spans)
}
/** A span representing inserted inline elements that may contain nested spans.
*/
case class Inserted(content: Seq[Span], options: Options = Options.empty) extends Span
with SpanContainer {
type Self = Inserted
def withContent(newContent: Seq[Span]): Inserted = copy(content = newContent)
def withOptions(options: Options): Inserted = copy(options = options)
}
object Inserted extends SpanContainerCompanion {
type ContainerType = Inserted
protected def createSpanContainer(spans: Seq[Span]): Inserted = Inserted(spans)
}
/** A generic container element containing a list of 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. A span sequence is special in that in can be
* used as both a span and a block.
*/
case class SpanSequence(content: Seq[Span], options: Options = Options.empty) extends Block
with Span
with SpanContainer {
type Self = SpanSequence
def withContent(newContent: Seq[Span]): SpanSequence = copy(content = newContent)
def withOptions(options: Options): SpanSequence = copy(options = options)
}
object SpanSequence extends SpanContainerCompanion {
type ContainerType = SpanSequence
protected def createSpanContainer(spans: Seq[Span]): SpanSequence = SpanSequence(spans)
}
/** Represents a section number, usually used in header elements
* when autonumbering is applied.
*/
case class SectionNumber(position: Seq[Int], options: Options = Options.empty) extends Span
with TextContainer {
type Self = SectionNumber
val content = position.mkString(".") + " "
/** Creates a new instance for a child section
* of this section at the specified position.
*/
def child(childPosition: Int) = SectionNumber(position :+ childPosition)
def withOptions(options: Options): SectionNumber = copy(options = options)
}
/** A single span inside a code block that has been
* categorized by a syntax highlighter.
*/
sealed trait CategorizedCode extends Span
/** A span of code associated with zero or more code categories.
*/
case class CodeSpan(
content: String,
categories: Set[CodeCategory],
options: Options = Options.empty
) extends CategorizedCode with TextContainer {
type Self = CodeSpan
def withOptions(options: Options): CodeSpan = copy(options = options)
}
object CodeSpan {
def apply(content: String, category: CodeCategory): CodeSpan = apply(content, Set(category))
def apply(content: String): CodeSpan = apply(content, Set(), Options.empty)
}
object CodeSpans {
/** Extracts all code spans from the given span while at the same time
* converting all regular text nodes to code spans associated with the specified
* set of categories.
*
* This is a fairly low-level operation, usually performed after using a generic
* inline parser (like `InlineParsers.spans`) for syntax highlighting.
*/
def extract(defaultCategories: Set[CodeCategory] = Set())(span: Span): Seq[CodeSpan] =
span match {
case Text(content, _) => Seq(CodeSpan(content, defaultCategories))
case codeSpan: CodeSpan => Seq(codeSpan)
case codeSeq: CodeSpanSequence => codeSeq.collect { case cs: CodeSpan => cs }
case _ => Nil
}
/** Merges all occurrences of two or more adjacent spans with the exact same set of
* associated code categories.
*/
def merge(spans: Seq[CodeSpan]): Seq[CodeSpan] = {
val filtered = spans.filterNot(_.content.isEmpty)
if (filtered.isEmpty) Nil
else {
filtered.tail.foldLeft(List(filtered.head)) { case (acc, next) =>
if (acc.last.categories == next.categories)
acc.init :+ CodeSpan(acc.last.content + next.content, next.categories)
else acc :+ next
}
}
}
}
/** A sequence of code spans where most of them are usually associated with zero or more code categories.
*/
case class CodeSpanSequence(content: Seq[Span], options: Options = Options.empty)
extends CategorizedCode
with SpanContainer {
type Self = CodeSpanSequence
def withContent(newContent: Seq[Span]): CodeSpanSequence = copy(content = newContent)
def withOptions(options: Options): CodeSpanSequence = copy(options = options)
}
object CodeSpanSequence extends SpanContainerCompanion {
type ContainerType = CodeSpanSequence
protected def createSpanContainer(spans: Seq[Span]): CodeSpanSequence = CodeSpanSequence(spans)
}
/** An explicit hard line break.
*/
case class LineBreak(options: Options = Options.empty) extends Span {
type Self = LineBreak
def withOptions(options: Options): LineBreak = copy(options = options)
}
case class Reverse(length: Int, target: Span, fallback: Span, options: Options = Options.empty)
extends Span {
type Self = Reverse
def withOptions(options: Options): Reverse = copy(options = options)
}