amiorin/reloadlive

View on GitHub
lib/reloadlive/render.rb

Summary

Maintainability
B
6 hrs
Test Coverage
require 'github/markup'
require 'pygments'
require 'yaml'

# initialize Pygments
Pygments.start

module Reloadlive
  class Render
    include Rack::Utils
    alias_method :h, :escape_html
    attr_accessor :filename, :data, :title

    def initialize filename, data
      @filename = filename
      @data = data
      @codemap = {}
      @title = filename
    end

    def formatted_data encoding=nil
      data = @data.dup
      data = extract_yaml data
      data = extract_code data
      begin
        data = GitHub::Markup.render(@filename, data)
      rescue => e
        puts "Exception rendering #{@filename}: #{e.message}"
      end
      data = process_code(data, encoding)
      if data == @data
        p_lexer = Pygments::Lexer.find_by_extname(File.extname(@filename))
        lexer = p_lexer ? p_lexer.aliases.first : nil
        data = Pygments.highlight(data, :lexer => lexer, :options => {:encoding => encoding.to_s, :startinline => true})
      end
      data
    end

    def extract_yaml(data)
      if data =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
        @title = YAML.load($1)['title']
        data = $'
      end
      data
    rescue => e
      puts "YAML Exception reading #{@filename}: #{e.message}"
    end

    def extract_code(data)
      data.gsub!(/^([ \t]*)(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n\1(~~~+)[ \t\r]*$/m) do
        m_indent = $1
        m_start  = $2 # ~~~
        m_lang   = $3
        m_code   = $4
        m_end    = $5 # ~~~

        # start and finish tilde fence must be the same length
        return '' if m_start.length != m_end.length

        lang   = m_lang ? m_lang.strip : nil
        id     = Digest::SHA1.hexdigest("#{lang}.#{m_code}")
        cached = check_cache(:code, id)

        # extract lang from { .ruby } or { #stuff .ruby .indent }
        # see http://johnmacfarlane.net/pandoc/README.html#delimited-code-blocks

        if lang
            lang = lang.match(/\.([^}\s]+)/)
            lang = lang[1] unless lang.nil?
        end

        @codemap[id] = cached   ?
          { :output => cached } :
          { :lang => lang, :code => m_code, :indent => m_indent }

        "#{m_indent}#{id}" # print the SHA1 ID with the proper indentation
      end

      data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```[ \t]*\r?$/m) do
        lang   = $2 ? $2.strip : nil
        id     = Digest::SHA1.hexdigest("#{lang}.#{$3}")
        cached = check_cache(:code, id)
        @codemap[id] = cached   ?
          { :output => cached } :
          { :lang => lang, :code => $3, :indent => $1 }
        "#{$1}#{id}" # print the SHA1 ID with the proper indentation
      end
      data
    end

    def process_code(data, encoding = nil)
      return data if data.nil? || data.size.zero? || @codemap.size.zero?

      blocks    = []
      @codemap.each do |id, spec|
        next if spec[:output] # cached

        code = spec[:code]

        remove_leading_space(code, /^#{spec[:indent]}/m)
        remove_leading_space(code, /^(  |\t)/m)

        blocks << [spec[:lang], code]
      end

      highlighted = []
      blocks.each do |lang, code|
        encoding ||= 'utf-8'
        begin
          # must set startinline to true for php to be highlighted without <?
          # http://pygments.org/docs/lexers/
          hl_code = Pygments.highlight(code, :lexer => lang, :options => {:encoding => encoding.to_s, :startinline => true})
        rescue
          hl_code = code
        end
        highlighted << hl_code
      end

      @codemap.each do |id, spec|
        body = spec[:output] || begin
          if (body = highlighted.shift.to_s).size > 0
            update_cache(:code, id, body)
            body
          else
            "<pre><code>#{CGI.escapeHTML(spec[:code])}</code></pre>"
          end
        end
        data.gsub!(id) do
          body
        end
      end

      data
    end

    def remove_leading_space(code, regex)
      if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ regex }
        code.gsub!(regex) do
          ''
        end
      end
    end

    def check_cache(type, id)
    end

    def update_cache(type, id, data)
    end
  end
end