rouge 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- 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
|