forked from asciidoctor/asciidoctor-pdf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
resolves asciidoctor#247 add support for the Rouge source highlighter
- add integration with Rouge for highlighting source listings - organize the code to setup source highlighting - enable line number support when highlighting with Rouge - add pastie theme for Rouge - patch Rouge style lookup (see rouge-ruby/rouge#280)
- Loading branch information
1 parent
0ca198f
commit aa18f79
Showing
5 changed files
with
227 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
require 'rouge' | ||
require_relative 'rouge_ext/formatters/prawn' | ||
require_relative 'rouge_ext/css_theme' | ||
require_relative 'rouge_ext/themes/pastie' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module Rouge | ||
class CSSTheme | ||
# Patch style_for to return most specific style first | ||
# See https://github.com/jneen/rouge/issues/280 (fix pending) | ||
def style_for token | ||
token.token_chain.reverse_each do |t| | ||
if (s = styles[t]) | ||
return s | ||
end | ||
end | ||
nil | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
module Rouge | ||
module Formatters | ||
# Transforms a token stream into an array of | ||
# formatted text fragments for use with Prawn. | ||
class Prawn < Formatter | ||
tag 'prawn' | ||
|
||
EOL = "\n" | ||
NoBreakSpace = [0x00a0].pack 'U*' | ||
BoldStyle = [:bold].to_set | ||
ItalicStyle = [:italic].to_set | ||
BoldItalicStyle = [:bold, :italic].to_set | ||
|
||
def initialize opts = {} | ||
unless ::Rouge::Theme === (theme = opts[:theme]) | ||
unless theme && (theme = ::Rouge::Theme.find theme) | ||
theme = ::Rouge::Themes::Pastie | ||
end | ||
theme = theme.new | ||
end | ||
@theme = theme | ||
@normalized_colors = {} | ||
@linenum_fragment_base = (create_fragment Token['Generic.Lineno']).merge linenum: true | ||
end | ||
|
||
# Override format method so fragments don't get flatted to a string | ||
# and to add an options Hash. | ||
def format tokens, opts = {} | ||
stream tokens, opts | ||
|
||
# ...we could strip trailing endline added to address https://github.com/jneen/rouge/issues/279 | ||
#fragments = stream tokens, opts | ||
#if (last_fragment = fragments[-1]) | ||
# last_fragment[:text] = last_fragment[:text].chomp | ||
#end | ||
#fragments | ||
end | ||
|
||
def stream tokens, opts = {} | ||
if opts[:line_numbers] | ||
# TODO implement line number start (offset) | ||
linenum = 0 | ||
fragments = [] | ||
fragments << (create_linenum_fragment linenum += 1) | ||
tokens.each do |tok, val| | ||
fragment = create_fragment tok, val | ||
if val == EOL | ||
fragments << fragment | ||
fragments << (create_linenum_fragment linenum += 1) | ||
elsif val.include? EOL | ||
val.each_line do |line| | ||
fragments << (fragment.merge text: line) | ||
# NOTE append linenum fragment if there's a next line; only works if source doesn't have trailing endline | ||
if line.end_with? EOL | ||
fragments << (create_linenum_fragment linenum += 1) | ||
end | ||
end | ||
else | ||
fragments << fragment | ||
end | ||
end | ||
# NOTE drop orphaned linenum fragment (due to trailing endline in source) | ||
fragments.pop if (last_fragment = fragments[-1]) && last_fragment[:linenum] | ||
# NOTE pad numbers with less digits than the highest line number | ||
if (linenum_w = (linenum / 10) + 1) > 1 | ||
# NOTE extra column is the trailing space after the line number | ||
linenum_w += 1 | ||
fragments.each do |fragment| | ||
fragment[:text] = %(#{fragment[:text].rjust linenum_w, NoBreakSpace}) if fragment[:linenum] | ||
end | ||
end | ||
fragments | ||
else | ||
tokens.map {|tok, val| create_fragment tok, val } | ||
end | ||
end | ||
|
||
def create_fragment tok, val = nil | ||
fragment = val ? { text: val } : {} | ||
if (style_rules = @theme.style_for tok) | ||
# TODO support background color | ||
if (fg = normalize_color style_rules.fg) | ||
fragment[:color] = fg | ||
end | ||
if style_rules[:bold] | ||
fragment[:styles] = style_rules[:italic] ? BoldItalicStyle : BoldStyle | ||
elsif style_rules[:italic] | ||
fragment[:styles] = ItalicStyle | ||
end | ||
end | ||
fragment | ||
end | ||
|
||
def create_linenum_fragment linenum | ||
@linenum_fragment_base.merge text: %(#{linenum} ) | ||
end | ||
|
||
def normalize_color raw | ||
return unless raw | ||
if (normalized = @normalized_colors[raw]) | ||
normalized | ||
else | ||
normalized = raw | ||
normalized = normalized[1..-1] if normalized.start_with? '#' | ||
normalized = normalized.each_char.map {|c| c * 2 }.join if normalized.size == 3 | ||
@normalized_colors[raw] = normalized | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
module Rouge | ||
module Themes | ||
# A Rouge theme that matches the pastie style from Pygments. | ||
# See https://bitbucket.org/birkenfeld/pygments-main/src/default/pygments/styles/pastie.py | ||
class Pastie < CSSTheme | ||
name 'pastie' | ||
|
||
style Text::Whitespace, fg: '#bbbbbb' | ||
|
||
style Comment, fg: '#888888' | ||
style Comment::Preproc, fg: '#cc0000', bold: true | ||
style Comment::Special, fg: '#cc0000', bg: '#fff0f0', bold: true | ||
|
||
style Error, fg: '#a61717', bg: '#e3d2d2' | ||
style Generic::Error, fg: '#aa0000' | ||
style Generic::Traceback, fg: '#aa0000' | ||
|
||
style Generic::Deleted, fg: '#000000', bg: '#ffdddd' | ||
style Generic::Emph, italic: true | ||
style Generic::Inserted, fg: '#000000', bg: '#ddffdd' | ||
style Generic::Heading, fg: '#333333' | ||
#style Generic::Lineno, fg: '#555555' | ||
style Generic::Lineno, fg: '#888888' | ||
style Generic::Output, fg: '#888888' | ||
style Generic::Prompt, fg: '#555555' | ||
style Generic::Strong, bold: true | ||
style Generic::Subheading, fg: '#666666' | ||
|
||
style Keyword, fg: '#008800', bold: true | ||
style Keyword::Pseudo, fg: '#008800' | ||
style Keyword::Type, fg: '#888888', bold: true | ||
|
||
style Literal::Number, fg: '#0000dd', bold: true | ||
|
||
style Literal::String, fg: '#dd2200', bg: '#fff0f0' | ||
style Literal::String::Escape, fg: '#0044dd' | ||
style Literal::String::Interpol, fg: '#3333bb' | ||
style Literal::String::Other, fg: '#22bb22', bg: '#f0fff0' | ||
style Literal::String::Regex, fg: '#008800', bg: '#fff0ff' | ||
style Literal::String::Symbol, fg: '#aa6600' | ||
|
||
style Name::Attribute, fg: '#336699' | ||
style Name::Builtin, fg: '#003388' | ||
style Name::Class, fg: '#bb0066', bold: true | ||
style Name::Constant, fg: '#003366', bold: true | ||
style Name::Decorator, fg: '#555555' | ||
style Name::Exception, fg: '#bb0066', bold: true | ||
style Name::Function, fg: '#0066bb', bold: true | ||
style Name::Label, fg: '#336699', italic: true | ||
style Name::Namespace, fg: '#bb0066', bold: true | ||
style Name::Property, fg: '#336699', bold: true | ||
style Name::Tag, fg: '#bb0066', bold: true | ||
style Name::Variable::Global, fg: '#dd7700' | ||
style Name::Variable::Instance, fg: '#3333bb' | ||
style Name::Variable, fg: '#336699' | ||
|
||
style Operator::Word, fg: '#008800' | ||
end | ||
end | ||
end |