Modernize Kramdown for Markdown converter.
This commit is contained in:
parent
b01b089f69
commit
3432fd2c2d
3
Gemfile
3
Gemfile
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}]
|
||||
|
||||
|
|
|
@ -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
|
||||
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")
|
||||
Jekyll::External.require_with_graceful_fail "kramdown"
|
||||
@main_fallback_highlighter = config["highlighter"] || "rogue"
|
||||
@config = config["kramdown"] || {}
|
||||
setup
|
||||
end
|
||||
|
||||
# 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)
|
||||
end
|
||||
end
|
||||
|
||||
Kramdown::Document.new(content, Utils.symbolize_hash_keys(@config['kramdown'])).to_html
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
'toc_levels' => '1..6',
|
||||
'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>(“|“)Pit(’|’)hy(”|”)<\/p>/, @markdown.convert(%{"Pit'hy"}).strip
|
||||
context "when asked to convert smart quotes" do
|
||||
should "convert" do
|
||||
assert_match %r!<p>(“|“)Pit(’|’)hy(”|”)<\/p>!, @markdown.convert(%{"Pit'hy"}).strip
|
||||
end
|
||||
|
||||
override = { 'kramdown' => { 'smart_quotes' => 'lsaquo,rsaquo,laquo,raquo' } }
|
||||
markdown = Converters::Markdown.new(Utils.deep_merge_hashes(@config, override))
|
||||
assert_match /<p>(«|«)Pit(›|›)hy(»|»)<\/p>/, markdown.convert(%{"Pit'hy"}).strip
|
||||
should "support custom types" do
|
||||
override = {
|
||||
'kramdown' => {
|
||||
'smart_quotes' => 'lsaquo,rsaquo,laquo,raquo'
|
||||
}
|
||||
}
|
||||
|
||||
markdown = Converters::Markdown.new(Utils.deep_merge_hashes(@config, override))
|
||||
assert_match %r!<p>(«|«)Pit(›|›)hy(»|»)<\/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 "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 "also work for defaults" do
|
||||
default = Jekyll::Configuration::DEFAULTS['kramdown']['coderay']['coderay_tab_width']
|
||||
assert_equal default, @converter_config['coderay_tab_width']
|
||||
end
|
||||
|
||||
should "not overwrite" do
|
||||
assert_equal 12, @converter_config['coderay_bold_every']
|
||||
end
|
||||
markdown.convert("hello world")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue