0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018 require 'liquid'
0019 require 'rouge'
0020
0021 module Jekyll
0022 class IncludeExampleTag < Liquid::Tag
0023
0024 def initialize(tag_name, markup, tokens)
0025 @markup = markup
0026 super
0027 end
0028
0029 def render(context)
0030 site = context.registers[:site]
0031 config_dir = '../examples/src/main'
0032 @code_dir = File.join(site.source, config_dir)
0033
0034 clean_markup = @markup.strip
0035
0036 parts = clean_markup.strip.split(' ')
0037 if parts.length > 1 then
0038 @snippet_label = ':' + parts[0]
0039 snippet_file = parts[1]
0040 else
0041 @snippet_label = ''
0042 snippet_file = parts[0]
0043 end
0044
0045 @file = File.join(@code_dir, snippet_file)
0046 @lang = snippet_file.split('.').last
0047
0048 begin
0049 code = File.open(@file).read.encode("UTF-8")
0050 rescue => e
0051
0052
0053 puts(e)
0054 puts(e.backtrace)
0055 exit 1
0056 end
0057 code = select_lines(code).strip
0058
0059 formatter = Rouge::Formatters::HTMLPygments.new(Rouge::Formatters::HTML.new)
0060 lexer = Rouge::Lexer.find(@lang)
0061 rendered_code = formatter.format(lexer.lex(code))
0062
0063 hint = "<div><small>Find full example code at " \
0064 "\"examples/src/main/#{snippet_file}\" in the Spark repo.</small></div>"
0065
0066 rendered_code + hint
0067 end
0068
0069
0070
0071 def trim_codeblock(lines)
0072
0073 min_start_spaces = lines
0074 .select { |l| l.strip.size !=0 }
0075 .map { |l| l[/\A */].size }
0076 .min
0077
0078 lines.map { |l| l.strip.size == 0 ? l : l[min_start_spaces .. -1] }
0079 end
0080
0081
0082
0083 def select_lines(code)
0084 lines = code.each_line.to_a
0085
0086
0087 startIndices = lines
0088 .each_with_index
0089 .select { |l, i| l.include? "$example on#{@snippet_label}$" }
0090 .map { |l, i| i }
0091
0092
0093 endIndices = lines
0094 .each_with_index
0095 .select { |l, i| l.include? "$example off#{@snippet_label}$" }
0096 .map { |l, i| i }
0097
0098 raise "Start indices amount is not equal to end indices amount, see #{@file}." \
0099 unless startIndices.size == endIndices.size
0100
0101 raise "No code is selected by include_example, see #{@file}." \
0102 if startIndices.size == 0
0103
0104
0105
0106 lastIndex = -1
0107 result = ""
0108 startIndices.zip(endIndices).each do |start, endline|
0109 raise "Overlapping between two example code blocks are not allowed, see #{@file}." \
0110 if start <= lastIndex
0111 raise "$example on$ should not be in the same line with $example off$, see #{@file}." \
0112 if start == endline
0113 lastIndex = endline
0114 range = Range.new(start + 1, endline - 1)
0115 trimmed = trim_codeblock(lines[range])
0116
0117 taggs_filtered = trimmed.select { |l| !l.include? '$example ' }
0118 result += taggs_filtered.join
0119 result += "\n"
0120 end
0121 result
0122 end
0123 end
0124 end
0125
0126 Liquid::Template.register_tag('include_example', Jekyll::IncludeExampleTag)