rouge 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rougify +13 -0
- data/lib/rouge.rb +1 -0
- data/lib/rouge/cli.rb +77 -0
- data/lib/rouge/lexer.rb +58 -6
- data/lib/rouge/lexers/css.rb +3 -2
- data/lib/rouge/lexers/diff.rb +10 -2
- data/lib/rouge/lexers/html.rb +9 -3
- data/lib/rouge/lexers/javascript.rb +66 -2
- data/lib/rouge/lexers/python.rb +8 -2
- data/lib/rouge/lexers/shell.rb +7 -2
- data/lib/rouge/lexers/tcl.rb +9 -1
- data/lib/rouge/lexers/text.rb +2 -0
- data/lib/rouge/text_analyzer.rb +37 -0
- data/lib/rouge/theme.rb +1 -1
- data/lib/rouge/themes/thankful_eyes.rb +1 -5
- data/lib/rouge/version.rb +1 -1
- metadata +24 -4
data/bin/rougify
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
ROOT_DIR = Pathname.new(__FILE__).dirname.parent
|
5
|
+
load ROOT_DIR.join('lib/rouge.rb')
|
6
|
+
load ROOT_DIR.join('lib/rouge/cli.rb')
|
7
|
+
|
8
|
+
begin
|
9
|
+
Rouge::CLI.start
|
10
|
+
rescue => e
|
11
|
+
$stderr.puts e.message
|
12
|
+
exit 1
|
13
|
+
end
|
data/lib/rouge.rb
CHANGED
data/lib/rouge/cli.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# not required by the main lib.
|
2
|
+
# to use this module, require 'rouge/cli'.
|
3
|
+
|
4
|
+
# stdlib
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
# gems
|
8
|
+
require 'thor'
|
9
|
+
|
10
|
+
module Rouge
|
11
|
+
class CLI < Thor
|
12
|
+
default_task :highlight
|
13
|
+
|
14
|
+
def self.start(argv=ARGV, *a)
|
15
|
+
unless %w(highlight style).include?(argv.first)
|
16
|
+
argv.unshift 'highlight'
|
17
|
+
end
|
18
|
+
|
19
|
+
super(argv, *a)
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'highlight [FILE]', 'highlight some code'
|
23
|
+
option :file, :aliases => '-f', :desc => 'the file to operate on'
|
24
|
+
option :lexer, :aliases => '-l',
|
25
|
+
:desc => ('Which lexer to use. If not provided, rougify will try to ' +
|
26
|
+
'guess based on --mimetype, the filename, and the file ' +
|
27
|
+
'contents.')
|
28
|
+
option :mimetype, :aliases => '-m',
|
29
|
+
:desc => ('a mimetype that Rouge will use to guess the correct lexer. ' +
|
30
|
+
'This is ignored if --lexer is specified.')
|
31
|
+
option :lexer_opts, :aliases => '-L', :type => :hash, :default => {},
|
32
|
+
:desc => ('a hash of options to pass to the lexer.')
|
33
|
+
option :formatter_opts, :aliases => '-F', :type => :hash, :default => {},
|
34
|
+
:desc => ('a hash of options to pass to the formatter.')
|
35
|
+
def highlight(file=nil)
|
36
|
+
filename = options[:file] || file
|
37
|
+
source = filename ? File.read(filename) : $stdin.read
|
38
|
+
|
39
|
+
if options[:lexer].nil?
|
40
|
+
lexer_class = Lexer.guess(
|
41
|
+
:filename => filename,
|
42
|
+
:mimetype => options[:mimetype],
|
43
|
+
:source => source,
|
44
|
+
)
|
45
|
+
else
|
46
|
+
lexer_class = Lexer.find(options[:lexer])
|
47
|
+
raise "unknown lexer: #{options[:lexer]}" unless lexer_class
|
48
|
+
end
|
49
|
+
|
50
|
+
# only HTML is supported for now
|
51
|
+
formatter = Formatters::HTML.new(normalize_hash_keys(options[:formatter_opts]))
|
52
|
+
lexer = lexer_class.new(normalize_hash_keys(options[:lexer_opts]))
|
53
|
+
|
54
|
+
puts Rouge.highlight(source, lexer, formatter)
|
55
|
+
end
|
56
|
+
|
57
|
+
desc 'style THEME', 'render THEME as css'
|
58
|
+
def style(theme_name='thankful_eyes')
|
59
|
+
theme = Theme.find(theme_name)
|
60
|
+
raise "unknown theme: #{theme_name}" unless theme
|
61
|
+
|
62
|
+
puts theme.new(options).render
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
# TODO: does Thor do this for me?
|
67
|
+
def normalize_hash_keys(hash)
|
68
|
+
out = {}
|
69
|
+
hash.each do |k, v|
|
70
|
+
new_key = k.tr('-', '_').to_sym
|
71
|
+
out[new_key] = v
|
72
|
+
end
|
73
|
+
|
74
|
+
out
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/rouge/lexer.rb
CHANGED
@@ -41,6 +41,53 @@ module Rouge
|
|
41
41
|
registry[name.to_s]
|
42
42
|
end
|
43
43
|
|
44
|
+
def guess(info={})
|
45
|
+
by_mimetype = guess_by_mimetype(info[:mimetype]) if info[:mimetype]
|
46
|
+
return by_mimetype if by_mimetype
|
47
|
+
|
48
|
+
by_filename = guess_by_filename(info[:filename]) if info[:filename]
|
49
|
+
return by_filename if by_filename
|
50
|
+
|
51
|
+
by_source = guess_by_source(info[:source]) if info[:source]
|
52
|
+
return by_source if by_source
|
53
|
+
|
54
|
+
# guessing failed, just parse it as text
|
55
|
+
return Lexers::Text
|
56
|
+
end
|
57
|
+
|
58
|
+
def guess_by_mimetype(mt)
|
59
|
+
registry.values.detect do |lexer|
|
60
|
+
lexer.mimetypes.include? mt
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def guess_by_filename(fname)
|
65
|
+
fname = File.basename(fname)
|
66
|
+
registry.values.detect do |lexer|
|
67
|
+
lexer.filenames.any? do |pattern|
|
68
|
+
File.fnmatch?(pattern, fname)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def guess_by_source(source)
|
74
|
+
source = TextAnalyzer.new(source)
|
75
|
+
|
76
|
+
best_result = 0
|
77
|
+
best_match = nil
|
78
|
+
registry.values.each do |lexer|
|
79
|
+
result = lexer.analyze_text(source) || 0
|
80
|
+
return lexer if result == 1
|
81
|
+
|
82
|
+
if result > best_result
|
83
|
+
best_match = lexer
|
84
|
+
best_result = result
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
best_match
|
89
|
+
end
|
90
|
+
|
44
91
|
def register(name, lexer)
|
45
92
|
registry[name.to_s] = lexer
|
46
93
|
end
|
@@ -56,14 +103,12 @@ module Rouge
|
|
56
103
|
args.each { |arg| Lexer.register(arg, self) }
|
57
104
|
end
|
58
105
|
|
59
|
-
def
|
60
|
-
|
61
|
-
Lexer.extension_registry[ext] = self
|
62
|
-
end
|
106
|
+
def filenames(*fnames)
|
107
|
+
(@filenames ||= []).concat(fnames)
|
63
108
|
end
|
64
109
|
|
65
|
-
def
|
66
|
-
@
|
110
|
+
def mimetypes(*mts)
|
111
|
+
(@mimetypes ||= []).concat(mts)
|
67
112
|
end
|
68
113
|
|
69
114
|
private
|
@@ -125,6 +170,13 @@ module Rouge
|
|
125
170
|
def stream_tokens(stream, &b)
|
126
171
|
raise 'abstract'
|
127
172
|
end
|
173
|
+
|
174
|
+
# return a number between 0 and 1 indicating the
|
175
|
+
# likelihood that the text given should be lexed
|
176
|
+
# with this lexer.
|
177
|
+
def self.analyze_text(text)
|
178
|
+
0
|
179
|
+
end
|
128
180
|
end
|
129
181
|
|
130
182
|
class RegexLexer < Lexer
|
data/lib/rouge/lexers/css.rb
CHANGED
data/lib/rouge/lexers/diff.rb
CHANGED
@@ -2,8 +2,16 @@ module Rouge
|
|
2
2
|
module Lexers
|
3
3
|
class Diff < RegexLexer
|
4
4
|
tag 'diff'
|
5
|
-
aliases 'patch'
|
6
|
-
|
5
|
+
aliases 'patch', 'udiff'
|
6
|
+
filenames '*.diff', '*.patch'
|
7
|
+
mimetypes 'text/x-diff', 'text/x-patch'
|
8
|
+
|
9
|
+
def self.analyze_text(text)
|
10
|
+
return 1 if text.start_with?('Index: ')
|
11
|
+
return 1 if text.start_with?('diff ')
|
12
|
+
|
13
|
+
return 0.9 if text =~ /\A---.*?\n\+\+\+/m
|
14
|
+
end
|
7
15
|
|
8
16
|
state :header do
|
9
17
|
rule /^diff .*?\n(?=---|\+\+\+)/m, 'Generic.Heading'
|
data/lib/rouge/lexers/html.rb
CHANGED
@@ -2,7 +2,13 @@ module Rouge
|
|
2
2
|
module Lexers
|
3
3
|
class HTML < RegexLexer
|
4
4
|
tag 'html'
|
5
|
-
|
5
|
+
filenames '*.htm', '*.html', '*.xhtml', '*.xslt'
|
6
|
+
mimetypes 'text/html', 'application/xhtml+xml'
|
7
|
+
|
8
|
+
def self.analyze_text(text)
|
9
|
+
return 1 if text.doctype?(/\bhtml\b/i)
|
10
|
+
return 1 if text =~ /<\s*html\b/
|
11
|
+
end
|
6
12
|
|
7
13
|
state :root do
|
8
14
|
rule /[^<&]+/m, 'Text'
|
@@ -50,14 +56,14 @@ module Rouge
|
|
50
56
|
state :script_content do
|
51
57
|
rule %r(<\s*/\s*script\s*>)m, 'Name.Tag', :pop!
|
52
58
|
rule %r(.*?(?=<\s*/\s*script\s*>))m do
|
53
|
-
delegate
|
59
|
+
delegate Javascript
|
54
60
|
end
|
55
61
|
end
|
56
62
|
|
57
63
|
state :style_content do
|
58
64
|
rule %r(<\s*/\s*style\s*>)m, 'Name.Tag', :pop!
|
59
65
|
rule %r(.*(?=<\s*/\s*style\s*>))m do
|
60
|
-
delegate
|
66
|
+
delegate CSS
|
61
67
|
end
|
62
68
|
end
|
63
69
|
end
|
@@ -1,9 +1,17 @@
|
|
1
1
|
module Rouge
|
2
2
|
module Lexers
|
3
|
-
class
|
3
|
+
class Javascript < RegexLexer
|
4
4
|
tag 'javascript'
|
5
5
|
aliases 'js'
|
6
|
-
|
6
|
+
filenames '*.js'
|
7
|
+
mimetypes 'application/javascript', 'application/x-javascript',
|
8
|
+
'text/javascript', 'text/x-javascript'
|
9
|
+
|
10
|
+
def self.analyze_text(text)
|
11
|
+
return 1 if text.shebang?('node')
|
12
|
+
return 1 if text.shebang?('jsc')
|
13
|
+
# TODO: rhino, spidermonkey, etc
|
14
|
+
end
|
7
15
|
|
8
16
|
state :comments_and_whitespace do
|
9
17
|
rule /\s+/, 'Text'
|
@@ -61,6 +69,7 @@ module Rouge
|
|
61
69
|
).join('|')
|
62
70
|
|
63
71
|
state :root do
|
72
|
+
rule /\A\s*#!.*?\n/m, 'Comment.Preproc'
|
64
73
|
rule %r(^(?=\s|/|<!--)), 'Text', :slash_starts_regex
|
65
74
|
mixin :comments_and_whitespace
|
66
75
|
rule %r(\+\+ | -- | ~ | && | \|\| | \\(?=\n) | << | >>>? | ===
|
@@ -83,5 +92,60 @@ module Rouge
|
|
83
92
|
rule /'(\\\\|\\'|[^'])*'/, 'Literal.String.Single'
|
84
93
|
end
|
85
94
|
end
|
95
|
+
|
96
|
+
class JSON < RegexLexer
|
97
|
+
tag 'json'
|
98
|
+
filenames '*.json'
|
99
|
+
mimetypes 'application/json'
|
100
|
+
|
101
|
+
# TODO: is this too much of a performance hit? JSON is quite simple,
|
102
|
+
# so I'd think this wouldn't be too bad, but for large documents this
|
103
|
+
# could mean doing two full lexes.
|
104
|
+
def self.analyze_text(text)
|
105
|
+
text.lexes_cleanly?(self) ? 0.8 : 0
|
106
|
+
end
|
107
|
+
|
108
|
+
state :root do
|
109
|
+
mixin :whitespace
|
110
|
+
# special case for empty objects
|
111
|
+
rule /(\{)(\s*)(\})/ do
|
112
|
+
group 'Punctuation'
|
113
|
+
group 'Text.Whitespace'
|
114
|
+
group 'Punctuation'
|
115
|
+
end
|
116
|
+
rule /{/, 'Punctuation', :object_key
|
117
|
+
rule /\[/, 'Punctuation', :array
|
118
|
+
rule /-?(?:0|[1-9]\d*)\.\d+(?:e[+-]\d+)?/i, 'Literal.Number.Float'
|
119
|
+
rule /-?(?:0|[1-9]\d*)(?:e[+-]\d+)?/i, 'Literal.Number.Integer'
|
120
|
+
mixin :has_string
|
121
|
+
end
|
122
|
+
|
123
|
+
state :whitespace do
|
124
|
+
rule /\s+/m, 'Text.Whitespace'
|
125
|
+
end
|
126
|
+
|
127
|
+
state :has_string do
|
128
|
+
rule /"(\\.|[^"])*"/, 'Literal.String.Double'
|
129
|
+
end
|
130
|
+
|
131
|
+
state :object_key do
|
132
|
+
mixin :whitespace
|
133
|
+
rule /:/, 'Punctuation', :object_val
|
134
|
+
rule /}/, 'Error', :pop!
|
135
|
+
mixin :has_string
|
136
|
+
end
|
137
|
+
|
138
|
+
state :object_val do
|
139
|
+
rule /,/, 'Punctuation', :pop!
|
140
|
+
rule(/}/) { token 'Punctuation'; pop!; pop! }
|
141
|
+
mixin :root
|
142
|
+
end
|
143
|
+
|
144
|
+
state :array do
|
145
|
+
rule /\]/, 'Punctuation', :pop!
|
146
|
+
rule /,/, 'Punctuation'
|
147
|
+
mixin :root
|
148
|
+
end
|
149
|
+
end
|
86
150
|
end
|
87
151
|
end
|
data/lib/rouge/lexers/python.rb
CHANGED
@@ -3,7 +3,12 @@ module Rouge
|
|
3
3
|
class Python < RegexLexer
|
4
4
|
tag 'python'
|
5
5
|
aliases 'py'
|
6
|
-
|
6
|
+
filenames '*.py', '*.pyw', '*.sc', 'SConstruct', 'SConscript', '*.tac'
|
7
|
+
mimetypes 'text/x-python', 'application/x-python'
|
8
|
+
|
9
|
+
def self.analyze_text(text)
|
10
|
+
return 1 if text.shebang?(/pythonw?(3|2(\.\d)?)?/)
|
11
|
+
end
|
7
12
|
|
8
13
|
keywords = %w(
|
9
14
|
assert break continue del elif else except exec
|
@@ -45,7 +50,8 @@ module Rouge
|
|
45
50
|
dotted_identifier = /[a-z_.][a-z0-9_.]*/i
|
46
51
|
state :root do
|
47
52
|
rule /\n+/m, 'Text'
|
48
|
-
rule /^(\s*)([
|
53
|
+
rule /^(:)(\s*)([ru]{,2}""".*?""")/mi do
|
54
|
+
group 'Punctuation'
|
49
55
|
group 'Text'
|
50
56
|
group 'Literal.String.Doc'
|
51
57
|
end
|
data/lib/rouge/lexers/shell.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Rouge
|
2
2
|
module Lexers
|
3
|
-
class
|
3
|
+
class Shell < RegexLexer
|
4
4
|
tag 'shell'
|
5
5
|
aliases 'bash', 'zsh', 'ksh', 'sh'
|
6
|
-
|
6
|
+
filenames '*.sh', '*.bash', '*.zsh', '*.ksh'
|
7
|
+
mimetypes 'application/x-sh', 'application/x-shellscript'
|
8
|
+
|
9
|
+
def self.analyze_text(text)
|
10
|
+
text.shebang?(/(ba|z|k)?sh/) ? 1 : 0
|
11
|
+
end
|
7
12
|
|
8
13
|
KEYWORDS = %w(
|
9
14
|
if fi else while do done for then return function
|
data/lib/rouge/lexers/tcl.rb
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
module Rouge
|
2
2
|
module Lexers
|
3
|
-
class
|
3
|
+
class TCL < RegexLexer
|
4
4
|
tag 'tcl'
|
5
|
+
filenames '*.tcl'
|
6
|
+
mimetypes 'text/x-tcl', 'text/x-script.tcl', 'application/x-tcl'
|
7
|
+
|
8
|
+
def self.analyze_text(text)
|
9
|
+
return 1 if text.shebang? 'tclsh'
|
10
|
+
return 1 if text.shebang? 'wish'
|
11
|
+
return 1 if text.shebang? 'jimsh'
|
12
|
+
end
|
5
13
|
|
6
14
|
KEYWORDS = %w(
|
7
15
|
after apply array break catch continue elseif else error
|
data/lib/rouge/lexers/text.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rouge
|
2
|
+
class TextAnalyzer < String
|
3
|
+
def shebang
|
4
|
+
return @shebang if instance_variable_defined? :@shebang
|
5
|
+
|
6
|
+
self =~ /\A\s*#!(.*)$/
|
7
|
+
@shebang = $1
|
8
|
+
end
|
9
|
+
|
10
|
+
def shebang?(match)
|
11
|
+
match = /\b#{match}(\s|$)/
|
12
|
+
match === shebang
|
13
|
+
end
|
14
|
+
|
15
|
+
def doctype
|
16
|
+
return @doctype if instance_variable_defined? :@doctype
|
17
|
+
|
18
|
+
self =~ %r(\A\s*
|
19
|
+
(?:<\?.*?\?>\s*)? # possible <?xml...?> tag
|
20
|
+
<!DOCTYPE\s+(.+?)>
|
21
|
+
)xm
|
22
|
+
@doctype = $1
|
23
|
+
end
|
24
|
+
|
25
|
+
def doctype?(type)
|
26
|
+
type === doctype
|
27
|
+
end
|
28
|
+
|
29
|
+
def lexes_cleanly?(lexer)
|
30
|
+
lexer.lex(self) do |(tok, _)|
|
31
|
+
return false if tok.name == 'Error'
|
32
|
+
end
|
33
|
+
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/rouge/theme.rb
CHANGED
@@ -22,16 +22,12 @@ module Rouge
|
|
22
22
|
|
23
23
|
style 'Text', :fg => :unicorn, :bg => :krasna
|
24
24
|
|
25
|
-
style 'Comment', :fg => :cool_as_ice
|
25
|
+
style 'Comment', :fg => :cool_as_ice, :italic => true
|
26
26
|
style 'Error',
|
27
27
|
'Generic.Error', :fg => :aluminum1, :bg => :scarletred2
|
28
28
|
style 'Keyword', :fg => :sandy, :bold => true
|
29
29
|
style 'Operator',
|
30
30
|
'Punctuation', :fg => :backlit, :bold => true
|
31
|
-
style 'Comment.Preproc',
|
32
|
-
'Comment.Multiline',
|
33
|
-
'Comment.Single',
|
34
|
-
'Comment.Special', :fg => :cool_as_ice, :italic => true
|
35
31
|
style 'Generic.Deleted', :fg => :scarletred2
|
36
32
|
style 'Generic.Inserted', :fg => :go_get_it
|
37
33
|
style 'Generic.Emph', :italic => true
|
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.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,29 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-09-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
14
30
|
description: Rouge aims to a be a simple, easy-to-extend drop-in replacement for pygments.
|
15
31
|
email:
|
16
32
|
- jjmadkisson@gmail.com
|
17
|
-
executables:
|
33
|
+
executables:
|
34
|
+
- rougify
|
18
35
|
extensions: []
|
19
36
|
extra_rdoc_files: []
|
20
37
|
files:
|
@@ -33,11 +50,14 @@ files:
|
|
33
50
|
- lib/rouge/themes/base16.rb
|
34
51
|
- lib/rouge/token.rb
|
35
52
|
- lib/rouge/formatters/html.rb
|
53
|
+
- lib/rouge/text_analyzer.rb
|
36
54
|
- lib/rouge/version.rb
|
37
55
|
- lib/rouge/formatter.rb
|
56
|
+
- lib/rouge/cli.rb
|
38
57
|
- lib/rouge/lexer.rb
|
39
58
|
- lib/rouge/theme.rb
|
40
59
|
- lib/rouge.rb
|
60
|
+
- bin/rougify
|
41
61
|
homepage: http://github.com/jayferd/rouge
|
42
62
|
licenses: []
|
43
63
|
post_install_message:
|