]>
Commit | Line | Data |
---|---|---|
91e44d91 S |
1 | module Jekyll |
2 | module Tags | |
3 | class ExampleBlock < Liquid::Block | |
4 | include Liquid::StandardFilters | |
5 | ||
6 | # The regular expression syntax checker. Start with the language specifier. | |
7 | # Follow that by zero or more space separated options that take one of three | |
8 | # forms: name, name=value, or name="<quoted list>" | |
9 | # | |
10 | # <quoted list> is a space-separated list of numbers | |
11 | SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=((\w|[0-9_-])+|"([0-9]+\s)*[0-9]+"))?)*)$/ | |
12 | ||
13 | def initialize(tag_name, markup, tokens) | |
14 | super | |
15 | if markup.strip =~ SYNTAX | |
16 | @lang = $1.downcase | |
17 | @options = {} | |
18 | if defined?($2) && $2 != '' | |
19 | # Split along 3 possible forms -- key="<quoted list>", key=value, or key | |
20 | $2.scan(/(?:\w+(?:=(?:(?:\w|[0-9_-])+|"[^"]*")?)?)/) do |opt| | |
21 | key, value = opt.split('=') | |
22 | # If a quoted list, convert to array | |
23 | if value && value.include?("\"") | |
24 | value.gsub!(/"/, "") | |
25 | value = value.split | |
26 | end | |
27 | @options[key.to_sym] = value || true | |
28 | end | |
29 | end | |
30 | @options[:linenos] = false | |
31 | else | |
32 | raise SyntaxError.new <<-eos | |
33 | Syntax Error in tag 'example' while parsing the following markup: | |
34 | ||
35 | #{markup} | |
36 | ||
37 | Valid syntax: example <lang> | |
38 | eos | |
39 | end | |
40 | end | |
41 | ||
42 | def render(context) | |
43 | prefix = context["highlighter_prefix"] || "" | |
44 | suffix = context["highlighter_suffix"] || "" | |
45 | code = super.to_s.strip | |
46 | ||
47 | output = case context.registers[:site].highlighter | |
48 | ||
49 | when 'rouge' | |
50 | render_rouge(code) | |
51 | end | |
52 | ||
53 | rendered_output = example(code) + add_code_tag(output) | |
54 | prefix + rendered_output + suffix | |
55 | end | |
56 | ||
57 | def example(output) | |
58 | "<div class=\"bd-example\" data-example-id=\"#{@options[:id]}\">\n#{output}\n</div>" | |
59 | end | |
60 | ||
61 | def remove_holderjs(code) | |
62 | code = code.gsub(/data-src="holder.js.+?"/, 'src="..."') | |
63 | end | |
64 | ||
65 | def remove_example_classes(code) | |
66 | # Find `bd-` classes and remove them from the highlighted code. Because of how this regex works, it will also | |
67 | # remove classes that are after the `bd-` class. While this is a bug, I left it because it can be helpful too. | |
68 | # To fix the bug, replace `(?=")` with `(?=("|\ ))`. | |
69 | code = code.gsub(/(?!class=".)\ *?bd-.+?(?=")/, "") | |
70 | # Find empty class attributes after the previous regex and remove those too. | |
71 | code = code.gsub(/\ class=""/, "") | |
72 | end | |
73 | ||
74 | def render_rouge(code) | |
75 | require 'rouge' | |
76 | formatter = Rouge::Formatters::HTML.new(line_numbers: @options[:linenos], wrap: false) | |
77 | lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText | |
78 | code = remove_holderjs(code) | |
79 | code = remove_example_classes(code) | |
80 | code = formatter.format(lexer.lex(code)) | |
81 | "<div class=\"highlight\"><pre>#{code}</pre></div>" | |
82 | end | |
83 | ||
84 | def add_code_tag(code) | |
85 | # Add nested <code> tags to code blocks | |
86 | code = code.sub(/<pre>\n*/,'<pre><code class="language-' + @lang.to_s.gsub("+", "-") + '" data-lang="' + @lang.to_s + '">') | |
87 | code = code.sub(/\n*<\/pre>/,"</code></pre>") | |
88 | code.strip | |
89 | end | |
90 | ||
91 | end | |
92 | end | |
93 | end | |
94 | ||
95 | Liquid::Template.register_tag('example', Jekyll::Tags::ExampleBlock) |