# frozen_string_literal: true require 'English' module Jekyll # Definition of the filters. module CodeHighlightFilters def manual_highlight_code(input) new_input = input last_code_block_end = 0 until (code_block = find_next_code_block(new_input, last_code_block_end)).nil? unless code_block.valid? last_code_block_end = code_block.end next end code_block_begin = '' code_block_begin = new_input[0..code_block.begin - 1] || '' if code_block.begin > 0 new_input = code_block_begin \ + replace_highlights(code_block.contents, block: code_block.block?) \ + (new_input[code_block.end..] || '') last_code_block_end = 0 end new_input end private CodeBlock = Struct.new(:begin, :end, :delimiter, :data) do def valid? !(delimiter == '`' && !block?) || (block? && %w[manual manual-highlight].include?(type)) end def block? data.include?("\n") end def contents if block? new_content = data.delete_prefix("#{type}\n") new_content = unindent(new_content) # Make sure that it ends with a newline. new_content = "#{new_content.delete_suffix("\n")}\n" return new_content end data end def type return '' unless block? /^(.+?)\n/ =~ data Regexp.last_match(1) || '' end private def unindent(str) loop do populated_lines_regex = /^.+/ unindent_regex = /^#{str.scan(/^[^\S\n\r]+/).min}/ # We hit the maximum. break unless str.scan(unindent_regex).length == str.scan(populated_lines_regex).length unindented_str = str.gsub(unindent_regex, '') str = unindented_str end str end end def find_next_code_block(input, from, delimiter = '`') code_block_start_regex = /(?:#{delimiter})+/ from_substr_start = input[from..] code_block_start_match = code_block_start_regex.match(from_substr_start) return nil if code_block_start_match.nil? code_block_start_begin = from + code_block_start_match.begin(0) code_block_start_end = from + code_block_start_match.end(0) code_block_delimiter = code_block_start_match[0] code_block_end_regex = /(?.*?)\]\((?.+?)\)/ non_highlights_regex = /(?.*?)(?:(?#{highlights_regex})|$)/ num_highlights = input.scan(highlights_regex).length highlight_only = input.gsub(highlights_regex, '') == '' force_spaces_input = input.gsub(non_highlights_regex) do |_match| non_highlight = $LAST_MATCH_INFO[:non_highlight] highlight = $LAST_MATCH_INFO[:highlight] || '' non_highlight.gsub!('<', '<') non_highlight.gsub!('>', '>') non_highlight + highlight end highlights_result = force_spaces_input.gsub(highlights_regex) do |_match| type = $LAST_MATCH_INFO[:type] next '' if type == 'wbr' classes = [] classes.push('code-highlight') if num_highlights == 1 && !block classes.push(type) content = $LAST_MATCH_INFO[:content] content.gsub!('<', '<') content.gsub!('>', '>') "#{content}" end if block code_tag = "#{highlights_result}" pre_tag = "
#{code_tag}
" return pre_tag end return "#{highlights_result}" unless highlight_only && num_highlights == 1 highlights_result end end end Liquid::Template.register_filter(Jekyll::CodeHighlightFilters)