rouge 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/rouge.rb +8 -0
- data/lib/rouge/cli.rb +6 -2
- data/lib/rouge/formatter.rb +13 -0
- data/lib/rouge/formatters/html.rb +2 -0
- data/lib/rouge/formatters/terminal256.rb +152 -0
- data/lib/rouge/lexer.rb +31 -49
- data/lib/rouge/lexers/erb.rb +54 -0
- data/lib/rouge/lexers/haskell.rb +7 -0
- data/lib/rouge/lexers/html.rb +20 -2
- data/lib/rouge/lexers/make.rb +105 -0
- data/lib/rouge/lexers/perl.rb +196 -0
- data/lib/rouge/lexers/php.rb +145 -0
- data/lib/rouge/lexers/php/builtins.rb +196 -0
- data/lib/rouge/lexers/ruby.rb +25 -10
- data/lib/rouge/lexers/scheme.rb +2 -2
- data/lib/rouge/lexers/shell.rb +16 -2
- data/lib/rouge/lexers/tex.rb +71 -0
- data/lib/rouge/theme.rb +21 -2
- data/lib/rouge/themes/colorful.rb +1 -1
- data/lib/rouge/themes/thankful_eyes.rb +4 -3
- data/lib/rouge/version.rb +1 -1
- metadata +9 -2
data/lib/rouge.rb
CHANGED
@@ -18,19 +18,26 @@ load load_dir.join('rouge/version.rb')
|
|
18
18
|
load load_dir.join('rouge/text_analyzer.rb')
|
19
19
|
load load_dir.join('rouge/token.rb')
|
20
20
|
load load_dir.join('rouge/lexer.rb')
|
21
|
+
|
21
22
|
load load_dir.join('rouge/lexers/text.rb')
|
22
23
|
load load_dir.join('rouge/lexers/diff.rb')
|
24
|
+
load load_dir.join('rouge/lexers/tex.rb')
|
23
25
|
|
26
|
+
load load_dir.join('rouge/lexers/make.rb')
|
24
27
|
load load_dir.join('rouge/lexers/shell.rb')
|
25
28
|
|
26
29
|
load load_dir.join('rouge/lexers/javascript.rb')
|
27
30
|
load load_dir.join('rouge/lexers/css.rb')
|
28
31
|
load load_dir.join('rouge/lexers/html.rb')
|
29
32
|
load load_dir.join('rouge/lexers/xml.rb')
|
33
|
+
load load_dir.join('rouge/lexers/php.rb')
|
34
|
+
|
35
|
+
load load_dir.join('rouge/lexers/erb.rb')
|
30
36
|
|
31
37
|
load load_dir.join('rouge/lexers/tcl.rb')
|
32
38
|
load load_dir.join('rouge/lexers/python.rb')
|
33
39
|
load load_dir.join('rouge/lexers/ruby.rb')
|
40
|
+
load load_dir.join('rouge/lexers/perl.rb')
|
34
41
|
|
35
42
|
load load_dir.join('rouge/lexers/haskell.rb')
|
36
43
|
load load_dir.join('rouge/lexers/scheme.rb')
|
@@ -42,6 +49,7 @@ load load_dir.join('rouge/lexers/java.rb')
|
|
42
49
|
|
43
50
|
load load_dir.join('rouge/formatter.rb')
|
44
51
|
load load_dir.join('rouge/formatters/html.rb')
|
52
|
+
load load_dir.join('rouge/formatters/terminal256.rb')
|
45
53
|
|
46
54
|
load load_dir.join('rouge/theme.rb')
|
47
55
|
load load_dir.join('rouge/themes/thankful_eyes.rb')
|
data/lib/rouge/cli.rb
CHANGED
@@ -25,11 +25,13 @@ module Rouge
|
|
25
25
|
end
|
26
26
|
|
27
27
|
desc 'highlight [FILE]', 'highlight some code'
|
28
|
-
option :
|
28
|
+
option :input_file, :aliases => '-i', :desc => 'the file to operate on'
|
29
29
|
option :lexer, :aliases => '-l',
|
30
30
|
:desc => ('Which lexer to use. If not provided, rougify will try to ' +
|
31
31
|
'guess based on --mimetype, the filename, and the file ' +
|
32
32
|
'contents.')
|
33
|
+
option :formatter, :aliases => '-f', :default => 'html',
|
34
|
+
:desc => ('Which formatter to use.')
|
33
35
|
option :mimetype, :aliases => '-m',
|
34
36
|
:desc => ('a mimetype that Rouge will use to guess the correct lexer. ' +
|
35
37
|
'This is ignored if --lexer is specified.')
|
@@ -52,8 +54,10 @@ module Rouge
|
|
52
54
|
raise "unknown lexer: #{options[:lexer]}" unless lexer_class
|
53
55
|
end
|
54
56
|
|
57
|
+
formatter_class = Formatter.find(options[:formatter])
|
58
|
+
|
55
59
|
# only HTML is supported for now
|
56
|
-
formatter =
|
60
|
+
formatter = formatter_class.new(normalize_hash_keys(options[:formatter_opts]))
|
57
61
|
lexer = lexer_class.new(normalize_hash_keys(options[:lexer_opts]))
|
58
62
|
|
59
63
|
puts Rouge.highlight(source, lexer, formatter)
|
data/lib/rouge/formatter.rb
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
module Rouge
|
2
2
|
class Formatter
|
3
|
+
REGISTRY = {}
|
4
|
+
|
5
|
+
def self.tag(tag=nil)
|
6
|
+
return @tag unless tag
|
7
|
+
REGISTRY[tag] = self
|
8
|
+
|
9
|
+
@tag = tag
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find(tag)
|
13
|
+
REGISTRY[tag]
|
14
|
+
end
|
15
|
+
|
3
16
|
def render(tokens)
|
4
17
|
enum_for(:stream, tokens).to_a.join
|
5
18
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Rouge
|
2
|
+
module Formatters
|
3
|
+
class Terminal256 < Formatter
|
4
|
+
tag 'terminal256'
|
5
|
+
|
6
|
+
attr_reader :theme
|
7
|
+
def initialize(opts={})
|
8
|
+
@theme = opts[:theme] || Themes::ThankfulEyes
|
9
|
+
@theme = Theme.find(@theme) if @theme.is_a? String
|
10
|
+
end
|
11
|
+
|
12
|
+
def stream(tokens, &b)
|
13
|
+
tokens.each do |tok, val|
|
14
|
+
escape = escape_sequence(tok)
|
15
|
+
yield escape.style_string
|
16
|
+
yield val
|
17
|
+
yield escape.reset_string
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class EscapeSequence
|
22
|
+
attr_reader :style
|
23
|
+
def initialize(style)
|
24
|
+
@style = style
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.xterm_colors
|
28
|
+
@xterm_colors ||= [].tap do |out|
|
29
|
+
# colors 0..15: 16 basic colors
|
30
|
+
out << [0x00, 0x00, 0x00] # 0
|
31
|
+
out << [0xcd, 0x00, 0x00] # 1
|
32
|
+
out << [0x00, 0xcd, 0x00] # 2
|
33
|
+
out << [0xcd, 0xcd, 0x00] # 3
|
34
|
+
out << [0x00, 0x00, 0xee] # 4
|
35
|
+
out << [0xcd, 0x00, 0xcd] # 5
|
36
|
+
out << [0x00, 0xcd, 0xcd] # 6
|
37
|
+
out << [0xe5, 0xe5, 0xe5] # 7
|
38
|
+
out << [0x7f, 0x7f, 0x7f] # 8
|
39
|
+
out << [0xff, 0x00, 0x00] # 9
|
40
|
+
out << [0x00, 0xff, 0x00] # 10
|
41
|
+
out << [0xff, 0xff, 0x00] # 11
|
42
|
+
out << [0x5c, 0x5c, 0xff] # 12
|
43
|
+
out << [0xff, 0x00, 0xff] # 13
|
44
|
+
out << [0x00, 0xff, 0xff] # 14
|
45
|
+
out << [0xff, 0xff, 0xff] # 15
|
46
|
+
|
47
|
+
# colors 16..232: the 6x6x6 color cube
|
48
|
+
valuerange = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
|
49
|
+
|
50
|
+
217.times do |i|
|
51
|
+
r = valuerange[(i / 36) % 6]
|
52
|
+
g = valuerange[(i / 6) % 6]
|
53
|
+
b = valuerange[i % 6]
|
54
|
+
out << [r, g, b]
|
55
|
+
end
|
56
|
+
|
57
|
+
# colors 233..253: grayscale
|
58
|
+
1.upto 22 do |i|
|
59
|
+
v = 8 + i * 10
|
60
|
+
out << [v, v, v]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def fg
|
66
|
+
return @fg if instance_variable_defined? :@fg
|
67
|
+
@fg = style.fg && self.class.color_index(style.fg)
|
68
|
+
end
|
69
|
+
|
70
|
+
def bg
|
71
|
+
return @bg if instance_variable_defined? :@bg
|
72
|
+
@bg = style.bg && self.class.color_index(style.bg)
|
73
|
+
end
|
74
|
+
|
75
|
+
def style_string
|
76
|
+
@style_string ||= begin
|
77
|
+
attrs = []
|
78
|
+
|
79
|
+
attrs << ['38', '5', fg.to_s] if fg
|
80
|
+
attrs << ['45', '5', bg.to_s] if bg
|
81
|
+
attrs << '01' if style[:bold]
|
82
|
+
attrs << '04' if style[:italic] # underline, but hey, whatevs
|
83
|
+
escape(attrs)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def reset_string
|
88
|
+
@reset_string ||= begin
|
89
|
+
attrs = []
|
90
|
+
attrs << '39' if fg # fg reset
|
91
|
+
attrs << '49' if bg # bg reset
|
92
|
+
attrs << '00' if style[:bold] || style[:italic]
|
93
|
+
|
94
|
+
escape(attrs)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# private
|
99
|
+
def escape(attrs)
|
100
|
+
return '' if attrs.empty?
|
101
|
+
"\e[#{attrs.join(';')}m"
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.color_index(color)
|
105
|
+
@color_index_cache ||= {}
|
106
|
+
@color_index_cache[color] ||= closest_color(*get_rgb(color))
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.get_rgb(color)
|
110
|
+
color = $1 if color =~ /#([0-9a-f]+)/i
|
111
|
+
hexes = case color.size
|
112
|
+
when 3
|
113
|
+
color.chars.map { |c| "#{c}#{c}" }
|
114
|
+
when 6
|
115
|
+
color.scan /../
|
116
|
+
else
|
117
|
+
raise "invalid color: #{color}"
|
118
|
+
end
|
119
|
+
|
120
|
+
hexes.map { |h| h.to_i(16) }
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.closest_color(r, g, b)
|
124
|
+
distance = 257 * 257 * 3 # (max distance, from #000000 to #ffffff)
|
125
|
+
|
126
|
+
match = 0
|
127
|
+
|
128
|
+
xterm_colors.each_with_index do |(cr, cg, cb), i|
|
129
|
+
d = (r - cr)**2 + (g - cg)**2 + (b - cb)**2
|
130
|
+
next if d >= distance
|
131
|
+
|
132
|
+
match = i
|
133
|
+
distance = d
|
134
|
+
end
|
135
|
+
|
136
|
+
match
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# private
|
141
|
+
def escape_sequence(token)
|
142
|
+
@escape_sequences ||= {}
|
143
|
+
@escape_sequences[token.name] ||= begin
|
144
|
+
esc = EscapeSequence.new(theme.get_style(token))
|
145
|
+
# don't highlight text backgrounds
|
146
|
+
esc.style.delete(:bg) if token.name == 'Text'
|
147
|
+
esc
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/rouge/lexer.rb
CHANGED
@@ -4,35 +4,10 @@ require 'strscan'
|
|
4
4
|
module Rouge
|
5
5
|
class Lexer
|
6
6
|
class << self
|
7
|
-
def make(opts={}, &b)
|
8
|
-
_sup = self
|
9
|
-
|
10
|
-
Class.new(self) do
|
11
|
-
@lazy_load_proc = b
|
12
|
-
@default_options = _sup.default_options.merge(opts)
|
13
|
-
@parent = _sup
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
7
|
def lex(stream, opts={}, &b)
|
18
8
|
new(opts).lex(stream, &b)
|
19
9
|
end
|
20
10
|
|
21
|
-
protected
|
22
|
-
def force_load!
|
23
|
-
return self if @force_load
|
24
|
-
@force_load = true
|
25
|
-
@lazy_load_proc && instance_eval(&@lazy_load_proc)
|
26
|
-
|
27
|
-
self
|
28
|
-
end
|
29
|
-
public
|
30
|
-
|
31
|
-
def new(*a, &b)
|
32
|
-
force_load!
|
33
|
-
super(*a, &b)
|
34
|
-
end
|
35
|
-
|
36
11
|
def default_options
|
37
12
|
@default_options ||= {}
|
38
13
|
end
|
@@ -121,7 +96,6 @@ module Rouge
|
|
121
96
|
|
122
97
|
def initialize(opts={}, &b)
|
123
98
|
options(opts)
|
124
|
-
@lazy_load_proc = b
|
125
99
|
end
|
126
100
|
|
127
101
|
def options(o={})
|
@@ -146,9 +120,15 @@ module Rouge
|
|
146
120
|
lex(stream).to_a
|
147
121
|
end
|
148
122
|
|
149
|
-
def
|
123
|
+
def reset!
|
124
|
+
# noop, called after each lex is finished
|
125
|
+
end
|
126
|
+
|
127
|
+
def lex(string, opts={}, &b)
|
150
128
|
return enum_for(:lex, string) unless block_given?
|
151
129
|
|
130
|
+
reset! unless opts[:continue]
|
131
|
+
|
152
132
|
last_token = nil
|
153
133
|
last_val = nil
|
154
134
|
stream_tokens(StringScanner.new(string)) do |tok, val|
|
@@ -244,7 +224,7 @@ module Rouge
|
|
244
224
|
attr_accessor :scanner
|
245
225
|
attr_accessor :stack
|
246
226
|
attr_accessor :lexer
|
247
|
-
def initialize(lexer, scanner, stack=nil)
|
227
|
+
def initialize(lexer, scanner=nil, stack=nil)
|
248
228
|
@lexer = lexer
|
249
229
|
@scanner = scanner
|
250
230
|
@stack = stack || [lexer.get_state(:root)]
|
@@ -258,8 +238,15 @@ module Rouge
|
|
258
238
|
end
|
259
239
|
|
260
240
|
def push(state_name=nil, &b)
|
261
|
-
|
262
|
-
|
241
|
+
# use the top of the stack by default
|
242
|
+
if state_name || b
|
243
|
+
push_state = state.relative_state(state_name, &b)
|
244
|
+
else
|
245
|
+
push_state = self.state
|
246
|
+
end
|
247
|
+
|
248
|
+
debug { " pushing #{push_state.name}" }
|
249
|
+
stack.push(push_state)
|
263
250
|
end
|
264
251
|
|
265
252
|
def in_state?(state_name)
|
@@ -300,10 +287,10 @@ module Rouge
|
|
300
287
|
end
|
301
288
|
|
302
289
|
def delegate(lexer, text=nil)
|
303
|
-
debug { " delegating to #{lexer.
|
290
|
+
debug { " delegating to #{lexer.inspect}" }
|
304
291
|
text ||= scanner[0]
|
305
292
|
|
306
|
-
lexer.lex(text) do |tok, val|
|
293
|
+
lexer.lex(text, :continue => true) do |tok, val|
|
307
294
|
debug { " delegated token: #{tok.inspect}, #{val.inspect}" }
|
308
295
|
token(tok, val)
|
309
296
|
end
|
@@ -352,8 +339,8 @@ module Rouge
|
|
352
339
|
else
|
353
340
|
tok = Token[tok]
|
354
341
|
|
355
|
-
callback = proc do
|
356
|
-
token tok
|
342
|
+
callback = proc do
|
343
|
+
token tok
|
357
344
|
case next_state
|
358
345
|
when :pop!
|
359
346
|
pop!
|
@@ -388,16 +375,6 @@ module Rouge
|
|
388
375
|
states[name] = State.new(self, name, &b)
|
389
376
|
end
|
390
377
|
|
391
|
-
def initialize(parent=nil, opts={}, &defn)
|
392
|
-
if parent.is_a? Hash
|
393
|
-
opts = parent
|
394
|
-
parent = nil
|
395
|
-
end
|
396
|
-
|
397
|
-
@parent = parent
|
398
|
-
super(opts, &defn)
|
399
|
-
end
|
400
|
-
|
401
378
|
def self.get_state(name)
|
402
379
|
return name if name.is_a? State
|
403
380
|
|
@@ -414,18 +391,23 @@ module Rouge
|
|
414
391
|
self.class.get_state(name)
|
415
392
|
end
|
416
393
|
|
417
|
-
def
|
418
|
-
scan_state
|
394
|
+
def scan_state
|
395
|
+
@scan_state ||= ScanState.new(self)
|
396
|
+
end
|
397
|
+
|
398
|
+
def reset!
|
399
|
+
@scan_state = nil
|
419
400
|
|
420
401
|
self.class.start_procs.each do |pr|
|
421
402
|
scan_state.instance_eval(&pr)
|
422
403
|
end
|
423
|
-
|
424
|
-
stream_with_state(scan_state, &b)
|
425
404
|
end
|
426
405
|
|
427
|
-
def
|
406
|
+
def stream_tokens(stream, &b)
|
407
|
+
scan_state.scanner = stream
|
408
|
+
|
428
409
|
until scan_state.eos?
|
410
|
+
debug { "lexer: #{self.class.tag}" }
|
429
411
|
debug { "stack: #{scan_state.stack.map(&:name).inspect}" }
|
430
412
|
debug { "stream: #{scan_state.scanner.peek(20).inspect}" }
|
431
413
|
success = step(get_state(scan_state.state), scan_state, &b)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rouge
|
2
|
+
module Lexers
|
3
|
+
class ERB < RegexLexer
|
4
|
+
tag 'erb'
|
5
|
+
aliases 'eruby', 'rhtml'
|
6
|
+
|
7
|
+
filenames '*.erb', '*.erubis', '*.rhtml', '*.eruby'
|
8
|
+
|
9
|
+
def self.analyze_text(text)
|
10
|
+
return 0.4 if text =~ /<%.*%>/
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :parent
|
14
|
+
attr_reader :ruby_lexer
|
15
|
+
def initialize(opts={})
|
16
|
+
@parent = opts.delete(:parent) || 'html'
|
17
|
+
if @parent.is_a? String
|
18
|
+
lexer_class = Lexer.find(@parent)
|
19
|
+
@parent = lexer_class.new(opts)
|
20
|
+
end
|
21
|
+
|
22
|
+
@ruby_lexer = Ruby.new(opts)
|
23
|
+
|
24
|
+
super(opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
open = /<%%|<%=|<%#|<%-|<%/
|
28
|
+
close = /%%>|-%>|%>/
|
29
|
+
|
30
|
+
state :root do
|
31
|
+
rule /<%#/, 'Comment', :comment
|
32
|
+
|
33
|
+
rule open, 'Comment.Preproc', :ruby
|
34
|
+
|
35
|
+
rule /.+?(?=#{open})|.+/m do
|
36
|
+
delegate lexer.parent
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
state :comment do
|
41
|
+
rule close, 'Comment', :pop!
|
42
|
+
rule /.+(?=#{close})|.+/m, 'Comment'
|
43
|
+
end
|
44
|
+
|
45
|
+
state :ruby do
|
46
|
+
rule close, 'Comment.Preproc', :pop!
|
47
|
+
|
48
|
+
rule /.+?(?=#{close})|.+/m do
|
49
|
+
delegate lexer.ruby_lexer
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|