-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathhighlight_caption_block.rb
141 lines (119 loc) · 4.32 KB
/
highlight_caption_block.rb
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
module Czak
module Tags
class HighlightCaptionBlock < Liquid::Block
include Liquid::StandardFilters
# The regular expression syntax checker. Start with the language specifier.
# Follow that by zero or more space separated options that take one of three
# forms: name, name=value, or name="quoted text"
#
# quoted text is allowed to contain spaces
SYNTAX = %r!^([\w.+#-]+)((\s+\w+="[^"]*"|\s+\w+=[\w\.\/-]+|\w+?)*)$!
def initialize(tag_name, markup, tokens)
super
if markup.strip =~ SYNTAX
@lang = Regexp.last_match(1).downcase
@highlight_options = parse_options(Regexp.last_match(2))
else
raise SyntaxError, <<-eos
Syntax Error in tag 'highlight' while parsing the following markup:
#{markup}
Valid syntax: highlight <lang> [linenos]
eos
end
end
def render(context)
prefix = context["highlighter_prefix"] || ""
suffix = context["highlighter_suffix"] || ""
code = super.to_s.gsub(%r!\A(\n|\r)+|(\n|\r)+\z!, "")
is_safe = !!context.registers[:site].safe
output =
case context.registers[:site].highlighter
when "pygments"
render_pygments(code, is_safe)
when "rouge"
render_rouge(code)
else
render_codehighlighter(code)
end
rendered_output = add_code_tag(output)
prefix + rendered_output + suffix
end
def sanitized_opts(opts, is_safe)
if is_safe
Hash[[
[:startinline, opts.fetch(:startinline, nil)],
[:hl_lines, opts.fetch(:hl_lines, nil)],
[:linenos, opts.fetch(:linenos, nil)],
[:encoding, opts.fetch(:encoding, "utf-8")],
[:cssclass, opts.fetch(:cssclass, nil)],
[:caption, opts.fetch(:caption, nil)]
].reject { |f| f.last.nil? }]
else
opts
end
end
private
def parse_options(input)
options = {}
unless input.empty?
# Split along 3 possible forms -- key="<quoted list>", key=value, or key
input.scan(%r!(?:\w+="[^"]*"|\w+=[\w\.\/-]+|\w+)!) do |opt|
key, value = opt.split("=")
# If a quoted list, remove quotes
if value && value.include?("\"")
value.delete!('"')
end
options[key.to_sym] = value || true
end
end
if options.key?(:linenos) && options[:linenos] == true
options[:linenos] = "inline"
end
options
end
def render_pygments(code, is_safe)
Jekyll::External.require_with_graceful_fail("pygments")
highlighted_code = Pygments.highlight(
code,
:lexer => @lang,
:options => sanitized_opts(@highlight_options, is_safe)
)
if highlighted_code.nil?
Jekyll.logger.error <<eos
There was an error highlighting your code:
#{code}
While attempting to convert the above code, Pygments.rb returned an unacceptable value.
This is usually a timeout problem solved by running `jekyll build` again.
eos
raise ArgumentError, "Pygments.rb returned an unacceptable value "\
"when attempting to highlight some code."
end
highlighted_code.sub('<div class="highlight"><pre>', "").sub("</pre></div>", "")
end
def render_rouge(code)
Jekyll::External.require_with_graceful_fail("rouge")
formatter = Rouge::Formatters::HTML.new(
:line_numbers => @highlight_options[:linenos],
:wrap => false
)
lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
formatter.format(lexer.lex(code))
end
def render_codehighlighter(code)
h(code).strip
end
def add_code_tag(code)
code_attributes = [
"class=\"language-#{@lang.to_s.tr("+", "-")}\"",
"data-lang=\"#{@lang}\""
].join(" ")
res = "<figure>"
res += "<pre class=\"highlight\"><code #{code_attributes}>#{code.chomp}</code></pre>"
res += "<figcaption>#{@highlight_options[:caption]}</figcaption>" if @highlight_options[:caption]
res += "</figure>"
res
end
end
end
end
Liquid::Template.register_tag("code", Czak::Tags::HighlightCaptionBlock)