diff --git a/History.markdown b/History.markdown index 27c262d8..6707a19e 100644 --- a/History.markdown +++ b/History.markdown @@ -7,6 +7,7 @@ * Remove support for Ruby 1.8.x (#1780) * Move to jekyll/jekyll from mojombo/jekyll (#1817) * Allow custom markdown processors (#1872) + * Provide support for the Rouge syntax highlighter (#1859) ### Minor Enhancements * Move the EntryFilter class into the Jekyll module to avoid polluting the @@ -37,6 +38,7 @@ * Add in History and site changes from `v1-stable` branch (#1836) * Testing additions on the Excerpt class (#1893) * Update Kramdown to `~> 1.3` (#1894) + * Fix the `highlight` tag feature (#1859) ### Site Enhancements * Document Kramdown's GFM parser option (#1791) diff --git a/features/site_configuration.feature b/features/site_configuration.feature index 0a08f753..a7c59b00 100644 --- a/features/site_configuration.feature +++ b/features/site_configuration.feature @@ -91,11 +91,19 @@ Feature: Site configuration And I should see "Google" in "_site/index.html" Scenario: Highlight code with pygments - Given I have an "index.html" file that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}" - And I have a configuration file with "pygments" set to "true" + Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}" When I run jekyll Then the _site directory should exist - And I should see "puts 'Hello world!'" in "_site/index.html" + And I should see "Hello world!" in "_site/index.html" + And I should see "class=\"highlight\"" in "_site/index.html" + + Scenario: Highlight code with rouge + Given I have an "index.html" page that contains "{% highlight ruby %} puts 'Hello world!' {% endhighlight %}" + And I have a configuration file with "highlighter" set to "rouge" + When I run jekyll + Then the _site directory should exist + And I should see "Hello world!" in "_site/index.html" + And I should see "class=\"highlight\"" in "_site/index.html" Scenario: Set time and no future dated posts Given I have a _layouts directory diff --git a/jekyll.gemspec b/jekyll.gemspec index 564c264d..06dc2e56 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -52,6 +52,7 @@ Gem::Specification.new do |s| s.add_development_dependency('activesupport', '~> 3.2.13') s.add_development_dependency('jekyll_test_plugin') s.add_development_dependency('jekyll_test_plugin_malicious') + s.add_development_dependency('rouge', '~> 1.3') # = MANIFEST = s.files = %w[ diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb index 340847c3..f5e10fe0 100644 --- a/lib/jekyll/configuration.rb +++ b/lib/jekyll/configuration.rb @@ -24,12 +24,12 @@ module Jekyll 'limit_posts' => 0, 'lsi' => false, 'future' => true, # remove and make true just default - 'pygments' => true, 'relative_permalinks' => true, # backwards-compatibility with < 1.0 # will be set to false once 2.0 hits 'markdown' => 'maruku', + 'highlighter' => 'pygments', 'permalink' => 'date', 'baseurl' => '/', 'include' => ['.htaccess'], @@ -210,6 +210,16 @@ module Jekyll config.delete('server_port') end + if config.has_key? 'pygments' + Jekyll.logger.warn "Deprecation:", "The 'pygments' configuration option" + + " has been renamed to 'highlighter'. Please update your" + + " config file accordingly. The allowed values are 'rouge', " + + "'pygments' or null." + + config['highlighter'] = 'pygments' if config['pygments'] + config.delete('pygments') + end + %w[include exclude].each do |option| if config.fetch(option, []).is_a?(String) Jekyll.logger.warn "Deprecation:", "The '#{option}' configuration option" + diff --git a/lib/jekyll/converter.rb b/lib/jekyll/converter.rb index e2dc2796..c30f4944 100644 --- a/lib/jekyll/converter.rb +++ b/lib/jekyll/converter.rb @@ -1,27 +1,27 @@ module Jekyll class Converter < Plugin - # Public: Get or set the pygments prefix. When an argument is specified, + # Public: Get or set the highlighter prefix. When an argument is specified, # the prefix will be set. If no argument is specified, the current prefix # will be returned. # - # pygments_prefix - The String prefix (default: nil). + # highlighter_prefix - The String prefix (default: nil). # # Returns the String prefix. - def self.pygments_prefix(pygments_prefix = nil) - @pygments_prefix = pygments_prefix if pygments_prefix - @pygments_prefix + def self.highlighter_prefix(highlighter_prefix = nil) + @highlighter_prefix = highlighter_prefix if highlighter_prefix + @highlighter_prefix end - # Public: Get or set the pygments suffix. When an argument is specified, + # Public: Get or set the highlighter suffix. When an argument is specified, # the suffix will be set. If no argument is specified, the current suffix # will be returned. # - # pygments_suffix - The String suffix (default: nil). + # highlighter_suffix - The String suffix (default: nil). # # Returns the String suffix. - def self.pygments_suffix(pygments_suffix = nil) - @pygments_suffix = pygments_suffix if pygments_suffix - @pygments_suffix + def self.highlighter_suffix(highlighter_suffix = nil) + @highlighter_suffix = highlighter_suffix if highlighter_suffix + @highlighter_suffix end # Initialize the converter. @@ -31,18 +31,18 @@ module Jekyll @config = config end - # Get the pygments prefix. + # Get the highlighter prefix. # # Returns the String prefix. - def pygments_prefix - self.class.pygments_prefix + def highlighter_prefix + self.class.highlighter_prefix end - # Get the pygments suffix. + # Get the highlighter suffix. # # Returns the String suffix. - def pygments_suffix - self.class.pygments_suffix + def highlighter_suffix + self.class.highlighter_suffix end end end diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb index 178eaaa8..f68af673 100644 --- a/lib/jekyll/converters/markdown.rb +++ b/lib/jekyll/converters/markdown.rb @@ -3,8 +3,8 @@ module Jekyll class Markdown < Converter safe true - pygments_prefix "\n" - pygments_suffix "\n" + highlighter_prefix "\n" + highlighter_suffix "\n" def setup return if @setup diff --git a/lib/jekyll/converters/markdown/redcarpet_parser.rb b/lib/jekyll/converters/markdown/redcarpet_parser.rb index 9af80571..7a805fe9 100644 --- a/lib/jekyll/converters/markdown/redcarpet_parser.rb +++ b/lib/jekyll/converters/markdown/redcarpet_parser.rb @@ -22,7 +22,7 @@ module Jekyll end end - module WithoutPygments + module WithoutHighlighting require 'cgi' include CommonMethods @@ -37,19 +37,50 @@ module Jekyll end end + module WithRouge + require 'rouge' + require 'rouge/plugins/redcarpet' + + if Rouge.version < '1.3.0' + abort "Please install Rouge 1.3.0 or greater and try running Jekyll again." + end + + include Rouge::Plugins::Redcarpet + include CommonMethods + + def block_code(code, lang) + code = "
#{super}
" + + output = "
" + output << add_code_tags(code, lang) + output << "
" + end + + protected + def rouge_formatter(opts = {}) + Rouge::Formatters::HTML.new(opts.merge(wrap: false)) + end + end + + def initialize(config) require 'redcarpet' @config = config @redcarpet_extensions = {} @config['redcarpet']['extensions'].each { |e| @redcarpet_extensions[e.to_sym] = true } - @renderer ||= if @config['pygments'] + @renderer ||= case @config['highlighter'] + when 'pygments' Class.new(Redcarpet::Render::HTML) do include WithPygments end + when 'rouge' + Class.new(Redcarpet::Render::HTML) do + include WithRouge + end else Class.new(Redcarpet::Render::HTML) do - include WithoutPygments + include WithoutHighlighting end end rescue LoadError diff --git a/lib/jekyll/converters/textile.rb b/lib/jekyll/converters/textile.rb index 54e93749..d05b2169 100644 --- a/lib/jekyll/converters/textile.rb +++ b/lib/jekyll/converters/textile.rb @@ -3,8 +3,8 @@ module Jekyll class Textile < Converter safe true - pygments_prefix '' - pygments_suffix '' + highlighter_prefix '' + highlighter_suffix '' def setup return if @setup diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index 4d8cb37f..397383e5 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -145,8 +145,8 @@ module Jekyll info = { :filters => [Jekyll::Filters], :registers => { :site => self.site, :page => payload['page'] } } # render and transform content (this becomes the final content of the object) - payload["pygments_prefix"] = converter.pygments_prefix - payload["pygments_suffix"] = converter.pygments_suffix + payload["highlighter_prefix"] = converter.highlighter_prefix + payload["highlighter_suffix"] = converter.highlighter_suffix self.content = self.render_liquid(self.content, payload, diff --git a/lib/jekyll/deprecator.rb b/lib/jekyll/deprecator.rb index 23af739d..7600cd29 100644 --- a/lib/jekyll/deprecator.rb +++ b/lib/jekyll/deprecator.rb @@ -9,8 +9,8 @@ module Jekyll arg_is_present? args, "--auto", "The switch '--auto' has been replaced with '--watch'." arg_is_present? args, "--no-auto", "To disable auto-replication, simply leave off \ the '--watch' switch." - arg_is_present? args, "--pygments", "The 'pygments' setting can only be set in \ - your config files." + arg_is_present? args, "--pygments", "The 'pygments'settings has been removed in \ + favour of 'highlighter'." arg_is_present? args, "--paginate", "The 'paginate' setting can only be set in your \ config files." arg_is_present? args, "--url", "The 'url' setting can only be set in your config files." diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 8fee59cf..95bb46f9 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -1,7 +1,7 @@ module Jekyll class Site attr_accessor :config, :layouts, :posts, :pages, :static_files, - :categories, :exclude, :include, :source, :dest, :lsi, :pygments, + :categories, :exclude, :include, :source, :dest, :lsi, :highlighter, :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts, :show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :gems @@ -13,7 +13,7 @@ module Jekyll def initialize(config) self.config = config.clone - %w[safe lsi pygments baseurl exclude include future show_drafts limit_posts keep_files gems].each do |opt| + %w[safe lsi highlighter baseurl exclude include future show_drafts limit_posts keep_files gems].each do |opt| self.send("#{opt}=", config[opt]) end diff --git a/lib/jekyll/tags/highlight.rb b/lib/jekyll/tags/highlight.rb index 2ea144df..8719511f 100644 --- a/lib/jekyll/tags/highlight.rb +++ b/lib/jekyll/tags/highlight.rb @@ -41,8 +41,11 @@ eos end def render(context) - if context.registers[:site].pygments + case context.registers[:site].highlighter + when 'pygments' render_pygments(context, super) + when 'rouge' + render_rouge(context, super) else render_codehighlighter(context, super) end @@ -58,9 +61,28 @@ eos @lang ) - output = context["pygments_prefix"] + output if context["pygments_prefix"] - output = output + context["pygments_suffix"] if context["pygments_suffix"] - output + output = context["highlighter_prefix"] + output if context["highlighter_prefix"] + output << context["highlighter_suffix"] if context["highlighter_suffix"] + + return output + end + + def render_rouge(context, code) + require 'rouge' + + linenos = @options.keys.include?('linenos') + lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText + formatter = Rouge::Formatters::HTML.new(line_numbers: linenos, wrap: false) + + pre = "
#{formatter.format(lexer.lex(code))}
" + + output = context["highlighter_prefix"] || "" + output << "
" + output << add_code_tags(pre, @lang) + output << "
" + output << context["highlighter_suffix"] if context["highlighter_suffix"] + + return output end def render_codehighlighter(context, code) diff --git a/lib/site_template/_config.yml b/lib/site_template/_config.yml index 85daa771..627c7898 100644 --- a/lib/site_template/_config.yml +++ b/lib/site_template/_config.yml @@ -1,3 +1,3 @@ name: Your New Jekyll Site markdown: redcarpet -pygments: true +highlighter: pygments diff --git a/site/_config.yml b/site/_config.yml index f14c4e05..fc1fd46d 100644 --- a/site/_config.yml +++ b/site/_config.yml @@ -1,4 +1,4 @@ -pygments: true +highlight: pygments relative_permalinks: false gauges_id: 503c5af6613f5d0f19000027 permalink: /news/:year/:month/:day/:title/ diff --git a/site/docs/configuration.md b/site/docs/configuration.md index f903adde..1c1b4938 100644 --- a/site/docs/configuration.md +++ b/site/docs/configuration.md @@ -288,7 +288,7 @@ encoding: nil future: true show_drafts: nil limit_posts: 0 -pygments: true +highlighter: pygments relative_permalinks: true @@ -363,7 +363,7 @@ Jekyll handles two special Redcarpet extensions: # ...ruby code ``` - With both fenced code blocks and pygments enabled, this will statically highlight the code; without pygments, it will add a `class="LANGUAGE"` attribute to the `` element, which can be used as a hint by various JavaScript code highlighting libraries. + With both fenced code blocks and highlighter enabled, this will statically highlight the code; without any syntax highlighter, it will add a `class="LANGUAGE"` attribute to the `` element, which can be used as a hint by various JavaScript code highlighting libraries. - `smart` --- This pseudo-extension turns on SmartyPants, which converts straight quotes to curly quotes and runs of hyphens to em (`---`) and en (`--`) dashes. All other extensions retain their usual names from Redcarpet, and no renderer options aside from `smart` can be specified in Jekyll. [A list of available extensions can be found in the Redcarpet README file.][redcarpet_extensions] Make sure you're looking at the README for the right version of Redcarpet: Jekyll currently uses v2.2.x, and extensions like `footnotes` and `highlight` weren't added until after version 3.0.0. The most commonly used extensions are: diff --git a/site/docs/installation.md b/site/docs/installation.md index 8fb82a0a..7e4bd570 100644 --- a/site/docs/installation.md +++ b/site/docs/installation.md @@ -66,9 +66,10 @@ Check out [the extras page](../extras/) for more information.
ProTip™: Enable Syntax Highlighting

If you’re the kind of person who is using Jekyll, then chances are you’ll - want to enable syntax highlighting using Pygments. You should really - check out how to do - that before you go any further. + want to enable syntax highlighting using Pygments + or Rouge. You should really + check out how to + do that before you go any farther.

diff --git a/site/docs/posts.md b/site/docs/posts.md index 88036bdb..e944885e 100644 --- a/site/docs/posts.md +++ b/site/docs/posts.md @@ -139,8 +139,8 @@ your `excerpt_separator` to `""`. ## Highlighting code snippets Jekyll also has built-in support for syntax highlighting of code snippets using -Pygments, and including a code snippet in any post is easy. Just use the -dedicated Liquid tag as follows: +either Pygments or Rouge, and including a code snippet in any post is easy. Just +use the dedicated Liquid tag as follows: {% highlight text %} {% raw %}{% highlight ruby %}{% endraw %} diff --git a/site/docs/templates.md b/site/docs/templates.md index 67b30123..6c34993b 100644 --- a/site/docs/templates.md +++ b/site/docs/templates.md @@ -230,8 +230,14 @@ These parameters are available via Liquid in the include: Jekyll has built in support for syntax highlighting of [over 100 languages](http://pygments.org/languages/) thanks to -[Pygments](http://pygments.org/). To use Pygments, you must have Python installed on your -system and set `pygments` to `true` in your site's configuration file. +[Pygments](http://pygments.org/). To use Pygments, you must have Python installed +on your system and set `highlighter` to `pygments` in your site's configuration +file. + +Alternatively, you can use [Rouge](https://github.com/jayferd/rouge) to highlight +your code snippets. It doesn't support as many languages as Pygments does but +it should fit in most cases and it's written in pure Ruby ; you don't need Python +on your system! To render a code block with syntax highlighting, surround your code as follows: @@ -247,8 +253,9 @@ end The argument to the `highlight` tag (`ruby` in the example above) is the language identifier. To find the appropriate identifier to use for the language -you want to highlight, look for the “short name” on the [Lexers -page](http://pygments.org/docs/lexers/). +you want to highlight, look for the “short name” on the [Pygments' Lexers +page](http://pygments.org/docs/lexers/) or the [Rouge +wiki](https://github.com/jayferd/rouge/wiki/List-of-supported-languages-and-lexers). #### Line numbers @@ -288,7 +295,7 @@ will generate the correct permalink URL for the post you specify. {% endraw %} {% endhighlight %} -If you organize your posts in subdirectories, you need to include subdirectory +If you organize your posts in subdirectories, you need to include subdirectory path to the post: {% highlight text %} diff --git a/site/docs/troubleshooting.md b/site/docs/troubleshooting.md index e481af6b..c27c1665 100644 --- a/site/docs/troubleshooting.md +++ b/site/docs/troubleshooting.md @@ -123,8 +123,8 @@ bug](http://aaronqian.com/articles/2009/04/07/redcloth-ate-my-notextile.html) and will hopefully be fixed for 4.2. You can still use 4.1.9, but the test suite requires that 4.1.0 be installed. If you use a version of RedCloth that does not have the notextile tag, you may notice that -syntax highlighted blocks from Pygments are not formatted correctly, -among other things. If you’re seeing this just install 4.1.0. +syntax highlighted blocks from Pygments or Rouge are not formatted +correctly, among other things. If you’re seeing this just install 4.1.0. ### Liquid diff --git a/test/test_configuration.rb b/test/test_configuration.rb index d911b779..36e6cb1d 100644 --- a/test/test_configuration.rb +++ b/test/test_configuration.rb @@ -51,11 +51,12 @@ class TestConfiguration < Test::Unit::TestCase context "#backwards_compatibilize" do setup do @config = Configuration[{ - "auto" => true, - "watch" => true, - "server" => true, - "exclude" => "READ-ME.md, Gemfile,CONTRIBUTING.hello.markdown", - "include" => "STOP_THE_PRESSES.txt,.heloses, .git" + "auto" => true, + "watch" => true, + "server" => true, + "exclude" => "READ-ME.md, Gemfile,CONTRIBUTING.hello.markdown", + "include" => "STOP_THE_PRESSES.txt,.heloses, .git", + "pygments" => true, }] end should "unset 'auto' and 'watch'" do @@ -78,6 +79,11 @@ class TestConfiguration < Test::Unit::TestCase assert @config.backwards_compatibilize.has_key?("include") assert_equal @config.backwards_compatibilize["include"], %w[STOP_THE_PRESSES.txt .heloses .git] end + should "set highlighter to pygments" do + assert @config.has_key?("pygments") + assert !@config.backwards_compatibilize.has_key?("pygments") + assert_equal @config.backwards_compatibilize["highlighter"], "pygments" + end end context "#fix_common_issues" do setup do diff --git a/test/test_redcarpet.rb b/test/test_redcarpet.rb index c6e8b922..dabcd62c 100644 --- a/test/test_redcarpet.rb +++ b/test/test_redcarpet.rb @@ -28,7 +28,7 @@ class TestRedcarpet < Test::Unit::TestCase context "with pygments enabled" do setup do - @markdown = Converters::Markdown.new @config.merge({ 'pygments' => true }) + @markdown = Converters::Markdown.new @config.merge({ 'highlighter' => 'pygments' }) end should "render fenced code blocks with syntax highlighting" do @@ -42,9 +42,25 @@ puts "Hello world" end end - context "with pygments disabled" do + context "with rouge enabled" do setup do - @markdown = Converters::Markdown.new @config.merge({ 'pygments' => false }) + @markdown = Converters::Markdown.new @config.merge({ 'highlighter' => 'rouge' }) + end + + should "render fenced code blocks with syntax highlighting" do + assert_equal "
puts \"Hello world\"\n
", @markdown.convert( + <<-EOS +```ruby +puts "Hello world" +``` + EOS + ).strip + end + end + + context "without any highlighter" do + setup do + @markdown = Converters::Markdown.new @config.merge({ 'highlighter' => nil }) end should "render fenced code blocks without syntax highlighting" do diff --git a/test/test_tags.rb b/test/test_tags.rb index 3d9abd1b..8ecaf19b 100644 --- a/test/test_tags.rb +++ b/test/test_tags.rb @@ -6,7 +6,7 @@ class TestTags < Test::Unit::TestCase def create_post(content, override = {}, converter_class = Jekyll::Converters::Markdown) stub(Jekyll).configuration do - Jekyll::Configuration::DEFAULTS.deep_merge({'pygments' => true}).deep_merge(override) + Jekyll::Configuration::DEFAULTS.deep_merge({'highlighter' => 'pygments'}).deep_merge(override) end site = Site.new(Jekyll.configuration) @@ -16,8 +16,8 @@ class TestTags < Test::Unit::TestCase info = { :filters => [Jekyll::Filters], :registers => { :site => site } } @converter = site.converters.find { |c| c.class == converter_class } - payload = { "pygments_prefix" => @converter.pygments_prefix, - "pygments_suffix" => @converter.pygments_suffix } + payload = { "highlighter_prefix" => @converter.highlighter_prefix, + "highlighter_suffix" => @converter.highlighter_suffix } @result = Liquid::Template.parse(content).render!(payload, info) @result = @converter.convert(@result)