-
Notifications
You must be signed in to change notification settings - Fork 115
/
HTMLOutputFormat.swift
85 lines (71 loc) · 2.5 KB
/
HTMLOutputFormat.swift
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
/**
* Splash
* Copyright (c) John Sundell 2018
* MIT license - see LICENSE.md
*/
import Foundation
/// Output format to use to generate an HTML string with a semantic
/// representation of the highlighted code. Each token will be wrapped
/// in a `span` element with a CSS class matching the token's type.
/// Optionally, a `classPrefix` can be set to prefix each CSS class with
/// a given string.
public struct HTMLOutputFormat: OutputFormat {
public var classPrefix: String
public init(classPrefix: String = "") {
self.classPrefix = classPrefix
}
public func makeBuilder() -> Builder {
return Builder(classPrefix: classPrefix)
}
}
public extension HTMLOutputFormat {
struct Builder: OutputBuilder {
private let classPrefix: String
private var html = ""
private var pendingToken: (string: String, type: TokenType)?
private var pendingWhitespace: String?
fileprivate init(classPrefix: String) {
self.classPrefix = classPrefix
}
public mutating func addToken(_ token: String, ofType type: TokenType) {
if var pending = pendingToken {
guard pending.type != type else {
pendingWhitespace.map { pending.string += $0 }
pendingWhitespace = nil
pending.string += token
pendingToken = pending
return
}
}
appendPending()
pendingToken = (token, type)
}
public mutating func addPlainText(_ text: String) {
appendPending()
html.append(text.escapingHTMLEntities())
}
public mutating func addWhitespace(_ whitespace: String) {
if pendingToken != nil {
pendingWhitespace = (pendingWhitespace ?? "") + whitespace
} else {
html.append(whitespace)
}
}
public mutating func build() -> String {
appendPending()
return html
}
private mutating func appendPending() {
if let pending = pendingToken {
html.append("""
<span class="\(classPrefix)\(pending.type.string)">\(pending.string.escapingHTMLEntities())</span>
""")
pendingToken = nil
}
if let whitespace = pendingWhitespace {
html.append(whitespace)
pendingWhitespace = nil
}
}
}
}