/
html.cr
102 lines (91 loc) · 2.48 KB
/
html.cr
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
require "../syntax_highlighter"
require "html"
# A syntax highlighter that renders Crystal source code with HTML markup.
#
# ```
# code = %(foo = bar("baz\#{PI + 1}") # comment)
# html = Crystal::SyntaxHighlighter::HTML.highlight(code)
# html # => "foo <span class=\"o\">=</span> bar(<span class=\"s\">"baz</span><span class=\"i\">\#{</span><span class=\"t\">PI</span> <span class=\"o\">+</span> <span class=\"n\">1</span><span class=\"i\">}</span><span class=\"s\">"</span>) <span class=\"c\"># comment</span>"
# ```
class Crystal::SyntaxHighlighter::HTML < Crystal::SyntaxHighlighter
# Highlights *code* and writes the result to *io*.
def self.highlight(io : IO, code : String)
new(io).highlight(code)
end
# Highlights *code* and returns the result.
def self.highlight(code : String)
String.build do |io|
highlight(io, code)
end
end
# Highlights *code* or returns unhighlighted *code* on error.
#
# Same as `.highlight(code : String)` except that any error is rescued and
# returns unhighlighted source code.
def self.highlight!(code : String)
highlight(code)
rescue
::HTML.escape(code)
end
# Creates a new instance of an HTML syntax highlighter.
#
# Appends highlighted output (when calling `#highlight`) to *io*.
def initialize(@io : IO)
end
def render(type : TokenType, value : String)
case type
when .comment?
span "c" { ::HTML.escape(value, @io) }
when .number?
span "n", &.print value
when .char?
span "s" { ::HTML.escape(value, @io) }
when .symbol?
span "n" { ::HTML.escape(value, @io) }
when .const?
span "t", &.print value
when .string?
span "s" { ::HTML.escape(value, @io) }
when .ident?
span "m" { ::HTML.escape(value, @io) }
when .keyword?, .self?
span "k", &.print value
when .primitive_literal?
span "n", &.print value
when .operator?
span "o" { ::HTML.escape(value, @io) }
else
::HTML.escape(value, @io)
end
end
def render_delimiter(&)
span "s" do
yield
end
end
def render_interpolation(&)
span_end
span "i", &.print "\#{"
yield
span "i", &.print "}"
span_start "s"
end
def render_string_array(&)
span "s" do
yield
end
end
private def span(klass, &)
span_start klass
yield @io
span_end
end
private def span_start(klass)
@io << %(<span class=")
@io << klass
@io << %(">)
end
private def span_end
@io << %(</span>)
end
end