rouge 0.0.6 → 0.0.7

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.
@@ -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: