rouge 0.0.1 → 0.0.2
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/Gemfile +11 -0
- data/lib/rouge.rb +23 -0
- data/lib/rouge/formatter.rb +11 -0
- data/lib/rouge/formatters/html.rb +33 -0
- data/lib/rouge/lexer.rb +238 -0
- data/lib/rouge/lexers/javascript.rb +83 -0
- data/lib/rouge/lexers/shell.rb +109 -0
- data/lib/rouge/theme.rb +112 -0
- data/lib/rouge/themes/thankful_eyes.rb +65 -0
- data/lib/rouge/token.rb +168 -0
- data/lib/rouge/version.rb +1 -1
- metadata +10 -2
data/Gemfile
CHANGED
data/lib/rouge.rb
CHANGED
@@ -1,2 +1,25 @@
|
|
1
|
+
# stdlib
|
2
|
+
require 'pathname'
|
3
|
+
|
1
4
|
module Rouge
|
5
|
+
class << self
|
6
|
+
def highlight(text, lexer_name, formatter)
|
7
|
+
lexer = Lexer.find(lexer_name)
|
8
|
+
raise "unknown lexer #{lexer_name}" unless lexer
|
9
|
+
|
10
|
+
formatter.render(lexer.lex(text))
|
11
|
+
end
|
12
|
+
end
|
2
13
|
end
|
14
|
+
|
15
|
+
load_dir = Pathname.new(__FILE__).dirname
|
16
|
+
load load_dir.join('rouge/token.rb')
|
17
|
+
load load_dir.join('rouge/lexer.rb')
|
18
|
+
load load_dir.join('rouge/lexers/shell.rb')
|
19
|
+
load load_dir.join('rouge/lexers/javascript.rb')
|
20
|
+
|
21
|
+
load load_dir.join('rouge/formatter.rb')
|
22
|
+
load load_dir.join('rouge/formatters/html.rb')
|
23
|
+
|
24
|
+
load load_dir.join('rouge/theme.rb')
|
25
|
+
load load_dir.join('rouge/themes/thankful_eyes.rb')
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rouge
|
2
|
+
module Formatters
|
3
|
+
class HTML < Formatter
|
4
|
+
def initialize(opts={})
|
5
|
+
@css_class = opts[:css_class] || 'highlight'
|
6
|
+
end
|
7
|
+
|
8
|
+
def stream(tokens, &b)
|
9
|
+
yield "<pre class=#{@css_class.inspect}>"
|
10
|
+
tokens.each do |tok, val|
|
11
|
+
# TODO: properly html-encode val
|
12
|
+
val.gsub! '&', '&'
|
13
|
+
val.gsub! '<', '<'
|
14
|
+
val.gsub! '>', '>'
|
15
|
+
|
16
|
+
case tok.shortname
|
17
|
+
when ''
|
18
|
+
yield val
|
19
|
+
when nil
|
20
|
+
raise "unknown token: #{tok.inspect}"
|
21
|
+
else
|
22
|
+
yield '<span class='
|
23
|
+
yield tok.shortname.inspect
|
24
|
+
yield '>'
|
25
|
+
yield val
|
26
|
+
yield '</span>'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
yield '</pre>'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/rouge/lexer.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
module Rouge
|
2
|
+
class Lexer
|
3
|
+
class << self
|
4
|
+
def create(opts={}, &b)
|
5
|
+
new(opts, &b).send(:force_load!)
|
6
|
+
end
|
7
|
+
|
8
|
+
def find(name)
|
9
|
+
registry[name.to_s]
|
10
|
+
end
|
11
|
+
|
12
|
+
def register(name, lexer)
|
13
|
+
registry[name.to_s] = lexer
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def registry
|
18
|
+
@registry ||= {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def name(n=nil)
|
23
|
+
return @name if n.nil?
|
24
|
+
|
25
|
+
@name = n.to_s
|
26
|
+
aliases @name
|
27
|
+
end
|
28
|
+
|
29
|
+
def aliases(*args)
|
30
|
+
args.each { |arg| Lexer.register(arg, self) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(opts={}, &b)
|
34
|
+
options opts
|
35
|
+
@lazy_load_proc = b
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_options
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
|
42
|
+
def options(o={})
|
43
|
+
(@options ||= default_options).merge!(o)
|
44
|
+
|
45
|
+
@options
|
46
|
+
end
|
47
|
+
|
48
|
+
def option(k, v=:absent)
|
49
|
+
if v == :absent
|
50
|
+
options[k.to_s]
|
51
|
+
else
|
52
|
+
options({ k.to_s => v })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def debug(&b)
|
57
|
+
puts(b.call) if option :debug
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_tokens(stream)
|
61
|
+
lex(stream).to_a
|
62
|
+
end
|
63
|
+
|
64
|
+
def lex(stream, &b)
|
65
|
+
return enum_for(:lex, stream) unless block_given?
|
66
|
+
|
67
|
+
stream_tokens(stream, &b)
|
68
|
+
end
|
69
|
+
|
70
|
+
def stream_tokens(stream, &b)
|
71
|
+
raise 'abstract'
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def force_load!
|
77
|
+
return self if @force_load
|
78
|
+
@force_load = true
|
79
|
+
instance_eval &@lazy_load_proc
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class RegexLexer < Lexer
|
86
|
+
class Rule
|
87
|
+
attr_reader :callback
|
88
|
+
attr_reader :next_lexer
|
89
|
+
attr_reader :re
|
90
|
+
def initialize(re, callback, next_lexer)
|
91
|
+
@orig_re = re
|
92
|
+
@re = Regexp.new %/\\A(?:#{re.source})/
|
93
|
+
@callback = callback
|
94
|
+
@next_lexer = next_lexer
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
"#<Rule #{@orig_re.inspect}>"
|
99
|
+
end
|
100
|
+
|
101
|
+
def consume(stream, &b)
|
102
|
+
# TODO: I'm sure there is a much faster way of doing this.
|
103
|
+
# also, encapsulate the stream in its own class.
|
104
|
+
match = stream.match(@re)
|
105
|
+
|
106
|
+
if match
|
107
|
+
stream.slice!(0...$&.size)
|
108
|
+
yield match
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize(parent=nil, opts={}, &defn)
|
117
|
+
if parent.is_a? Hash
|
118
|
+
opts = parent
|
119
|
+
parent = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
@parent = parent
|
123
|
+
super(opts, &defn)
|
124
|
+
end
|
125
|
+
|
126
|
+
def lexer(name, opts={}, &defn)
|
127
|
+
@scope ||= {}
|
128
|
+
name = name.to_s
|
129
|
+
|
130
|
+
if block_given?
|
131
|
+
l = @scope[name] = RegexLexer.new(self, options.merge(opts), &defn)
|
132
|
+
l.instance_variable_set :@name, name
|
133
|
+
l
|
134
|
+
else
|
135
|
+
@scope[name] || @parent && @parent.lexer(name)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def mixin(lexer)
|
140
|
+
lexer = get_lexer(lexer)
|
141
|
+
lexer.force_load!
|
142
|
+
|
143
|
+
rules << lexer
|
144
|
+
end
|
145
|
+
|
146
|
+
def rules
|
147
|
+
force_load!
|
148
|
+
@rules ||= []
|
149
|
+
end
|
150
|
+
|
151
|
+
def rule(re, token=nil, next_lexer=nil, &callback)
|
152
|
+
if block_given?
|
153
|
+
next_lexer = token
|
154
|
+
else
|
155
|
+
if token.is_a? String
|
156
|
+
token = Token[token]
|
157
|
+
end
|
158
|
+
|
159
|
+
callback = proc { |match, &b| b.call token, match }
|
160
|
+
end
|
161
|
+
|
162
|
+
rules << Rule.new(re, callback, get_lexer(next_lexer))
|
163
|
+
end
|
164
|
+
|
165
|
+
def stream_tokens(stream, &b)
|
166
|
+
stream = stream.dup
|
167
|
+
stack = [self]
|
168
|
+
|
169
|
+
stream_with_stack(stream.dup, [self], &b)
|
170
|
+
end
|
171
|
+
|
172
|
+
def stream_with_stack(stream, stack, &b)
|
173
|
+
return true if stream.empty?
|
174
|
+
|
175
|
+
until stream.empty?
|
176
|
+
debug { "stack: #{stack.map(&:name).inspect}" }
|
177
|
+
debug { "parsing #{stream.slice(0..20).inspect}" }
|
178
|
+
success = stack.last.step(stream, stack, &b)
|
179
|
+
|
180
|
+
if !success
|
181
|
+
debug { " no match, yielding Error" }
|
182
|
+
b.call(Token['Error'], stream.slice!(0..0))
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def step(stream, stack, &b)
|
188
|
+
rules.each do |rule|
|
189
|
+
return true if run_rule(rule, stream, stack, &b)
|
190
|
+
end
|
191
|
+
|
192
|
+
false
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
def get_lexer(o)
|
197
|
+
case o
|
198
|
+
when RegexLexer, :pop!
|
199
|
+
o
|
200
|
+
else
|
201
|
+
lexer o
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def run_rule(rule, stream, stack, &b)
|
206
|
+
case rule
|
207
|
+
when String, RegexLexer
|
208
|
+
lexer = get_lexer(rule)
|
209
|
+
debug { " entering mixin #{lexer.name}" }
|
210
|
+
get_lexer(rule).step(stream, stack, &b)
|
211
|
+
when Rule
|
212
|
+
debug { " trying #{rule.inspect}" }
|
213
|
+
rule.consume(stream) do |match|
|
214
|
+
debug { " got #{match[0].inspect}" }
|
215
|
+
|
216
|
+
rule.callback.call(*match) do |tok, res|
|
217
|
+
if tok.is_a? String
|
218
|
+
tok = Token[tok]
|
219
|
+
end
|
220
|
+
|
221
|
+
debug { " yielding #{tok.name.inspect}, #{res.inspect}" }
|
222
|
+
b.call(tok, res)
|
223
|
+
end
|
224
|
+
|
225
|
+
if rule.next_lexer == :pop!
|
226
|
+
debug { " popping stack" }
|
227
|
+
stack.pop
|
228
|
+
elsif rule.next_lexer
|
229
|
+
lexer = get_lexer(rule.next_lexer)
|
230
|
+
debug { " entering #{lexer.name}" }
|
231
|
+
stack.push lexer
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Rouge
|
2
|
+
module Lexers
|
3
|
+
JavascriptLexer = RegexLexer.create do
|
4
|
+
option :debug, true
|
5
|
+
|
6
|
+
name 'javascript'
|
7
|
+
aliases 'js'
|
8
|
+
|
9
|
+
lexer :comments_and_whitespace do
|
10
|
+
rule /\s+/, 'Text'
|
11
|
+
rule /<!--/, 'Comment' # really...?
|
12
|
+
rule %r(//.*?\n), 'Comment.Single'
|
13
|
+
rule %r(/\*.*?\*/), 'Comment.Multiline'
|
14
|
+
end
|
15
|
+
|
16
|
+
lexer :slash_starts_regex do
|
17
|
+
mixin :comments_and_whitespace
|
18
|
+
rule %r(
|
19
|
+
/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/ # a nonempty regex
|
20
|
+
(?:[gim]+\b|\B) # regex flags
|
21
|
+
)x, 'Literal.String.Regex'
|
22
|
+
|
23
|
+
# if it's not matched by the above r.e., it's not
|
24
|
+
# a valid expression, so we use :bad_regex to eat until the
|
25
|
+
# end of the line.
|
26
|
+
rule %r(/), 'Literal.String.Regex', :bad_regex
|
27
|
+
rule //, 'Text', :pop!
|
28
|
+
|
29
|
+
lexer :bad_regex do
|
30
|
+
rule /[^\n]+/, 'Error', :pop!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
keywords = %w(
|
35
|
+
for in while do break return continue switch case default if else
|
36
|
+
throw try catch finally new delete typeof instanceof void this
|
37
|
+
).join('|')
|
38
|
+
|
39
|
+
declarations = %w(var let with function).join('|')
|
40
|
+
|
41
|
+
reserved = %w(
|
42
|
+
abstract boolean byte char class const debugger double enum export
|
43
|
+
extends final float goto implements import int interface long
|
44
|
+
native package private protected public short static super
|
45
|
+
synchronized throws transient volatile
|
46
|
+
).join('|')
|
47
|
+
|
48
|
+
constants = %w(true false null NaN Infinity undefined).join('|')
|
49
|
+
|
50
|
+
builtins = %w(
|
51
|
+
Array Boolean Date Error Function Math netscape
|
52
|
+
Number Object Packages RegExp String sun decodeURI
|
53
|
+
decodeURIComponent encodeURI encodeURIComponent
|
54
|
+
Error eval isFinite isNaN parseFloat parseInt document this
|
55
|
+
window
|
56
|
+
).join('|')
|
57
|
+
|
58
|
+
lexer :root do
|
59
|
+
rule %r(^(?=\s|/|<!--)), 'Text', :slash_starts_regex
|
60
|
+
mixin :comments_and_whitespace
|
61
|
+
rule %r(\+\+|--|~|&&|\?|\|\||\\(?=\n)|<<|>>>?|===|!==),
|
62
|
+
'Operator', :slash_starts_regex
|
63
|
+
rule %r([-<>+*%&|\^/!=]=?), 'Operator', :slash_starts_regex
|
64
|
+
rule /[{(\[;,]/, 'Punctuation', :slash_starts_regex
|
65
|
+
rule /[})\].]/, 'Punctuation'
|
66
|
+
rule /(?:#{keywords})\b/, 'Keyword', :slash_starts_regex
|
67
|
+
rule /(?:#{declarations})\b/, 'Keyword.Declaration', :slash_starts_regex
|
68
|
+
rule /(?:#{reserved})\b/, 'Keyword.Reserved'
|
69
|
+
rule /(?:#{constants})\b/, 'Keyword.Constant'
|
70
|
+
rule /(?:#{builtins})\b/, 'Name.Builtin'
|
71
|
+
rule /[$a-zA-Z_][a-zA-Z0-9_]*/, 'Name.Other'
|
72
|
+
|
73
|
+
rule /[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?/, 'Number.Float'
|
74
|
+
rule /0x[0-9a-fA-F]+/, 'Number.Hex'
|
75
|
+
rule /[0-9]+/, 'Number.Integer'
|
76
|
+
rule /"(\\\\|\\"|[^"])*"/, 'Literal.String.Double'
|
77
|
+
rule /'(\\\\|\\'|[^'])*'/, 'Literal.String.Single'
|
78
|
+
end
|
79
|
+
|
80
|
+
mixin :root
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Rouge
|
2
|
+
module Lexers
|
3
|
+
ShellLexer = RegexLexer.create do
|
4
|
+
name 'shell'
|
5
|
+
aliases 'bash', 'zsh', 'ksh', 'sh'
|
6
|
+
|
7
|
+
KEYWORDS = %w(
|
8
|
+
if fi else while do done for then return function case
|
9
|
+
select continue until esac elif
|
10
|
+
).join('|')
|
11
|
+
|
12
|
+
BUILTINS = %w(
|
13
|
+
alias bg bind break builtin caller cd command compgen
|
14
|
+
complete declare dirs disown echo enable eval exec exit
|
15
|
+
export false fc fg getopts hash help history jobs kill let
|
16
|
+
local logout popd printf pushd pwd read readonly set shift
|
17
|
+
shopt source suspend test time times trap true type typeset
|
18
|
+
ulimit umask unalias unset wait
|
19
|
+
).join('|')
|
20
|
+
|
21
|
+
lexer :basic do
|
22
|
+
rule /#.*\n/, 'Comment'
|
23
|
+
|
24
|
+
rule /\b(#{KEYWORDS})\s*\b/, 'Keyword'
|
25
|
+
|
26
|
+
rule /\b(#{BUILTINS})\s*\b(?!\.)/, 'Name.Builtin'
|
27
|
+
|
28
|
+
rule /(\b\w+)(=)/ do |_, var, eq, &out|
|
29
|
+
out.call 'Name.Variable', var
|
30
|
+
out.call 'Operator', eq
|
31
|
+
end
|
32
|
+
|
33
|
+
rule /[\[\]{}()=]/, 'Operator'
|
34
|
+
rule /&&|\|\|/, 'Operator'
|
35
|
+
# rule /\|\|/, 'Operator'
|
36
|
+
|
37
|
+
rule /<<</, 'Operator' # here-string
|
38
|
+
rule /<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2/, 'Literal.String'
|
39
|
+
end
|
40
|
+
|
41
|
+
lexer :double_quotes do
|
42
|
+
rule /"/, 'Literal.String.Double', :pop!
|
43
|
+
rule /\\./, 'Literal.String.Escape'
|
44
|
+
mixin :interp
|
45
|
+
rule /[^"`\\$]+/, 'Literal.String.Double'
|
46
|
+
end
|
47
|
+
|
48
|
+
lexer :data do
|
49
|
+
# TODO: this should be its own sublexer so we can capture
|
50
|
+
# interpolation and such
|
51
|
+
rule /$?"/, 'Literal.String.Double', :double_quotes
|
52
|
+
|
53
|
+
# single quotes are much easier than double quotes - we can
|
54
|
+
# literally just scan until the next single quote.
|
55
|
+
# POSIX: Enclosing characters in single-quotes ( '' )
|
56
|
+
# shall preserve the literal value of each character within the
|
57
|
+
# single-quotes. A single-quote cannot occur within single-quotes.
|
58
|
+
rule /$?'[^']*'/, 'Literal.String.Single'
|
59
|
+
|
60
|
+
rule /;/, 'Text'
|
61
|
+
rule /\s+/, 'Text'
|
62
|
+
rule /[^=\s\[\]{}()$"\'`\\<]+/, 'Text'
|
63
|
+
rule /\d+(?= |\Z)/, 'Number'
|
64
|
+
rule /</, 'Text'
|
65
|
+
mixin :interp
|
66
|
+
end
|
67
|
+
|
68
|
+
lexer :curly do
|
69
|
+
rule /}/, 'Keyword', :pop!
|
70
|
+
rule /:-/, 'Keyword'
|
71
|
+
rule /[a-zA-Z0-9_]+/, 'Name.Variable'
|
72
|
+
rule /[^}:"'`$]+/, 'Punctuation'
|
73
|
+
mixin :root
|
74
|
+
end
|
75
|
+
|
76
|
+
lexer :paren do
|
77
|
+
rule /\)/, 'Keyword', :pop!
|
78
|
+
mixin :root
|
79
|
+
end
|
80
|
+
|
81
|
+
lexer :math do
|
82
|
+
rule /\)\)/, 'Keyword', :pop!
|
83
|
+
rule %r([-+*/%^|&]|\*\*|\|\|), 'Operator'
|
84
|
+
rule /\d+/, 'Number'
|
85
|
+
mixin :root
|
86
|
+
end
|
87
|
+
|
88
|
+
lexer :backticks do
|
89
|
+
rule /`/, 'Literal.String.Backtick', :pop!
|
90
|
+
mixin :root
|
91
|
+
end
|
92
|
+
|
93
|
+
lexer :interp do
|
94
|
+
rule /\$\(\(/, 'Keyword', :math
|
95
|
+
rule /\$\(/, 'Keyword', :paren
|
96
|
+
rule /\${#?/, 'Keyword', :curly
|
97
|
+
rule /`/, 'Literal.String.Backtick', :backticks
|
98
|
+
rule /\$#?(\w+|.)/, 'Name.Variable'
|
99
|
+
end
|
100
|
+
|
101
|
+
lexer :root do
|
102
|
+
mixin :basic
|
103
|
+
mixin :data
|
104
|
+
end
|
105
|
+
|
106
|
+
mixin :root
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/rouge/theme.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
module Rouge
|
2
|
+
class Theme
|
3
|
+
class << self
|
4
|
+
def styles
|
5
|
+
@styles ||= {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def main_style
|
9
|
+
@main_style ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def style(*tokens)
|
13
|
+
opts = {}
|
14
|
+
opts = tokens.pop if tokens.last.is_a? Hash
|
15
|
+
|
16
|
+
if tokens.empty?
|
17
|
+
@main_style = opts
|
18
|
+
end
|
19
|
+
|
20
|
+
tokens.each do |tok|
|
21
|
+
styles[tok.to_s] = opts
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def name(n=nil)
|
26
|
+
return @name if n.nil?
|
27
|
+
|
28
|
+
@name = n.to_s
|
29
|
+
registry[@name] = self
|
30
|
+
end
|
31
|
+
|
32
|
+
def find(n)
|
33
|
+
registry[n.to_s]
|
34
|
+
end
|
35
|
+
|
36
|
+
def registry
|
37
|
+
@registry ||= {}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class CSSTheme < Theme
|
43
|
+
def initialize(opts={})
|
44
|
+
@opts = opts
|
45
|
+
end
|
46
|
+
|
47
|
+
def render
|
48
|
+
out = []
|
49
|
+
stream { |line| out << line }
|
50
|
+
out.join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
def stream(&b)
|
54
|
+
return enum_for(:stream) unless block_given?
|
55
|
+
|
56
|
+
self.class.styles.each do |tokname, style|
|
57
|
+
stream_single(Token[tokname], style, &b)
|
58
|
+
end
|
59
|
+
|
60
|
+
render_stanza('.highlight', self.class.main_style, &b)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def stream_single(tok, style, &b)
|
65
|
+
render_stanza(css_selector(tok), style, &b)
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_stanza(selector, style, &b)
|
69
|
+
return if style.empty?
|
70
|
+
|
71
|
+
yield "#{selector} {"
|
72
|
+
yield " color: #{style[:fg]};" if style[:fg]
|
73
|
+
yield " background-color: #{style[:bg]};" if style[:bg]
|
74
|
+
yield " font-weight: bold;" if style[:bold]
|
75
|
+
yield " font-style: italic;" if style[:italic]
|
76
|
+
yield " text-decoration: underline;" if style[:underline]
|
77
|
+
|
78
|
+
(style[:rules] || []).each do |rule|
|
79
|
+
yield " #{rule};"
|
80
|
+
end
|
81
|
+
|
82
|
+
yield "}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def css_selector(token)
|
86
|
+
tokens = [token]
|
87
|
+
parent = token.parent
|
88
|
+
|
89
|
+
inflate_token(token).map do |tok|
|
90
|
+
base = ".highlight"
|
91
|
+
base << " .#{tok.shortname}" unless tok.shortname.empty?
|
92
|
+
|
93
|
+
base
|
94
|
+
end.join(', ')
|
95
|
+
end
|
96
|
+
|
97
|
+
# yield all of the tokens that should be styled the same
|
98
|
+
# as the given token. Essentially this recursively all of
|
99
|
+
# the subtokens, except those which are more specifically
|
100
|
+
# styled.
|
101
|
+
def inflate_token(tok, &b)
|
102
|
+
return enum_for(:inflate_token, tok) unless block_given?
|
103
|
+
|
104
|
+
yield tok
|
105
|
+
tok.sub_tokens.each_value do |st|
|
106
|
+
next if self.class.styles.include? st.name
|
107
|
+
|
108
|
+
inflate_token(st, &b)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rouge
|
2
|
+
module Themes
|
3
|
+
class ThankfulEyes < CSSTheme
|
4
|
+
# pallette, from GTKSourceView's ThankfulEyes
|
5
|
+
cool_as_ice = '#6c8b9f'
|
6
|
+
slate_blue = '#4e5d62'
|
7
|
+
eggshell_cloud = '#dee5e7'
|
8
|
+
krasna = '#122b3b'
|
9
|
+
aluminum1 = '#fefeec'
|
10
|
+
scarletred2 = '#cc0000'
|
11
|
+
butter3 = '#c4a000'
|
12
|
+
go_get_it = '#b2fd6d'
|
13
|
+
chilly = '#a8e1fe'
|
14
|
+
unicorn = '#faf6e4'
|
15
|
+
sandy = '#f6dd62'
|
16
|
+
pink_merengue = '#f696db'
|
17
|
+
dune = '#fff0a6'
|
18
|
+
backlit = '#4df4ff'
|
19
|
+
schrill = '#ffb000'
|
20
|
+
|
21
|
+
style :fg => unicorn, :bg => krasna
|
22
|
+
|
23
|
+
style 'Comment', :fg => cool_as_ice
|
24
|
+
style 'Error',
|
25
|
+
'Generic.Error', :fg => aluminum1, :bg => scarletred2
|
26
|
+
style 'Keyword', :fg => sandy, :bold => true
|
27
|
+
style 'Operator', :fg => backlit, :bold => true
|
28
|
+
style 'Comment.Preproc',
|
29
|
+
'Comment.Multiline',
|
30
|
+
'Comment.Single',
|
31
|
+
'Comment.Special', :fg => cool_as_ice, :italic => true
|
32
|
+
style 'Generic.Deleted', :fg => scarletred2
|
33
|
+
style 'Generic.Emph', :italic => true
|
34
|
+
style 'Generic.Subheading', :fg => '#800080', :bold => true
|
35
|
+
style 'Generic.Traceback', :fg => '#0040D0'
|
36
|
+
style 'Keyword.Constant', :fg => pink_merengue, :bold => true
|
37
|
+
style 'Keyword.Namespace',
|
38
|
+
'Keyword.Pseudo',
|
39
|
+
'Keyword.Reserved', :fg => schrill, :bold => true
|
40
|
+
style 'Keyword.Type',
|
41
|
+
'Name.Constant',
|
42
|
+
'Name.Class',
|
43
|
+
'Name.Decorator',
|
44
|
+
'Name.Namespace',
|
45
|
+
'Name.Builtin.Pseudo',
|
46
|
+
'Name.Exception', :fg => go_get_it, :bold => true
|
47
|
+
style 'Literal.Number', :fg => pink_merengue, :bold => true
|
48
|
+
style 'Literal.String', :fg => dune, :bold => true
|
49
|
+
style 'Literal.String.Escape',
|
50
|
+
'Literal.String.Char',
|
51
|
+
'Literal.String.Interpol',
|
52
|
+
'Literal.String.Other',
|
53
|
+
'Literal.String.Symbol', :fg => backlit, :bold => true
|
54
|
+
style 'Name.Attribute', :fg => '#7D9029'
|
55
|
+
style 'Name.Builtin', :fg => sandy
|
56
|
+
style 'Name.Entity', :fg => '#999999', :bold => true
|
57
|
+
style 'Name.Label', :fg => '#A0A000'
|
58
|
+
style 'Name.Tag', :fg => '#008000', :bold => true
|
59
|
+
style 'Text.Whitespace', :fg => '#BBBBBB'
|
60
|
+
style 'Name.Variable',
|
61
|
+
'Name.Function', :fg => chilly
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/rouge/token.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
module Rouge
|
2
|
+
class Token
|
3
|
+
attr_reader :name
|
4
|
+
attr_reader :parent
|
5
|
+
attr_accessor :shortname
|
6
|
+
|
7
|
+
def make_single(name)
|
8
|
+
name = name.to_s
|
9
|
+
new_name = [self.name, name].compact.join('.')
|
10
|
+
|
11
|
+
new_token = self.clone
|
12
|
+
parent = self
|
13
|
+
new_token.instance_eval do
|
14
|
+
@name = new_name
|
15
|
+
@parent = parent
|
16
|
+
@sub_tokens = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
sub_tokens[name] = new_token
|
20
|
+
|
21
|
+
new_token
|
22
|
+
end
|
23
|
+
|
24
|
+
def make(name, shortname=nil)
|
25
|
+
names = name.split('.')
|
26
|
+
names.inject(self) do |tok, name|
|
27
|
+
tok.make_single(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](name)
|
32
|
+
name = name.to_s
|
33
|
+
|
34
|
+
name.split('.').inject(self) do |tok, name|
|
35
|
+
tok.sub_tokens[name] || tok.make_single(name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def sub_tokens
|
40
|
+
@sub_tokens ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def ===(other)
|
44
|
+
immediate = if self.class == other.class
|
45
|
+
self == other
|
46
|
+
else
|
47
|
+
self.name == other
|
48
|
+
end
|
49
|
+
|
50
|
+
immediate || !!(other.parent && self === other.parent)
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
parts = [name.inspect]
|
55
|
+
parts << shortname.inspect if shortname
|
56
|
+
"#<Token #{parts.join(' ')}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
class << self
|
60
|
+
def base
|
61
|
+
@base ||= new
|
62
|
+
end
|
63
|
+
|
64
|
+
def get(name)
|
65
|
+
base[name]
|
66
|
+
end
|
67
|
+
|
68
|
+
def token(name, shortname)
|
69
|
+
tok = get(name)
|
70
|
+
tok.shortname = shortname
|
71
|
+
tok
|
72
|
+
end
|
73
|
+
|
74
|
+
alias [] get
|
75
|
+
|
76
|
+
def each_token(&b)
|
77
|
+
recurse = proc do |token|
|
78
|
+
b.call(token)
|
79
|
+
token.sub_tokens.each_value(&recurse)
|
80
|
+
end
|
81
|
+
|
82
|
+
base.sub_tokens.each_value(&recurse)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# XXX IMPORTANT XXX
|
87
|
+
# For compatibility, this list must be kept in sync with
|
88
|
+
# pygments.token.STANDARD_TYPES
|
89
|
+
token 'Text', ''
|
90
|
+
token 'Text.Whitespace', 'w'
|
91
|
+
token 'Error', 'err'
|
92
|
+
token 'Other', 'x'
|
93
|
+
|
94
|
+
token 'Keyword', 'k'
|
95
|
+
token 'Keyword.Constant', 'kc'
|
96
|
+
token 'Keyword.Declaration', 'kd'
|
97
|
+
token 'Keyword.Namespace', 'kn'
|
98
|
+
token 'Keyword.Pseudo', 'kp'
|
99
|
+
token 'Keyword.Reserved', 'kr'
|
100
|
+
token 'Keyword.Type', 'kt'
|
101
|
+
|
102
|
+
token 'Name', 'n'
|
103
|
+
token 'Name.Attribute', 'na'
|
104
|
+
token 'Name.Builtin', 'nb'
|
105
|
+
token 'Name.Builtin.Pseudo', 'bp'
|
106
|
+
token 'Name.Class', 'nc'
|
107
|
+
token 'Name.Constant', 'no'
|
108
|
+
token 'Name.Decorator', 'nd'
|
109
|
+
token 'Name.Entity', 'ni'
|
110
|
+
token 'Name.Exception', 'ne'
|
111
|
+
token 'Name.Function', 'nf'
|
112
|
+
token 'Name.Property', 'py'
|
113
|
+
token 'Name.Label', 'nl'
|
114
|
+
token 'Name.Namespace', 'nn'
|
115
|
+
token 'Name.Other', 'nx'
|
116
|
+
token 'Name.Tag', 'nt'
|
117
|
+
token 'Name.Variable', 'nv'
|
118
|
+
token 'Name.Variable.Class', 'vc'
|
119
|
+
token 'Name.Variable.Global', 'vg'
|
120
|
+
token 'Name.Variable.Instance', 'vi'
|
121
|
+
|
122
|
+
token 'Literal', 'l'
|
123
|
+
token 'Literal.Date', 'ld'
|
124
|
+
|
125
|
+
token 'Literal.String', 's'
|
126
|
+
token 'Literal.String.Backtick', 'sb'
|
127
|
+
token 'Literal.String.Char', 'sc'
|
128
|
+
token 'Literal.String.Doc', 'sd'
|
129
|
+
token 'Literal.String.Double', 's2'
|
130
|
+
token 'Literal.String.Escape', 'se'
|
131
|
+
token 'Literal.String.Heredoc', 'sh'
|
132
|
+
token 'Literal.String.Interpol', 'si'
|
133
|
+
token 'Literal.String.Other', 'sx'
|
134
|
+
token 'Literal.String.Regex', 'sr'
|
135
|
+
token 'Literal.String.Single', 's1'
|
136
|
+
token 'Literal.String.Symbol', 'ss'
|
137
|
+
|
138
|
+
token 'Literal.Number', 'm'
|
139
|
+
token 'Literal.Number.Float', 'mf'
|
140
|
+
token 'Literal.Number.Hex', 'mh'
|
141
|
+
token 'Literal.Number.Integer', 'mi'
|
142
|
+
token 'Literal.Number.Integer.Long', 'il'
|
143
|
+
token 'Literal.Number.Oct', 'mo'
|
144
|
+
|
145
|
+
token 'Operator', 'o'
|
146
|
+
token 'Operator.Word', 'ow'
|
147
|
+
|
148
|
+
token 'Punctuation', 'p'
|
149
|
+
|
150
|
+
token 'Comment', 'c'
|
151
|
+
token 'Comment.Multiline', 'cm'
|
152
|
+
token 'Comment.Preproc', 'cp'
|
153
|
+
token 'Comment.Single', 'c1'
|
154
|
+
token 'Comment.Special', 'cs'
|
155
|
+
|
156
|
+
token 'Generic', 'g'
|
157
|
+
token 'Generic.Deleted', 'gd'
|
158
|
+
token 'Generic.Emph', 'ge'
|
159
|
+
token 'Generic.Error', 'gr'
|
160
|
+
token 'Generic.Heading', 'gh'
|
161
|
+
token 'Generic.Inserted', 'gi'
|
162
|
+
token 'Generic.Output', 'go'
|
163
|
+
token 'Generic.Prompt', 'gp'
|
164
|
+
token 'Generic.Strong', 'gs'
|
165
|
+
token 'Generic.Subheading', 'gu'
|
166
|
+
token 'Generic.Traceback', 'gt'
|
167
|
+
end
|
168
|
+
end
|
data/lib/rouge/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rouge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-01 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: see the description for now
|
15
15
|
email:
|
@@ -19,7 +19,15 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- Gemfile
|
22
|
+
- lib/rouge/lexers/shell.rb
|
23
|
+
- lib/rouge/lexers/javascript.rb
|
24
|
+
- lib/rouge/themes/thankful_eyes.rb
|
25
|
+
- lib/rouge/token.rb
|
26
|
+
- lib/rouge/formatters/html.rb
|
22
27
|
- lib/rouge/version.rb
|
28
|
+
- lib/rouge/formatter.rb
|
29
|
+
- lib/rouge/lexer.rb
|
30
|
+
- lib/rouge/theme.rb
|
23
31
|
- lib/rouge.rb
|
24
32
|
homepage: http://github.com/jayferd/rouge
|
25
33
|
licenses: []
|