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 syntax highlighter
- add integration with Rouge - organize the code to setup syntax highlighting - enable line number support when highlighting with Rouge - add pastie theme for Rouge - patch Rouge style lookup (see rouge-ruby/rouge#280) - don't crash if color value is 3-digit hex in span style
- Loading branch information
1 parent
bd93e77
commit 95cd8b0
Showing
6 changed files
with
245 additions
and
10 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
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,123 @@ | ||
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 | ||
|
||
# ...or 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] | ||
# NOTE strip trailing endline added to address https://github.com/jneen/rouge/issues/279 | ||
# disabled since it's benign | ||
#tokens = tokens.entries | ||
#if (last_token = tokens_a[-1]) | ||
# if (last_val = last_token[-1]) == EOL | ||
# tokens.pop | ||
# else | ||
# last_token[-1] = last_val.chomp | ||
# end | ||
#end | ||
|
||
# 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) | ||
# QUESTION only append if end_with?(EOL)? | ||
fragments << (create_linenum_fragment linenum += 1) | ||
end | ||
else | ||
fragments << fragment | ||
end | ||
end | ||
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 | ||
if val | ||
fragment = { text: val } | ||
else | ||
fragment = {} | ||
end | ||
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 |