diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2021-08-12 23:58:46 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-12 23:58:46 -0600 |
| commit | 802d96e188545ef6f2579505ffb60849215be450 (patch) | |
| tree | b8b841558e6aee20f6edb0755398de1f0cc34916 | |
| parent | aefdd6f3ec75a5662dddb5ad3458a0c7dd06cc3c (diff) | |
resolves #3641 add support for inline linenums mode when using Rouge as source highlighter (PR #4129)
| -rw-r--r-- | CHANGELOG.adoc | 4 | ||||
| -rw-r--r-- | data/stylesheets/asciidoctor-default.css | 4 | ||||
| -rw-r--r-- | lib/asciidoctor/rouge_ext.rb | 46 | ||||
| -rw-r--r-- | lib/asciidoctor/syntax_highlighter/rouge.rb | 12 | ||||
| -rw-r--r-- | src/stylesheets/asciidoctor.css | 6 | ||||
| -rw-r--r-- | test/syntax_highlighter_test.rb | 25 |
6 files changed, 72 insertions, 25 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index da662c24..9f9f7495 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -17,6 +17,10 @@ This project utilizes semantic versioning. == Unreleased +Enhancements:: + + * Add support for inline linenums mode when using Rouge as source highlighter (`rouge-linenums-mode=inline`) (#3641) + Bug Fixes:: * Remove unnamespaced selectors in Pygments stylesheet diff --git a/data/stylesheets/asciidoctor-default.css b/data/stylesheets/asciidoctor-default.css index 1a65da65..7b34860a 100644 --- a/data/stylesheets/asciidoctor-default.css +++ b/data/stylesheets/asciidoctor-default.css @@ -225,8 +225,8 @@ pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none} table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none} table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal} table.linenotable td.code{padding-left:.75em} -table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em} -pre.pygments span.linenos{display:inline-block;margin-right:.75em} +table.linenotable td.linenos,pre.pygments .linenos,pre.rouge .linenos{border-right:1px solid;opacity:.35;padding-right:.5em} +pre.pygments span.linenos,pre.rouge span.linenos{display:inline-block;margin-right:.75em} .quoteblock{margin:0 1em 1.25em 1.5em;display:table} .quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em} .quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} diff --git a/lib/asciidoctor/rouge_ext.rb b/lib/asciidoctor/rouge_ext.rb index 57a65d1b..96339beb 100644 --- a/lib/asciidoctor/rouge_ext.rb +++ b/lib/asciidoctor/rouge_ext.rb @@ -5,40 +5,56 @@ require 'rouge' unless defined? Rouge.version module Asciidoctor module RougeExt module Formatters - class HTMLTable < ::Rouge::Formatter + class HTMLLineHighlighter < ::Rouge::Formatter + def initialize delegate, opts + @delegate = delegate + @lines = opts[:lines] || [] + end + + def stream tokens + if @lines.empty? + token_lines(tokens) {|tokens_in_line| yield (@delegate.format tokens_in_line) + LF } + else + (token_lines tokens).with_index 1 do |tokens_in_line, lineno| + formatted_line = (@delegate.format tokens_in_line) + LF + yield (@lines.include? lineno) ? %(<span class="hll">#{formatted_line}</span>) : formatted_line + end + end + end + end + + class HTMLLineNumberer < ::Rouge::Formatter def initialize delegate, opts @delegate = delegate @start_line = opts[:start_line] || 1 end def stream tokens - formatted_code = @delegate.format tokens - formatted_code += LF unless formatted_code.end_with? LF, HangingEndSpanTagCs - last_lineno = (first_lineno = @start_line) + (formatted_code.count LF) - 1 # assume number of newlines is constant - lineno_format = %(%#{(::Math.log10 last_lineno).floor + 1}i) - formatted_linenos = ((first_lineno..last_lineno).map {|lineno| sprintf lineno_format, lineno } << '').join LF - yield %(<table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno">#{formatted_linenos}</pre></td><td class="code"><pre>#{formatted_code}</pre></td></tr></tbody></table>) + formatted_lines = (@delegate.to_enum :stream, tokens).map.with_index(@start_line) {|line, lineno| [lineno.to_s, line] } + return unless (last_lineno = (formatted_lines[-1] || [])[0]) + lineno_width = last_lineno.length + formatted_lines.each {|(lineno, line)| yield %(<span class="linenos">#{lineno.rjust lineno_width}</span>#{line}) } end end - class HTMLLineHighlighter < ::Rouge::Formatter + class HTMLTableLineNumberer < ::Rouge::Formatter def initialize delegate, opts @delegate = delegate - @lines = opts[:lines] || [] + @start_line = opts[:start_line] || 1 end def stream tokens - (token_lines tokens).with_index 1 do |tokens_in_line, lineno| - formatted_line = (@delegate.format tokens_in_line) + LF - yield (@lines.include? lineno) ? %(<span class="hll">#{formatted_line}</span>) : formatted_line - end + formatted_lines = (@delegate.to_enum :stream, tokens).with_index(@start_line).with_object({}) {|(line, lineno), accum| accum[lineno.to_s] = line } + return unless (last_lineno = formatted_lines.keys[-1]) + lineno_width = last_lineno.length + formatted_linenos = formatted_lines.keys.map {|lineno| (lineno.rjust lineno_width) + LF } + yield %(<table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno">#{formatted_linenos.join}</pre></td><td class="code"><pre>#{formatted_lines.values.join}</pre></td></tr></tbody></table>) end end LF = ?\n - HangingEndSpanTagCs = %(#{LF}</span>) - private_constant :HangingEndSpanTagCs, :LF + private_constant :LF end end end diff --git a/lib/asciidoctor/syntax_highlighter/rouge.rb b/lib/asciidoctor/syntax_highlighter/rouge.rb index d483a307..3709e4f6 100644 --- a/lib/asciidoctor/syntax_highlighter/rouge.rb +++ b/lib/asciidoctor/syntax_highlighter/rouge.rb @@ -76,10 +76,16 @@ class SyntaxHighlighter::RougeAdapter < SyntaxHighlighter::Base formatter = opts[:css_mode] == :class ? (::Rouge::Formatters::HTML.new inline_theme: @style) : (::Rouge::Formatters::HTMLInline.new (::Rouge::Theme.find @style).new) - if (highlight_lines = opts[:highlight_lines]) - formatter = RougeExt::Formatters::HTMLLineHighlighter.new formatter, lines: highlight_lines + if (number_lines = opts[:number_lines]) + formatter = RougeExt::Formatters::HTMLLineHighlighter.new formatter, lines: opts[:highlight_lines] + number_lines == :table ? + (RougeExt::Formatters::HTMLTableLineNumberer.new formatter, start_line: opts[:start_line_number]) : + (RougeExt::Formatters::HTMLLineNumberer.new formatter, start_line: opts[:start_line_number]) + elsif (highlight_lines = opts[:highlight_lines]) + RougeExt::Formatters::HTMLLineHighlighter.new formatter, lines: highlight_lines + else + formatter end - opts[:number_lines] ? (RougeExt::Formatters::HTMLTable.new formatter, start_line: opts[:start_line_number]) : formatter end module Loader diff --git a/src/stylesheets/asciidoctor.css b/src/stylesheets/asciidoctor.css index 9d86f8f8..6bf43cb4 100644 --- a/src/stylesheets/asciidoctor.css +++ b/src/stylesheets/asciidoctor.css @@ -1400,13 +1400,15 @@ table.linenotable td.code { } table.linenotable td.linenos, -pre.pygments .linenos { +pre.pygments .linenos, +pre.rouge .linenos { border-right: 1px solid; opacity: 0.35; padding-right: 0.5em; } -pre.pygments span.linenos { +pre.pygments span.linenos, +pre.rouge span.linenos { display: inline-block; margin-right: 0.75em; } diff --git a/test/syntax_highlighter_test.rb b/test/syntax_highlighter_test.rb index adb5064e..eafbf6ea 100644 --- a/test/syntax_highlighter_test.rb +++ b/test/syntax_highlighter_test.rb @@ -809,12 +809,12 @@ context 'Syntax Highlighter' do assert_css 'code span.gp', output, 1 end - test 'should set starting line number to 1 by default in HTML output if linenums option is enabled' do + test 'should number lines using table layout if linenums option is enabled and linenums mode is not set' do input = <<~'EOS' [source%linenums,ruby] ---- - puts 'Hello, World!' - puts 'Goodbye, World!' + puts 'Hello, world!' + puts 'Goodbye, world!' ---- EOS output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'rouge' } @@ -826,6 +826,25 @@ context 'Syntax Highlighter' do assert_xpath %(//pre[@class="lineno"][text()="1\n2\n"]), output, 1 end + test 'should number lines using inline element if linenums option is enabled and linenums mode is inline' do + input = <<~'EOS' + :rouge-linenums-mode: inline + + [source%linenums,ruby] + ---- + puts 'Hello, world!' + puts 'Goodbye, world!' + ---- + EOS + expected = <<~EOS.chop + <span class="linenos">1</span><span class="nb">puts</span> <span class="s1">'Hello, world!'</span> + <span class="linenos">2</span><span class="nb">puts</span> <span class="s1">'Goodbye, world!'</span> + EOS + output = convert_string_to_embedded input, attributes: { 'source-highlighter' => 'rouge' } + assert_css 'table.linenotable', output, 0 + assert_includes output, expected + end + test 'should set starting line number in HTML output if linenums option is enabled and start attribute is set' do input = <<~'EOS' [source%linenums,ruby,start=9] |
