rouge 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -13,6 +13,7 @@ module Rouge
13
13
  end
14
14
 
15
15
  load_dir = Pathname.new(__FILE__).dirname
16
+ load load_dir.join('rouge/text_analyzer.rb')
16
17
  load load_dir.join('rouge/token.rb')
17
18
  load load_dir.join('rouge/lexer.rb')
18
19
  load load_dir.join('rouge/lexers/text.rb')
@@ -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
@@ -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 extensions(*exts)
60
- exts.each do |ext|
61
- Lexer.extension_registry[ext] = self
62
- end
106
+ def filenames(*fnames)
107
+ (@filenames ||= []).concat(fnames)
63
108
  end
64
109
 
65
- def extension_registry
66
- @extension_registry ||= {}
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
@@ -1,8 +1,9 @@
1
1
  module Rouge
2
2
  module Lexers
3
- class CSSLexer < RegexLexer
3
+ class CSS < RegexLexer
4
4
  tag 'css'
5
- extensions 'css'
5
+ filenames '*.css'
6
+ mimetypes 'text/css'
6
7
 
7
8
  identifier = /[a-zA-Z0-9_-]+/
8
9
  number = /-?(?:[0-9]+(\.[0-9]+)?|\.[0-9]+)/
@@ -2,8 +2,16 @@ module Rouge
2
2
  module Lexers
3
3
  class Diff < RegexLexer
4
4
  tag 'diff'
5
- aliases 'patch'
6
- extensions 'diff', 'patch'
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'
@@ -2,7 +2,13 @@ module Rouge
2
2
  module Lexers
3
3
  class HTML < RegexLexer
4
4
  tag 'html'
5
- extensions 'htm', 'html'
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 JavascriptLexer
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 CSSLexer
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 JavascriptLexer < RegexLexer
3
+ class Javascript < RegexLexer
4
4
  tag 'javascript'
5
5
  aliases 'js'
6
- extensions 'js'
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
@@ -3,7 +3,12 @@ module Rouge
3
3
  class Python < RegexLexer
4
4
  tag 'python'
5
5
  aliases 'py'
6
- extensions 'py'
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*)([rRuU]{,2}""".*?""")/m do
53
+ rule /^(:)(\s*)([ru]{,2}""".*?""")/mi do
54
+ group 'Punctuation'
49
55
  group 'Text'
50
56
  group 'Literal.String.Doc'
51
57
  end
@@ -1,9 +1,14 @@
1
1
  module Rouge
2
2
  module Lexers
3
- class ShellLexer < RegexLexer
3
+ class Shell < RegexLexer
4
4
  tag 'shell'
5
5
  aliases 'bash', 'zsh', 'ksh', 'sh'
6
- extensions 'sh', 'bash', 'zsh', 'ksh'
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
@@ -1,7 +1,15 @@
1
1
  module Rouge
2
2
  module Lexers
3
- class TCLLexer < RegexLexer
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
@@ -2,6 +2,8 @@ module Rouge
2
2
  module Lexers
3
3
  class Text < Lexer
4
4
  tag 'text'
5
+ filenames '*.txt'
6
+ mimetypes 'text/plain'
5
7
 
6
8
  def stream_tokens(stream, &b)
7
9
  yield Token['Text'], stream.string
@@ -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
@@ -176,7 +176,7 @@ module Rouge
176
176
  return enum_for(:inflate_token, tok) unless block_given?
177
177
 
178
178
  yield tok
179
- tok.sub_tokens.each_value do |st|
179
+ tok.sub_tokens.each do |(_, st)|
180
180
  next if styles[st.name]
181
181
 
182
182
  inflate_token(st, &b)
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Rouge
2
2
  def self.version
3
- "0.0.6"
3
+ "0.0.7"
4
4
  end
5
5
  end
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.6
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-06 00:00:00.000000000 Z
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: