diff --git a/doc/config.yml.sample b/doc/config.yml.sample index 5550ca1af..a9c10fe56 100644 --- a/doc/config.yml.sample +++ b/doc/config.yml.sample @@ -172,9 +172,9 @@ secnolevel: 2 # fontdir内から取り込まれる対象となるファイル拡張子。省略した場合は以下 # font_ext: ["ttf", "woff", "otf"] -# ソースコードハイライトを利用する (pygmentsには外部gemが必要) +# ソースコードハイライトを利用する (rouge,pygmentsには外部gemが必要) # highlight: -# html: "pygments" +# html: "rouge" # latex: "listings" # カタログファイル名を指定する diff --git a/doc/config.yml.sample-simple b/doc/config.yml.sample-simple index 8e40dcd8e..4f2b98e0c 100644 --- a/doc/config.yml.sample-simple +++ b/doc/config.yml.sample-simple @@ -51,7 +51,7 @@ colophon: true ## Syntax Highlighting highlight: - html: "pygments" + html: "rouge" latex: "listings" ## for HTML diff --git a/doc/sample.css b/doc/sample.css index 909554813..1e4d2d966 100644 --- a/doc/sample.css +++ b/doc/sample.css @@ -107,6 +107,220 @@ p.flushright { text-align: right; } +/** + * from Rouge + */ +.highlight table td { padding: 5px; } +.highlight table pre { margin: 0; } +.highlight .cm { + color: #999988; + font-style: italic; +} +.highlight .cp { + color: #999999; + font-weight: bold; +} +.highlight .c1 { + color: #999988; + font-style: italic; +} +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; +} +.highlight .c, .highlight .cd { + color: #999988; + font-style: italic; +} +.highlight .err { + color: #a61717; + background-color: #e3d2d2; +} +.highlight .gd { + color: #000000; + background-color: #ffdddd; +} +.highlight .ge { + color: #000000; + font-style: italic; +} +.highlight .gr { + color: #aa0000; +} +.highlight .gh { + color: #999999; +} +.highlight .gi { + color: #000000; + background-color: #ddffdd; +} +.highlight .go { + color: #888888; +} +.highlight .gp { + color: #555555; +} +.highlight .gs { + font-weight: bold; +} +.highlight .gu { + color: #aaaaaa; +} +.highlight .gt { + color: #aa0000; +} +.highlight .kc { + color: #000000; + font-weight: bold; +} +.highlight .kd { + color: #000000; + font-weight: bold; +} +.highlight .kn { + color: #000000; + font-weight: bold; +} +.highlight .kp { + color: #000000; + font-weight: bold; +} +.highlight .kr { + color: #000000; + font-weight: bold; +} +.highlight .kt { + color: #445588; + font-weight: bold; +} +.highlight .k, .highlight .kv { + color: #000000; + font-weight: bold; +} +.highlight .mf { + color: #009999; +} +.highlight .mh { + color: #009999; +} +.highlight .il { + color: #009999; +} +.highlight .mi { + color: #009999; +} +.highlight .mo { + color: #009999; +} +.highlight .m, .highlight .mb, .highlight .mx { + color: #009999; +} +.highlight .sb { + color: #d14; +} +.highlight .sc { + color: #d14; +} +.highlight .sd { + color: #d14; +} +.highlight .s2 { + color: #d14; +} +.highlight .se { + color: #d14; +} +.highlight .sh { + color: #d14; +} +.highlight .si { + color: #d14; +} +.highlight .sx { + color: #d14; +} +.highlight .sr { + color: #009926; +} +.highlight .s1 { + color: #d14; +} +.highlight .ss { + color: #990073; +} +.highlight .s { + color: #d14; +} +.highlight .na { + color: #008080; +} +.highlight .bp { + color: #999999; +} +.highlight .nb { + color: #0086B3; +} +.highlight .nc { + color: #445588; + font-weight: bold; +} +.highlight .no { + color: #008080; +} +.highlight .nd { + color: #3c5d5d; + font-weight: bold; +} +.highlight .ni { + color: #800080; +} +.highlight .ne { + color: #990000; + font-weight: bold; +} +.highlight .nf { + color: #990000; + font-weight: bold; +} +.highlight .nl { + color: #990000; + font-weight: bold; +} +.highlight .nn { + color: #555555; +} +.highlight .nt { + color: #000080; +} +.highlight .vc { + color: #008080; +} +.highlight .vg { + color: #008080; +} +.highlight .vi { + color: #008080; +} +.highlight .nv { + color: #008080; +} +.highlight .ow { + color: #000000; + font-weight: bold; +} +.highlight .o { + color: #000000; + font-weight: bold; +} +.highlight .w { + color: #bbbbbb; +} +.highlight { + background-color: #f8f8f8; +} +.rouge-table { border-spacing: 0 } +.rouge-gutter { text-align: right } /** * from EBPAJ EPUB 3 File Creation Guide sample style diff --git a/lib/review/htmlbuilder.rb b/lib/review/htmlbuilder.rb index 918ca8acd..3b713645a 100644 --- a/lib/review/htmlbuilder.rb +++ b/lib/review/htmlbuilder.rb @@ -450,6 +450,7 @@ def list_body(id, lines, lang) class_names = ["list"] lexer = lang || File.extname(id).gsub(/\./, '') class_names.push("language-#{lexer}") unless lexer.blank? + class_names.push("highlight") if highlight? print %Q[
] body = lines.inject(''){|i, j| i + detab(j) + "\n"} puts highlight(:body => body, :lexer => lexer, :format => 'html') @@ -494,11 +495,12 @@ def listnum_body(lines, lang) body = lines.inject(''){|i, j| i + detab(j) + "\n"} lexer = lang first_line_number = get_line_num - puts highlight(:body => body, :lexer => lexer, :format => 'html', - :options => {:linenos => 'inline', :nowrap => false, :linenostart => first_line_number}) + puts highlight(:body => body, :lexer => lexer, :format => 'html', :linenum => true, + :options => {:linenostart => first_line_number}) else class_names = ["list"] class_names.push("language-#{lang}") unless lang.blank? + class_names.push("highlight") if highlight? print %Q[] first_line_num = get_line_num lines.each_with_index do |line, i| @@ -515,6 +517,7 @@ def emlist(lines, caption = nil, lang = nil) end class_names = ["emlist"] class_names.push("language-#{lang}") unless lang.blank? + class_names.push("highlight") if highlight? print %Q[] body = lines.inject(''){|i, j| i + detab(j) + "\n"} lexer = lang @@ -533,11 +536,12 @@ def emlistnum(lines, caption = nil, lang = nil) body = lines.inject(''){|i, j| i + detab(j) + "\n"} lexer = lang first_line_number = get_line_num - puts highlight(:body => body, :lexer => lexer, :format => 'html', - :options => {:linenos => 'inline', :nowrap => false, :linenostart => first_line_number}) + puts highlight(:body => body, :lexer => lexer, :format => 'html', :linenum => true, + :options => {:linenostart => first_line_number}) else class_names = ["emlist"] class_names.push("language-#{lang}") unless lang.blank? + class_names.push("highlight") if highlight? print %Q[] first_line_num = get_line_num lines.each_with_index do |line, i| diff --git a/lib/review/htmlutils.rb b/lib/review/htmlutils.rb index 8aef9edf2..9442de40c 100644 --- a/lib/review/htmlutils.rb +++ b/lib/review/htmlutils.rb @@ -9,6 +9,7 @@ # require 'cgi/util' +require 'rouge' module ReVIEW module HTMLUtils @@ -44,14 +45,25 @@ def escape_comment(str) def highlight? @book.config["highlight"] && - @book.config["highlight"]["html"] == "pygments" + @book.config["highlight"]["html"] end def highlight(ops) if @book.config["pygments"].present? raise ReVIEW::ConfigError, "'pygments:' in config.yml is obsoleted." end + return ops[:body].to_s if !highlight? + if @book.config["highlight"]["html"] == "pygments" + highlight_pygments(ops) + elsif @book.config["highlight"]["html"] == "rouge" + highlight_rouge(ops) + else + raise ReVIEW::ConfigError, "unknown highlight method #{@book.config["highlight"]["html"]} in config.yml." + end + end + + def highlight_pygments(ops) body = ops[:body] || '' if @book.config["highlight"] && @book.config["highlight"]["lang"] lexer = @book.config["highlight"]["lang"] # default setting @@ -61,10 +73,13 @@ def highlight(ops) lexer = ops[:lexer] if ops[:lexer].present? format = ops[:format] || '' options = {:nowrap => true, :noclasses => true} + if ops[:linenum] + options[:nowrap] = false + options[:linenos] = 'inline' + end if ops[:options] && ops[:options].kind_of?(Hash) options.merge!(ops[:options]) end - return body if !highlight? begin require 'pygments' @@ -81,6 +96,37 @@ def highlight(ops) end end + def highlight_rouge(ops) + body = ops[:body] || '' + if ops[:lexer].present? + lexer = ops[:lexer] + elsif @book.config["highlight"] && @book.config["highlight"]["lang"] + lexer = @book.config["highlight"]["lang"] # default setting + else + lexer = 'text' + end + format = ops[:format] || '' + + first_line_num = 1 ## default + if ops[:options] && ops[:options][:linenostart] + first_line_num = ops[:options][:linenostart] + end + + lexer = Rouge::Lexer.find(lexer) + raise "unknown lexer #{lexer}" unless lexer + + formatter = Rouge::Formatters::HTML.new(:css_class => 'highlight') + if ops[:linenum] + formatter = Rouge::Formatters::HTMLTable.new(formatter, + :table_class => 'highlight rouge-table', + :start_line => first_line_num) + end + raise "unknown formatter #{formatter}" unless formatter + + text = unescape_html(body) + formatter.format(lexer.lex(text)) + end + def normalize_id(id) if id =~ /\A[a-z][a-z0-9_.-]*\Z/i return id diff --git a/review.gemspec b/review.gemspec index 5b03e182c..c11e64c06 100644 --- a/review.gemspec +++ b/review.gemspec @@ -24,6 +24,7 @@ Gem::Specification.new do |gem| gem.require_paths = ["lib"] gem.add_dependency("rubyzip") + gem.add_dependency("rouge") gem.add_development_dependency("rake") gem.add_development_dependency("test-unit") gem.add_development_dependency("pygments.rb") diff --git a/test/sample-book/src/style.css b/test/sample-book/src/style.css index 75c67c1bd..a30603cdb 100644 --- a/test/sample-book/src/style.css +++ b/test/sample-book/src/style.css @@ -250,6 +250,221 @@ em { font-style: italic; } +/** + * from Rouge + */ +.highlight table td { padding: 5px; } +.highlight table pre { margin: 0; } +.highlight .cm { + color: #999988; + font-style: italic; +} +.highlight .cp { + color: #999999; + font-weight: bold; +} +.highlight .c1 { + color: #999988; + font-style: italic; +} +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; +} +.highlight .c, .highlight .cd { + color: #999988; + font-style: italic; +} +.highlight .err { + color: #a61717; + background-color: #e3d2d2; +} +.highlight .gd { + color: #000000; + background-color: #ffdddd; +} +.highlight .ge { + color: #000000; + font-style: italic; +} +.highlight .gr { + color: #aa0000; +} +.highlight .gh { + color: #999999; +} +.highlight .gi { + color: #000000; + background-color: #ddffdd; +} +.highlight .go { + color: #888888; +} +.highlight .gp { + color: #555555; +} +.highlight .gs { + font-weight: bold; +} +.highlight .gu { + color: #aaaaaa; +} +.highlight .gt { + color: #aa0000; +} +.highlight .kc { + color: #000000; + font-weight: bold; +} +.highlight .kd { + color: #000000; + font-weight: bold; +} +.highlight .kn { + color: #000000; + font-weight: bold; +} +.highlight .kp { + color: #000000; + font-weight: bold; +} +.highlight .kr { + color: #000000; + font-weight: bold; +} +.highlight .kt { + color: #445588; + font-weight: bold; +} +.highlight .k, .highlight .kv { + color: #000000; + font-weight: bold; +} +.highlight .mf { + color: #009999; +} +.highlight .mh { + color: #009999; +} +.highlight .il { + color: #009999; +} +.highlight .mi { + color: #009999; +} +.highlight .mo { + color: #009999; +} +.highlight .m, .highlight .mb, .highlight .mx { + color: #009999; +} +.highlight .sb { + color: #d14; +} +.highlight .sc { + color: #d14; +} +.highlight .sd { + color: #d14; +} +.highlight .s2 { + color: #d14; +} +.highlight .se { + color: #d14; +} +.highlight .sh { + color: #d14; +} +.highlight .si { + color: #d14; +} +.highlight .sx { + color: #d14; +} +.highlight .sr { + color: #009926; +} +.highlight .s1 { + color: #d14; +} +.highlight .ss { + color: #990073; +} +.highlight .s { + color: #d14; +} +.highlight .na { + color: #008080; +} +.highlight .bp { + color: #999999; +} +.highlight .nb { + color: #0086B3; +} +.highlight .nc { + color: #445588; + font-weight: bold; +} +.highlight .no { + color: #008080; +} +.highlight .nd { + color: #3c5d5d; + font-weight: bold; +} +.highlight .ni { + color: #800080; +} +.highlight .ne { + color: #990000; + font-weight: bold; +} +.highlight .nf { + color: #990000; + font-weight: bold; +} +.highlight .nl { + color: #990000; + font-weight: bold; +} +.highlight .nn { + color: #555555; +} +.highlight .nt { + color: #000080; +} +.highlight .vc { + color: #008080; +} +.highlight .vg { + color: #008080; +} +.highlight .vi { + color: #008080; +} +.highlight .nv { + color: #008080; +} +.highlight .ow { + color: #000000; + font-weight: bold; +} +.highlight .o { + color: #000000; + font-weight: bold; +} +.highlight .w { + color: #bbbbbb; +} +.highlight { + background-color: #f8f8f8; +} +.rouge-table { border-spacing: 0 } +.rouge-gutter { text-align: right } + /** * from EBPAJ EPUB 3 File Creation Guide sample style * diff --git a/test/test_htmlbuilder.rb b/test/test_htmlbuilder.rb index b19834a17..a5627096f 100644 --- a/test/test_htmlbuilder.rb +++ b/test/test_htmlbuilder.rb @@ -557,7 +557,7 @@ def @chapter.list(id) expected = <<-EOS