summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2021-08-12 23:58:46 -0600
committerGitHub <noreply@github.com>2021-08-12 23:58:46 -0600
commit802d96e188545ef6f2579505ffb60849215be450 (patch)
treeb8b841558e6aee20f6edb0755398de1f0cc34916
parentaefdd6f3ec75a5662dddb5ad3458a0c7dd06cc3c (diff)
resolves #3641 add support for inline linenums mode when using Rouge as source highlighter (PR #4129)
-rw-r--r--CHANGELOG.adoc4
-rw-r--r--data/stylesheets/asciidoctor-default.css4
-rw-r--r--lib/asciidoctor/rouge_ext.rb46
-rw-r--r--lib/asciidoctor/syntax_highlighter/rouge.rb12
-rw-r--r--src/stylesheets/asciidoctor.css6
-rw-r--r--test/syntax_highlighter_test.rb25
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]