diff --git a/History.txt b/History.txt index ef9a75ee..d0091fd0 100644 --- a/History.txt +++ b/History.txt @@ -1,6 +1,9 @@ == HEAD * Major Enhancements * Add command line importer functionality (#253) + * Add Redcarpet Markdown support (#318) + * Make markdown/textile extensions configurable (#312) + * Add `markdownify` filter * Minor Enhancements * Switch to Albino gem * Bundler support diff --git a/bin/jekyll b/bin/jekyll index 753eedad..2e33f377 100755 --- a/bin/jekyll +++ b/bin/jekyll @@ -82,6 +82,10 @@ opts = OptionParser.new do |opts| opts.on("--rdiscount", "Use rdiscount gem for Markdown") do options['markdown'] = 'rdiscount' end + + opts.on("--redcarpet", "Use redcarpet gem for Markdown") do + options['markdown'] = 'redcarpet' + end opts.on("--kramdown", "Use kramdown gem for Markdown") do options['markdown'] = 'kramdown' diff --git a/features/site_configuration.feature b/features/site_configuration.feature index 0681c2f5..8d3bee5d 100644 --- a/features/site_configuration.feature +++ b/features/site_configuration.feature @@ -55,6 +55,13 @@ Feature: Site configuration Then the _site directory should exist And I should see "Google" in "_site/index.html" + Scenario: Use Redcarpet for markup + Given I have an "index.markdown" page that contains "[Google](http://google.com)" + And I have a configuration file with "markdown" set to "redcarpet" + When I run jekyll + Then the _site directory should exist + And I should see "Google" in "_site/index.html" + Scenario: Use Maruku for markup Given I have an "index.markdown" page that contains "[Google](http://google.com)" And I have a configuration file with "markdown" set to "maruku" diff --git a/jekyll.gemspec b/jekyll.gemspec index 2b2efdba..d23bd192 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = 'jekyll' s.version = '0.10.0' - s.date = '2010-12-16' + s.date = '2011-05-30' s.rubyforge_project = 'jekyll' s.summary = "A simple, blog aware, static site generator." @@ -32,12 +32,14 @@ Gem::Specification.new do |s| s.add_development_dependency('redgreen', ">= 1.2.2") s.add_development_dependency('shoulda', ">= 2.11.3") s.add_development_dependency('rr', ">= 1.0.2") - s.add_development_dependency('cucumber', ">= 0.10.0") + s.add_development_dependency('cucumber', ">= 0.10.3") s.add_development_dependency('RedCloth', ">= 4.2.1") s.add_development_dependency('rdiscount', ">= 1.6.5") - + s.add_development_dependency('redcarpet', ">= 1.9.0") + # = MANIFEST = s.files = %w[ + Gemfile History.txt LICENSE README.textile @@ -56,7 +58,6 @@ Gem::Specification.new do |s| features/support/env.rb jekyll.gemspec lib/jekyll.rb - lib/jekyll/albino.rb lib/jekyll/converter.rb lib/jekyll/converters/identity.rb lib/jekyll/converters/markdown.rb @@ -70,13 +71,16 @@ Gem::Specification.new do |s| lib/jekyll/layout.rb lib/jekyll/migrators/csv.rb lib/jekyll/migrators/drupal.rb + lib/jekyll/migrators/enki.rb lib/jekyll/migrators/marley.rb lib/jekyll/migrators/mephisto.rb lib/jekyll/migrators/mt.rb + lib/jekyll/migrators/posterous.rb lib/jekyll/migrators/textpattern.rb + lib/jekyll/migrators/tumblr.rb lib/jekyll/migrators/typo.rb - lib/jekyll/migrators/wordpress.com.rb lib/jekyll/migrators/wordpress.rb + lib/jekyll/migrators/wordpressdotcom.rb lib/jekyll/page.rb lib/jekyll/plugin.rb lib/jekyll/post.rb @@ -132,6 +136,7 @@ Gem::Specification.new do |s| test/test_pager.rb test/test_post.rb test/test_rdiscount.rb + test/test_redcarpet.rb test/test_site.rb test/test_tags.rb test/test_redcloth.rb diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 3ffc36b4..9ff0ce7d 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -65,6 +65,9 @@ module Jekyll 'pygments' => false, 'markdown' => 'maruku', 'permalink' => 'date', + + 'markdown_ext' => 'markdown,mkd,mkdn,md', + 'textile_ext' => 'textile', 'maruku' => { 'use_tex' => false, @@ -76,6 +79,9 @@ module Jekyll 'rdiscount' => { 'extensions' => [] }, + 'redcarpet' => { + 'extensions' => [] + }, 'kramdown' => { 'auto_ids' => true, 'footnote_nr' => 1, diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb index 00c56d7c..b5655476 100644 --- a/lib/jekyll/converters/markdown.rb +++ b/lib/jekyll/converters/markdown.rb @@ -10,6 +10,15 @@ module Jekyll return if @setup # Set the Markdown interpreter (and Maruku self.config, if necessary) case @config['markdown'] + when 'redcarpet' + begin + require 'redcarpet' + @redcarpet_extensions = @config['redcarpet']['extensions'].map { |e| e.to_sym } + rescue LoadError + STDERR.puts 'You are missing a library required for Markdown. Please run:' + STDERR.puts ' $ [sudo] gem install redcarpet' + raise FatalException.new("Missing dependency: redcarpet") + end when 'kramdown' begin require 'kramdown' @@ -65,9 +74,10 @@ module Jekyll end @setup = true end - + def matches(ext) - ext =~ /(markdown|mkdn?|md)/i + rgx = '(' + @config['markdown_ext'].gsub(',','|') +')' + ext =~ Regexp.new(rgx, Regexp::IGNORECASE) end def output_ext(ext) @@ -77,6 +87,8 @@ module Jekyll def convert(content) setup case @config['markdown'] + when 'redcarpet' + Redcarpet.new(content, *@redcarpet_extensions).to_html when 'kramdown' # Check for use of coderay if @config['kramdown']['use_coderay'] diff --git a/lib/jekyll/converters/textile.rb b/lib/jekyll/converters/textile.rb index 347ab197..6433dbb5 100644 --- a/lib/jekyll/converters/textile.rb +++ b/lib/jekyll/converters/textile.rb @@ -17,7 +17,8 @@ module Jekyll end def matches(ext) - ext =~ /textile/i + rgx = '(' + @config['textile_ext'].gsub(',','|') +')' + ext =~ Regexp.new(rgx, Regexp::IGNORECASE) end def output_ext(ext) diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index 1723fa0d..46dbfea1 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -1,3 +1,5 @@ +require 'set' + # Convertible provides methods for converting a pagelike item # from a certain type of markup into actual content # @@ -86,6 +88,8 @@ module Jekyll # recursively render layouts layout = layouts[self.data["layout"]] + used = Set.new([layout]) + while layout payload = payload.deep_merge({"content" => self.output, "page" => layout.data}) @@ -95,7 +99,13 @@ module Jekyll puts "Liquid Exception: #{e.message} in #{self.data["layout"]}" end - layout = layouts[layout.data["layout"]] + if layout = layouts[layout.data["layout"]] + if used.include?(layout) + layout = nil # avoid recursive chain + else + used << layout + end + end end end end diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb index dbd9f511..eb61d84a 100644 --- a/lib/jekyll/filters.rb +++ b/lib/jekyll/filters.rb @@ -9,7 +9,20 @@ module Jekyll # # Returns the HTML formatted String. def textilize(input) - TextileConverter.new.convert(input) + site = @context.registers[:site] + converter = site.getConverterImpl(Jekyll::TextileConverter) + converter.convert(input) + end + + # Convert a Markdown string into HTML output. + # + # input - The Markdown String to convert. + # + # Returns the HTML formatted String. + def markdownify(input) + site = @context.registers[:site] + converter = site.getConverterImpl(Jekyll::MarkdownConverter) + converter.convert(input) end # Format a date in short format e.g. "27 Jan 2011". diff --git a/lib/jekyll/migrators/tumblr.rb b/lib/jekyll/migrators/tumblr.rb index 0345b4a9..d7cb3969 100644 --- a/lib/jekyll/migrators/tumblr.rb +++ b/lib/jekyll/migrators/tumblr.rb @@ -65,7 +65,7 @@ module Jekyll content << "
" + line['label'] + "
" + line.inner_html + "
" unless line['label'] == nil || line == nil end - content << "
" + content << "
" elsif post['type'] == "video" title = post.at("video-title").inner_html unless post.at("video-title") == nil content = CGI::unescapeHTML(post.at("video-player").inner_html) diff --git a/lib/jekyll/migrators/typo.rb b/lib/jekyll/migrators/typo.rb index 3de5130a..adb8be96 100644 --- a/lib/jekyll/migrators/typo.rb +++ b/lib/jekyll/migrators/typo.rb @@ -2,6 +2,7 @@ require 'fileutils' require 'rubygems' require 'sequel' +require 'yaml' module Jekyll module Typo @@ -24,7 +25,7 @@ module Jekyll FileUtils.mkdir_p '_posts' db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8') db[SQL].each do |post| - next unless post[:state] =~ /Published/ + next unless post[:state] =~ /published/ name = [ sprintf("%.04d", post[:date].year), sprintf("%.02d", post[:date].month), diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index a4a0f9f3..82b82048 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -3,8 +3,9 @@ module Jekyll class Page include Convertible + attr_writer :dir attr_accessor :site, :pager - attr_accessor :name, :ext, :basename, :dir + attr_accessor :name, :ext, :basename attr_accessor :data, :content, :output # Initialize a new Page. diff --git a/lib/jekyll/plugin.rb b/lib/jekyll/plugin.rb index 208472f0..600f326d 100644 --- a/lib/jekyll/plugin.rb +++ b/lib/jekyll/plugin.rb @@ -34,6 +34,7 @@ module Jekyll # # Returns the Symbol priority. def self.priority(priority = nil) + @priority ||= nil if priority && PRIORITIES.has_key?(priority) @priority = priority end diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 41905d65..9c24f8e4 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -266,7 +266,7 @@ module Jekyll def post_attr_hash(post_attr) # Build a hash map based on the specified post attribute ( post attr => # array of posts ) then sort each array in reverse order. - hash = Hash.new { |hash, key| hash[key] = Array.new } + hash = Hash.new { |hsh, key| hsh[key] = Array.new } self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } } hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a } } hash @@ -314,5 +314,18 @@ module Jekyll end end + # Get the implementation class for the given Converter. + # + # klass - The Class of the Converter to fetch. + # + # Returns the Converter instance implementing the given Converter. + def getConverterImpl(klass) + matches = self.converters.select { |c| c.class == klass } + if impl = matches.first + impl + else + raise "Converter implementation not found for #{klass}" + end + end end end diff --git a/test/helper.rb b/test/helper.rb index 6ee3f7a4..c6a8a763 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -6,12 +6,12 @@ require 'jekyll' require 'RedCloth' require 'rdiscount' require 'kramdown' +require 'redcarpet' require 'redgreen' if RUBY_VERSION < '1.9' require 'shoulda' require 'rr' - include Jekyll # Send STDERR into the void to suppress program output messages diff --git a/test/source/_posts/2011-04-12-md-extension.md b/test/source/_posts/2011-04-12-md-extension.md new file mode 100644 index 00000000..163e9133 --- /dev/null +++ b/test/source/_posts/2011-04-12-md-extension.md @@ -0,0 +1,7 @@ +--- +date: 2011-04-12 13:07:09 +--- + +under default configuration, this post should get processed by the identity converter. By changing +textile extension or markdown extension configuration parameters, you should be able to associate +it with either of those converters \ No newline at end of file diff --git a/test/source/_posts/2011-04-12-text-extension.text b/test/source/_posts/2011-04-12-text-extension.text new file mode 100644 index 00000000..e69de29b diff --git a/test/test_filters.rb b/test/test_filters.rb index 4fa0b819..205d4bca 100644 --- a/test/test_filters.rb +++ b/test/test_filters.rb @@ -3,6 +3,11 @@ require 'helper' class TestFilters < Test::Unit::TestCase class JekyllFilter include Jekyll::Filters + + def initialize + site = Jekyll::Site.new(Jekyll.configuration({})) + @context = Liquid::Context.new({}, {}, { :site => site }) + end end context "filters" do @@ -14,6 +19,10 @@ class TestFilters < Test::Unit::TestCase assert_equal "

something really simple

", @filter.textilize("something *really* simple") end + should "markdownify with simple string" do + assert_equal "

something really simple

", @filter.markdownify("something **really** simple") + end + should "convert array to sentence string with no args" do assert_equal "", @filter.array_to_sentence_string([]) end diff --git a/test/test_generated_site.rb b/test/test_generated_site.rb index c7087031..9fb7a409 100644 --- a/test/test_generated_site.rb +++ b/test/test_generated_site.rb @@ -14,7 +14,7 @@ class TestGeneratedSite < Test::Unit::TestCase end should "ensure post count is as expected" do - assert_equal 26, @site.posts.size + assert_equal 28, @site.posts.size end should "insert site.posts into the index" do diff --git a/test/test_post.rb b/test/test_post.rb index 323ab7dc..1c3ea641 100644 --- a/test/test_post.rb +++ b/test/test_post.rb @@ -397,6 +397,47 @@ class TestPost < Test::Unit::TestCase post = Post.new(@site, File.join(File.dirname(__FILE__), *%w[source]), 'foo', 'bar/2008-12-12-topical-post.textile') assert_equal ['foo'], post.categories end - end + + context "converter file extension settings" do + setup do + stub(Jekyll).configuration { Jekyll::DEFAULTS } + @site = Site.new(Jekyll.configuration) + end + + should "process .md as markdown under default configuration" do + post = setup_post '2011-04-12-md-extension.md' + conv = post.converter + assert conv.kind_of? Jekyll::MarkdownConverter + end + + should "process .text as indentity under default configuration" do + post = setup_post '2011-04-12-text-extension.text' + conv = post.converter + assert conv.kind_of? Jekyll::IdentityConverter + end + + should "process .text as markdown under alternate configuration" do + @site.config['markdown_ext'] = 'markdown,mdw,mdwn,md,text' + post = setup_post '2011-04-12-text-extension.text' + conv = post.converter + assert conv.kind_of? Jekyll::MarkdownConverter + end + + should "process .md as markdown under alternate configuration" do + @site.config['markdown_ext'] = 'markdown,mkd,mkdn,md,text' + post = setup_post '2011-04-12-text-extension.text' + conv = post.converter + assert conv.kind_of? Jekyll::MarkdownConverter + end + + should "process .text as textile under alternate configuration" do + @site.config['textile_ext'] = 'textile,text' + post = setup_post '2011-04-12-text-extension.text' + conv = post.converter + assert conv.kind_of? Jekyll::TextileConverter + end + + end + end diff --git a/test/test_redcarpet.rb b/test/test_redcarpet.rb new file mode 100644 index 00000000..1359c449 --- /dev/null +++ b/test/test_redcarpet.rb @@ -0,0 +1,21 @@ +require File.dirname(__FILE__) + '/helper' + +class TestRedcarpet < Test::Unit::TestCase + context "redcarpet" do + setup do + config = { + 'redcarpet' => { 'extensions' => ['smart'] }, + 'markdown' => 'redcarpet' + } + @markdown = MarkdownConverter.new config + end + + should "pass redcarpet options" do + assert_equal "

Some Header

", @markdown.convert('# Some Header #').strip + end + + should "pass redcarpet extensions" do + assert_equal "

“smart”

", @markdown.convert('"smart"').strip + end + end +end diff --git a/test/test_tags.rb b/test/test_tags.rb index ba3d0d3e..50750757 100644 --- a/test/test_tags.rb +++ b/test/test_tags.rb @@ -125,5 +125,16 @@ CONTENT assert_match %r{FINISH HIM}, @result end end + + context "using Redcarpet" do + setup do + create_post(@content, 'markdown' => 'redcarpet') + end + + should "parse correctly" do + assert_match %r{FIGHT!}, @result + assert_match %r{FINISH HIM}, @result + end + end end end