[DRE-commits] [ruby-org] 146/303: Add feature to colorize #+BEGIN_SRC blocks with Pygments or CodeRay

Jérémy Bobbio lunar at alioth.debian.org
Fri Aug 9 17:33:46 UTC 2013


This is an automated email from the git hooks/post-receive script.

lunar pushed a commit to branch master
in repository ruby-org.

commit 8dc7348834efa04a2b3e5963b66755828421d1c7
Author: Waldemar Quevedo <waldemar.quevedo at gmail.com>
Date:   Sun Jun 10 19:17:41 2012 +0900

    Add feature to colorize #+BEGIN_SRC blocks with Pygments or CodeRay
---
 Gemfile                               |    8 +++
 lib/org-ruby/html_output_buffer.rb    |   55 ++++++++++++++++-
 lib/org-ruby/line.rb                  |    7 ++-
 lib/org-ruby/output_buffer.rb         |   16 ++++-
 lib/org-ruby/parser.rb                |   24 +++++++-
 spec/html_examples/advanced-code.html |   81 -------------------------
 spec/html_examples/advanced-code.org  |  106 ---------------------------------
 spec/parser_spec.rb                   |   38 ++++++++++++
 tasks/test_case.rake                  |   60 +++++++++++++++++++
 9 files changed, 199 insertions(+), 196 deletions(-)

diff --git a/Gemfile b/Gemfile
index 36a0b4a..68eade4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,3 +6,11 @@ group :development do
   gem 'rspec'
   gem 'tilt'
 end
+
+group :coderay do
+  gem 'coderay'
+end
+
+group :pygments do
+  gem 'pygments.rb'
+end
diff --git a/lib/org-ruby/html_output_buffer.rb b/lib/org-ruby/html_output_buffer.rb
index aa67253..a58de14 100644
--- a/lib/org-ruby/html_output_buffer.rb
+++ b/lib/org-ruby/html_output_buffer.rb
@@ -1,6 +1,17 @@
 require OrgRuby.libpath(*%w[org-ruby html_symbol_replace])
 require OrgRuby.libpath(*%w[org-ruby output_buffer])
 
+begin
+  require 'pygments'
+rescue LoadError
+  # Pygments is not supported so we try instead with CodeRay
+  begin
+    require 'coderay'
+  rescue LoadError
+    # No code syntax highlighting
+  end
+end
+
 module Orgmode
 
   class HtmlOutputBuffer < OutputBuffer
@@ -86,13 +97,39 @@ module Orgmode
     end
 
     def flush!
-      escape_buffer!
-      if mode_is_code(@buffer_mode) then
+      if buffer_mode_is_src_block?
+
+        # Only try to colorize #+BEGIN_SRC blocks with a specified language,
+        # but we still have to catch the cases when a lexer for the language was not available
+        if not @block_lang.empty? and (defined? CodeRay or defined? Pygments)
+          # NOTE: CodeRay and Pygments already escape the html once, so no need to escape_buffer!
+          if defined? CodeRay
+            # CodeRay might throw a warning when unsupported lang is set
+            silence_warnings do
+              @buffer = CodeRay.scan(@buffer, @block_lang.to_s).html(:wrap => nil, :css => :style)
+            end
+          elsif defined? Pygments
+            begin
+              @buffer = Pygments.highlight(@buffer, :lexer => @block_lang)
+            rescue ::RubyPython::PythonError
+              # Not supported lexer from Pygments
+            end
+          end
+        else
+          escape_buffer!
+        end
+
+        @logger.debug "FLUSH SRC CODE ==========> #{@buffer.inspect}"
+        @output << @buffer
+      elsif mode_is_code(@buffer_mode) then
+        escape_buffer!
+
         # Whitespace is significant in :code mode. Always output the buffer
         # and do not do any additional translation.
         @logger.debug "FLUSH CODE ==========> #{@buffer.inspect}"
         @output << @buffer << "\n"
       else
+        escape_buffer!
         if @buffer.length > 0 and @output_type == :horizontal_rule then
           @output << "<hr />\n"
         elsif @buffer.length > 0 and @output_type == :definition_list then
@@ -166,6 +203,10 @@ module Orgmode
       @buffer_mode == :table
     end
 
+    def buffer_mode_is_src_block?
+      @buffer_mode == :src
+    end
+
     # Escapes any HTML content in the output accumulation buffer @buffer.
     def escape_buffer!
       @buffer.gsub!(/&/, "&")
@@ -245,5 +286,15 @@ module Orgmode
       Orgmode.special_symbols_to_html(str)
       str
     end
+
+    # Helper method taken from Rails
+    # https://github.com/rails/rails/blob/c2c8ef57d6f00d1c22743dc43746f95704d67a95/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L10
+    def silence_warnings
+      warn_level = $VERBOSE
+      $VERBOSE = nil
+      yield
+    ensure
+      $VERBOSE = warn_level
+    end
   end                           # class HtmlOutputBuffer
 end                             # module Orgmode
diff --git a/lib/org-ruby/line.rb b/lib/org-ruby/line.rb
index db6f233..848cc7c 100644
--- a/lib/org-ruby/line.rb
+++ b/lib/org-ruby/line.rb
@@ -164,10 +164,14 @@ module Orgmode
       $3 if @line =~ BlockRegexp
     end
 
-    def code_block_type?
+    def code_block?
       block_type =~ /^(EXAMPLE|SRC)$/i
     end
 
+    def code_block_line?
+      @assigned_paragraph_type == :src
+    end
+
     InlineExampleRegexp = /^\s*:\s/
 
     # Test if the line matches the "inline example" case:
@@ -200,6 +204,7 @@ module Orgmode
     # Determines the paragraph type of the current line.
     def paragraph_type
       return :blank if blank?
+      return :src if code_block_line? # Do not try to guess the type of this line if it is accumulating source code
       return :definition_list if definition_list? # order is important! A definition_list is also an unordered_list!
       return :ordered_list if ordered_list?
       return :unordered_list if unordered_list?
diff --git a/lib/org-ruby/output_buffer.rb b/lib/org-ruby/output_buffer.rb
index aedaa59..630e072 100644
--- a/lib/org-ruby/output_buffer.rb
+++ b/lib/org-ruby/output_buffer.rb
@@ -78,9 +78,16 @@ module Orgmode
     # Prepares the output buffer to receive content from a line.
     # As a side effect, this may flush the current accumulated text.
     def prepare(line)
-      @logger.debug "Looking at #{line.paragraph_type}: #{line.to_s}"
-      if not should_accumulate_output?(line) then
-        @block_lang = line.block_lang if line.begin_block? and line.code_block_type?
+      @logger.debug "Looking at #{line.paragraph_type}(#{current_mode}) : #{line.to_s}"
+      if line.begin_block? and line.code_block?
+        flush!
+        # We try to get the lang from #+BEGIN_SRC blocks
+        @block_lang = line.block_lang
+        @output_type = line.paragraph_type
+      elsif current_mode == :example and line.end_block?
+        flush!
+        @output_type = line.paragraph_type
+      elsif not should_accumulate_output?(line)
         flush!
         maintain_list_indent_stack(line)
         @output_type = line.paragraph_type 
@@ -207,6 +214,9 @@ module Orgmode
     # line breaks.)
     def should_accumulate_output?(line)
 
+      # Special case: We are accumulating source code block content for colorizing
+      return true if line.paragraph_type == :src and @output_type == :src
+
       # Special case: Preserve line breaks in block code mode.
       return false if preserve_whitespace?
 
diff --git a/lib/org-ruby/parser.rb b/lib/org-ruby/parser.rb
index 06a1870..1c5891e 100644
--- a/lib/org-ruby/parser.rb
+++ b/lib/org-ruby/parser.rb
@@ -120,7 +120,8 @@ module Orgmode
               end
             end
             table_header_set = false if !line.table?
-            mode = :code if line.begin_block? and line.block_type == "EXAMPLE"
+            mode = :code if line.begin_block? and line.block_type.casecmp("EXAMPLE") == 0
+            mode = :src_code if line.begin_block? and line.block_type.casecmp("SRC") == 0
             mode = :block_comment if line.begin_block? and line.block_type == "COMMENT"
             mode = :property_drawer if line.property_drawer_begin_block?
             if (@current_headline) then
@@ -143,7 +144,7 @@ module Orgmode
           # As long as we stay in code mode, force lines to be either blank or paragraphs.
           # Don't try to interpret structural items, like headings and tables.
           line = Line.new line, self
-          if line.end_block? and line.block_type == "EXAMPLE"
+          if line.end_block? and line.code_block?
             mode = :normal
           else
             line.assigned_paragraph_type = :paragraph unless line.blank?
@@ -154,6 +155,20 @@ module Orgmode
             @header_lines << line
           end
 
+        when :src_code
+
+          line = Line.new line, self
+          if line.end_block? and line.code_block?
+            mode = :normal            
+          else
+            line.assigned_paragraph_type = :src
+          end
+          if (@current_headline) then
+            @current_headline.body_lines << line
+          else
+            @header_lines << line
+          end
+
         when :property_drawer
 
           line = Line.new line, self
@@ -270,8 +285,11 @@ module Orgmode
 
           output_buffer << line.output_text
 
-        else
+       when :src
+
+         output_buffer << line.output_text << "\n"
 
+        else
           if output_buffer.preserve_whitespace? then
             output_buffer << line.output_text
           else
diff --git a/spec/html_examples/advanced-code.html b/spec/html_examples/advanced-code.html
deleted file mode 100644
index d19933c..0000000
--- a/spec/html_examples/advanced-code.html
+++ /dev/null
@@ -1,81 +0,0 @@
-<p class="title">advanced-code.org</p>
-<p>Turns out there’s more way to do code than just BEGIN_EXAMPLE.</p>
-<h1><span class="heading-number heading-number-1">1 </span>Inline examples</h1>
-<p>This should work:</p>
-<pre class="example">
-  fixed width? how does this work?   
-                        ...........
-                       ............
-                                  .
-                       .  .   .   .
-                       .          ..
-                       ....... .....
-                           .  .
-                           ....
-</pre>
-<p>Two ASCII blobs.</p>
-<h1><span class="heading-number heading-number-1">2 </span>BEGIN_SRC</h1>
-<p>And this:</p>
-<pre class="src">
-<code class="ruby">
-    # Finds all emphasis matches in a string.
-    # Supply a block that will get the marker and body as parameters.
-    def match_all(str)
-      str.scan(@org_emphasis_regexp) do |match|
-        yield $2, $3
-      end
-    end
-</code>
-</pre>
-<p>Now let’s test case-insensitive code blocks.</p>
-<pre class="src">
-<code class="ruby">
-    # Finds all emphasis matches in a string.
-    # Supply a block that will get the marker and body as parameters.
-    def match_all(str)
-      str.scan(@org_emphasis_regexp) do |match|
-        yield $2, $3
-      end
-    end
-</code>
-</pre>
-<pre class="src">
-<code class="clojure">
-(def fib-seq
-  (concat
-   [0 1]
-   ((fn rfib [a b]
-        (lazy-cons (+ a b) (rfib b (+ a b)))) 0 1)))
- 
-user> (take 20 fib-seq)
-(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)
-</code>
-</pre>
-<p>Even if no language is set, it is still wrapped in code tags but class is empty.</p>
-<pre class="src">
-<code class="">
-echo 'Defaults env_keeps="http_proxy https_proxy ftp_proxy"' | sudo tee -a /etc/sudoers
-</code>
-</pre>
-<h1><span class="heading-number heading-number-1">3 </span>It should be possible to write a colon at the beginning of an example</h1>
-<blockquote>
-  <p>I really love to write about :symbols. They sure are the best things in the world!</p>
-</blockquote>
-<pre class="src">
-<code class="ruby">
-{
-:one => 1,
-:two => 2
-}
-</code>
-</pre>
-<pre class="src">
-<code class="clojure">
-(defproject helloworld "0.1"
-:dependencies [[org.clojure/clojure
-                 "1.1.0-master-SNAPSHOT"]
-              [org.clojure/clojure-contrib
-                 "1.0-SNAPSHOT"]]
-:main helloworld)
-</code>
-</pre>
diff --git a/spec/html_examples/advanced-code.org b/spec/html_examples/advanced-code.org
deleted file mode 100644
index 6e4205b..0000000
--- a/spec/html_examples/advanced-code.org
+++ /dev/null
@@ -1,106 +0,0 @@
-#+TITLE:     advanced-code.org
-#+AUTHOR:    Brian Dewey
-#+EMAIL:     bdewey at gmail.com
-#+DATE:      2009-12-30 Wed
-#+DESCRIPTION: More types of code support
-#+KEYWORDS: 
-#+LANGUAGE:  en
-#+OPTIONS:   H:3 num:t toc:nil \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
-#+OPTIONS:   TeX:t LaTeX:nil skip:nil d:nil todo:t pri:nil tags:not-in-toc
-#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
-#+EXPORT_SELECT_TAGS: export
-#+EXPORT_EXCLUDE_TAGS: noexport
-#+LINK_UP:   
-#+LINK_HOME: 
-
-Turns out there's more way to do code than just BEGIN_EXAMPLE. 
-
-* Inline examples
-
-This should work:
-
-:   fixed width? how does this work?   
-:                         ...........
-:                        ............
-:                                   .
-:                        .  .   .   .
-:                        .          ..
-:                        ....... .....
-:                            .  .
-:                            ....
-
-Two ASCII blobs.
-
-* BEGIN_SRC
-:PROPERTIES:
-:ARCHIVE_TIME: 2009-12-26 Sat 22:16
-:ARCHIVE_FILE: ~/brians-brain/content/projects/orgmode_parser.org
-:ARCHIVE_OLPATH: <%= @page.title %>/Future Development
-:ARCHIVE_CATEGORY: orgmode_parser
-:ARCHIVE_TODO: DONE
-:END:
-
-And this:
-
-#+BEGIN_SRC ruby
-    # Finds all emphasis matches in a string.
-    # Supply a block that will get the marker and body as parameters.
-    def match_all(str)
-      str.scan(@org_emphasis_regexp) do |match|
-        yield $2, $3
-      end
-    end
-#+END_SRC
-
-Now let's test case-insensitive code blocks.
-
-#+begin_src ruby
-    # Finds all emphasis matches in a string.
-    # Supply a block that will get the marker and body as parameters.
-    def match_all(str)
-      str.scan(@org_emphasis_regexp) do |match|
-        yield $2, $3
-      end
-    end
-#+end_src
-
-#+begin_src clojure
-(def fib-seq
-  (concat
-   [0 1]
-   ((fn rfib [a b]
-        (lazy-cons (+ a b) (rfib b (+ a b)))) 0 1)))
- 
-user> (take 20 fib-seq)
-(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)
-#+end_src
-
-Even if no language is set, it is still wrapped in code tags but class is empty.
-
-#+BEGIN_SRC 
-echo 'Defaults env_keeps="http_proxy https_proxy ftp_proxy"' | sudo tee -a /etc/sudoers
-#+END_SRC
-
-* It should be possible to write a colon at the beginning of an example
-
-#+BEGIN_QUOTE
-I really love to write about
-:symbols. They sure are the
-best things in the world!
-#+END_QUOTE
-
-#+BEGIN_SRC ruby
-{
-:one => 1,
-:two => 2
-}
-#+END_SRC
-
-#+BEGIN_SRC clojure
-(defproject helloworld "0.1"
-:dependencies [[org.clojure/clojure
-                 "1.1.0-master-SNAPSHOT"]
-              [org.clojure/clojure-contrib
-                 "1.0-SNAPSHOT"]]
-:main helloworld)
-#+END_SRC
diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb
index f22ba5f..83929da 100644
--- a/spec/parser_spec.rb
+++ b/spec/parser_spec.rb
@@ -154,5 +154,43 @@ describe Orgmode::Parser do
       end
     end
   end
+
+  describe "Export to HTML test cases with code syntax highlight" do
+    code_syntax_examples_directory = File.join(File.dirname(__FILE__), "html_code_syntax_highlight_examples")
+
+    # Include the code syntax highlight support tests
+    if defined? CodeRay
+      # Use CodeRay for syntax highlight (pure Ruby solution)
+      org_files = File.expand_path(File.join(code_syntax_examples_directory, "*-coderay.org"))
+    elsif defined? Pygments
+      # Use pygments (so that it works with Jekyll, Gollum and possibly Github)
+      org_files = File.expand_path(File.join(code_syntax_examples_directory, "*-pygments.org"))
+    else
+      # Do not use syntax coloring for source code blocks
+      org_files = File.expand_path(File.join(code_syntax_examples_directory, "*-no-color.org"))
+    end
+    files = Dir.glob(org_files)
+
+    files.each do |file|
+      basename = File.basename(file, ".org")
+      textile_name = File.join(code_syntax_examples_directory, basename + ".html")
+      textile_name = File.expand_path(textile_name)
+
+      it "should convert #{basename}.org to HTML" do
+        expected = IO.read(textile_name)
+        expected.should be_kind_of(String)
+        parser = Orgmode::Parser.new(IO.read(file))
+        actual = parser.to_html
+        actual.should be_kind_of(String)
+        actual.should == expected
+      end
+
+      it "should render #{basename}.org to HTML using Tilt templates" do
+        expected = IO.read(textile_name)
+        template = Tilt.new(file).render
+        template.should == expected
+      end
+    end
+  end
 end
 
diff --git a/tasks/test_case.rake b/tasks/test_case.rake
index 451362b..ac2bdc2 100644
--- a/tasks/test_case.rake
+++ b/tasks/test_case.rake
@@ -42,8 +42,68 @@ namespace :testcase do
     p = Orgmode::Parser.new(data)
     puts p.to_html
   end
+
+  # Special namespace to test syntax highlighting with different technologies
+  namespace :highlight do
+    @code_syntax_examples_directory = File.join(File.dirname(__FILE__), "../spec/html_code_syntax_highlight_examples")
+
+    desc "List all of the current HTML test cases"
+    task :list do
+      org_files = File.expand_path(File.join(@code_syntax_examples_directory, "*.org" ))
+      files = Dir.glob(org_files)
+      files.each do |file|
+        puts File.basename(file, ".org")
+      end
+    end
+
+    desc "Special tests cases for code syntax highlight support"
+    task :accept, :case do |t, args|
+      basename = args[:case]
+      raise "Must supply a test case name. Example: rake testcase:accept[casename]" unless basename
+
+      fname = File.expand_path(File.join(@code_syntax_examples_directory, "#{basename}.org"))
+      oname = File.expand_path(File.join(@code_syntax_examples_directory, "#{basename}.html"))
+
+      data = IO.read(fname)
+      puts "=== #{fname} is:          ===>>>\n\n"
+      puts data
+      puts "\n\n=== ACCEPTING OUTPUT: ===>>>\n\n"
+      p = Orgmode::Parser.new(data)
+      puts p.to_html
+      File.open(oname, "w") do |s|
+        s.write(p.to_html)
+      end
+    end
+
+    desc "Inspect code syntax highlight support"
+    task :inspect, :case do |t, args|
+      basename = args[:case]
+      raise "Must supply a test case name. Example: rake testcase:inspecthighlight[casename]" unless basename
+
+      fname = File.expand_path(File.join(@code_syntax_examples_directory, "#{basename}.org"))
+
+      data = IO.read(fname)
+      puts "=== #{fname} is:          ===>>>\n\n"
+      puts data
+      puts "\n\n=== #{fname} converts to: ===>>>\n\n"
+      p = Orgmode::Parser.new(data)
+      puts p.to_html
+    end
+  end
+
 end
 
 desc "Alias for testcase:list"
 task :testcase => ["testcase:list"]
 
+task :test do
+  puts "Testing without CodeRay nor Pygments for code syntax highlight"
+  system('bundle --without pygments:coderay > /dev/null 2>&1')
+  system('bundle exec rake spec')
+  puts "Testing with CodeRay for code syntax highlight"
+  system('bundle --without pygments > /dev/null 2>&1')
+  system('bundle exec rake spec')
+  puts "Testing with Pygments for code syntax highlight"
+  system('bundle --without coderay > /dev/null 2>&1')
+  system('bundle exec rake spec')
+end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-org.git



More information about the Pkg-ruby-extras-commits mailing list