Modernize Kramdown for Markdown converter.

This commit is contained in:
Jordon Bedwell 2015-11-03 19:48:29 -06:00
parent b01b089f69
commit 3432fd2c2d
6 changed files with 173 additions and 74 deletions

View File

@ -18,8 +18,9 @@ group :test do
gem 'jekyll_test_plugin_malicious'
gem 'minitest-reporters'
gem 'minitest-profile'
gem 'minitest'
gem 'rspec-mocks'
gem 'minitest'
gem "nokogiri"
if RUBY_PLATFORM =~ /cygwin/ || RUBY_VERSION.start_with?("2.2")
gem 'test-unit'

View File

@ -68,17 +68,7 @@ module Jekyll
'footnote_nr' => 1,
'entity_output' => 'as_char',
'toc_levels' => '1..6',
'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
'enable_coderay' => false,
'coderay' => {
'coderay_wrap' => 'div',
'coderay_line_numbers' => 'inline',
'coderay_line_number_start' => 1,
'coderay_tab_width' => 4,
'coderay_bold_every' => 10,
'coderay_css' => 'style'
}
'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo'
}
}]

View File

@ -1,33 +1,86 @@
# Frozen-string-literal: true
# Encoding: utf-8
module Jekyll
module Converters
class Markdown
class KramdownParser
CODERAY_DEFAULTS = {
"css" => "style",
"bold_every" => 10,
"line_numbers" => "inline",
"line_number_start" => 1,
"tab_width" => 4,
"wrap" => "div"
}
def initialize(config)
require 'kramdown'
@config = config
# If kramdown supported highlighter enabled, use that
highlighter = @config['highlighter']
if highlighter == 'rouge' || highlighter == 'coderay'
@config['kramdown']['syntax_highlighter'] ||= highlighter
Jekyll::External.require_with_graceful_fail "kramdown"
@main_fallback_highlighter = config["highlighter"] || "rogue"
@config = config["kramdown"] || {}
setup
end
rescue LoadError
STDERR.puts 'You are missing a library required for Markdown. Please run:'
STDERR.puts ' $ [sudo] gem install kramdown'
raise Errors::FatalException.new("Missing dependency: kramdown")
# Setup and normalize the configuration:
# * Create Kramdown if it doesn't exist.
# * Set syntax_highlighter, detecting enable_coderay and merging highlighter if none.
# * Merge kramdown[coderay] into syntax_highlighter_opts stripping coderay_.
# * Make sure `syntax_highlighter_opts` exists.
def setup
@config["syntax_highlighter"] ||= highlighter
@config["syntax_highlighter_opts"] ||= {}
@config["coderay"] ||= {} # XXX: Legacy.
modernize_coderay_config
end
def convert(content)
# Check for use of coderay
if @config['kramdown']['enable_coderay']
%w[wrap line_numbers line_numbers_start tab_width bold_every css default_lang].each do |opt|
key = "coderay_#{opt}"
@config['kramdown'][key] = @config['kramdown']['coderay'][key] unless @config['kramdown'].key?(key)
Kramdown::Document.new(content, @config).to_html
end
# config[kramdown][syntax_higlighter] > config[kramdown][enable_coderay] > config[highlighter]
# Where `enable_coderay` is now deprecated because Kramdown
# supports Rouge now too.
private
def highlighter
@highlighter ||= begin
if highlighter = @config["syntax_highlighter"] then highlighter
elsif @config.key?("enable_coderay") && @config["enable_coderay"]
Jekyll.logger.warn "DEPRECATION: You are using 'enable_coderay', use syntax_highlighter: coderay."
"coderay"
else
@main_fallback_highlighter
end
end
end
Kramdown::Document.new(content, Utils.symbolize_hash_keys(@config['kramdown'])).to_html
private
def strip_coderay_prefix(hash)
hash.each_with_object({}) do |(key, val), hsh|
cleaned_key = key.gsub(/\Acoderay_/, "")
Jekyll.logger.warn "You are using '#{key}'. Normalizing to #{cleaned_key}." if key != cleaned_key
hsh[cleaned_key] = val
end
end
# If our highlighter is CodeRay we go in to merge the CodeRay defaults
# with your "coderay" key if it's there, deprecating it in the
# process of you using it.
private
def modernize_coderay_config
if highlighter == "coderay"
Jekyll.logger.warn "DEPRECATION: kramdown.coderay is deprecated use syntax_highlighter_opts."
@config["syntax_highlighter_opts"] = begin
strip_coderay_prefix(
@config["syntax_highlighter_opts"] \
.merge(CODERAY_DEFAULTS) \
.merge(@config["coderay"])
)
end
end
end
end
end
end

View File

@ -8,6 +8,10 @@ module Jekyll
SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
def strip_heredoc(str)
str.gsub(/^[ \t]{#{(str.scan(/^[ \t]*(?=\S)/).min || "").size}}/, "")
end
# Non-destructive version of deep_merge_hashes! See that method.
#
# Returns the merged hashes.

View File

@ -11,13 +11,13 @@ unless ENV['TRAVIS']
end
end
require "nokogiri"
require 'rubygems'
require 'ostruct'
require 'minitest/autorun'
require 'minitest/reporters'
require 'minitest/profile'
require 'rspec/mocks'
require 'jekyll'
Jekyll.logger = Logger.new(StringIO.new)
@ -51,15 +51,15 @@ class JekyllUnitTest < Minitest::Test
end
def before_setup
::RSpec::Mocks.setup
RSpec::Mocks.setup
super
end
def after_teardown
super
::RSpec::Mocks.verify
RSpec::Mocks.verify
ensure
::RSpec::Mocks.teardown
RSpec::Mocks.teardown
end
def fixture_site(overrides = {})
@ -120,4 +120,10 @@ class JekyllUnitTest < Minitest::Test
end
alias_method :capture_stdout, :capture_output
alias_method :capture_stderr, :capture_output
def nokogiri_fragment(str)
Nokogiri::HTML.fragment(
str
)
end
end

View File

@ -8,65 +8,110 @@ class TestKramdown < JekyllUnitTest
@config = {
'markdown' => 'kramdown',
'kramdown' => {
'auto_ids' => false,
'footnote_nr' => 1,
'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
'entity_output' => 'as_char',
'toc_levels' => '1..6',
'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
'auto_ids' => false,
'footnote_nr' => 1,
'enable_coderay' => true,
'coderay_bold_every'=> 12,
'coderay' => {
'coderay_css' => :style,
'coderay_bold_every' => 8
'syntax_highlighter_opts' => {
'bold_every' => 8, 'css' => :class
}
}
}
@config = Jekyll.configuration(@config)
@markdown = Converters::Markdown.new(@config)
@markdown = Converters::Markdown.new(
@config
)
end
# http://kramdown.gettalong.org/converter/html.html#options
should "pass kramdown options" do
should "run Kramdown" do
assert_equal "<h1>Some Header</h1>", @markdown.convert('# Some Header #').strip
end
should "convert quotes to smart quotes" do
assert_match /<p>(&#8220;|“)Pit(&#8217;|)hy(&#8221;|”)<\/p>/, @markdown.convert(%{"Pit'hy"}).strip
context "when asked to convert smart quotes" do
should "convert" do
assert_match %r!<p>(&#8220;|“)Pit(&#8217;|)hy(&#8221;|”)<\/p>!, @markdown.convert(%{"Pit'hy"}).strip
end
should "support custom types" do
override = {
'kramdown' => {
'smart_quotes' => 'lsaquo,rsaquo,laquo,raquo'
}
}
override = { 'kramdown' => { 'smart_quotes' => 'lsaquo,rsaquo,laquo,raquo' } }
markdown = Converters::Markdown.new(Utils.deep_merge_hashes(@config, override))
assert_match /<p>(&#171;|«)Pit(&#8250;|)hy(&#187;|»)<\/p>/, markdown.convert(%{"Pit'hy"}).strip
assert_match %r!<p>(&#171;|«)Pit(&#8250;|)hy(&#187;|»)<\/p>!, \
markdown.convert(%{"Pit'hy"}).strip
end
end
should "render fenced code blocks with syntax highlighting" do
assert_equal "<div class=\"highlighter-rouge\"><pre class=\"highlight\"><code><span class=\"nb\">puts</span> <span class=\"s2\">\"Hello world\"</span>\n</code></pre>\n</div>", @markdown.convert(
<<-EOS
~~~ruby
puts "Hello world"
~~~
EOS
).strip
result = nokogiri_fragment(@markdown.convert(Utils.strip_heredoc <<-MARKDOWN))
~~~ruby
puts "Hello World"
~~~
MARKDOWN
selector = "div.highlighter-rouge>pre.highlight>code"
refute result.css(selector).empty?
end
context "moving up nested coderay options" do
setup do
@markdown.convert('some markup')
@converter_config = @markdown.instance_variable_get(:@config)['kramdown']
context "when a custom highlighter is chosen" do
should "use the chosen highlighter if it's available" do
override = { "markdown" => "kramdown", "kramdown" => { "syntax_highlighter" => :coderay }}
markdown = Converters::Markdown.new(Utils.deep_merge_hashes(@config, override))
result = nokogiri_fragment(markdown.convert(Utils.strip_heredoc <<-MARKDOWN))
~~~ruby
puts "Hello World"
~~~
MARKDOWN
selector = "div.highlighter-coderay>div.CodeRay>div.code>pre"
refute result.css(selector).empty?
end
should "work correctly" do
assert_equal :style, @converter_config['coderay_css']
should "support legacy enable_coderay... for now" do
override = {
"markdown" => "kramdown",
"kramdown" => {
"syntax_highlighter" => nil,
"enable_coderay" => true
}
}
markdown = Converters::Markdown.new(Utils.deep_merge_hashes(@config, override))
result = nokogiri_fragment(markdown.convert(Utils.strip_heredoc <<-MARKDOWN))
~~~ruby
puts "Hello World"
~~~
MARKDOWN
selector = "div.highlighter-coderay>div.CodeRay>div.code>pre"
refute result.css(selector).empty?
end
end
should "also work for defaults" do
default = Jekyll::Configuration::DEFAULTS['kramdown']['coderay']['coderay_tab_width']
assert_equal default, @converter_config['coderay_tab_width']
should "move coderay to syntax_highlighter_opts" do
original = Kramdown::Document.method(:new)
markdown = Converters::Markdown.new(Utils.deep_merge_hashes(@config, {
"markdown" => "kramdown",
"kramdown" => {
"syntax_highlighter" => "coderay",
"coderay" => {
"hello" => "world"
}
}
}))
expect(Kramdown::Document).to receive(:new) do |arg1, hash|
assert_equal hash["syntax_highlighter_opts"]["hello"], "world"
original.call(arg1, hash)
end
should "not overwrite" do
assert_equal 12, @converter_config['coderay_bold_every']
end
markdown.convert("hello world")
end
end
end