coderay 1.0.9 → 1.1.0.rc1

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +2 -0
  3. data/bin/coderay +4 -4
  4. data/lib/coderay.rb +2 -3
  5. data/lib/coderay/encoders/debug.rb +5 -17
  6. data/lib/coderay/encoders/debug_lint.rb +62 -0
  7. data/lib/coderay/encoders/html.rb +84 -84
  8. data/lib/coderay/encoders/html/css.rb +7 -7
  9. data/lib/coderay/encoders/html/numbering.rb +24 -19
  10. data/lib/coderay/encoders/html/output.rb +1 -1
  11. data/lib/coderay/encoders/lint.rb +57 -0
  12. data/lib/coderay/encoders/statistic.rb +0 -1
  13. data/lib/coderay/encoders/terminal.rb +121 -105
  14. data/lib/coderay/helpers/file_type.rb +54 -47
  15. data/lib/coderay/helpers/plugin.rb +4 -13
  16. data/lib/coderay/scanner.rb +58 -26
  17. data/lib/coderay/scanners/c.rb +1 -1
  18. data/lib/coderay/scanners/cpp.rb +1 -1
  19. data/lib/coderay/scanners/css.rb +22 -25
  20. data/lib/coderay/scanners/diff.rb +53 -31
  21. data/lib/coderay/scanners/groovy.rb +17 -4
  22. data/lib/coderay/scanners/html.rb +38 -16
  23. data/lib/coderay/scanners/java.rb +1 -1
  24. data/lib/coderay/scanners/java_script.rb +30 -6
  25. data/lib/coderay/scanners/json.rb +15 -12
  26. data/lib/coderay/scanners/lua.rb +280 -0
  27. data/lib/coderay/scanners/php.rb +22 -4
  28. data/lib/coderay/scanners/python.rb +3 -3
  29. data/lib/coderay/scanners/raydebug.rb +8 -8
  30. data/lib/coderay/scanners/ruby.rb +2 -2
  31. data/lib/coderay/scanners/sass.rb +232 -0
  32. data/lib/coderay/scanners/sql.rb +7 -4
  33. data/lib/coderay/scanners/taskpaper.rb +36 -0
  34. data/lib/coderay/scanners/yaml.rb +2 -2
  35. data/lib/coderay/styles/alpha.rb +31 -21
  36. data/lib/coderay/token_kinds.rb +68 -71
  37. data/lib/coderay/tokens.rb +23 -77
  38. data/lib/coderay/version.rb +1 -1
  39. data/test/functional/examples.rb +3 -3
  40. data/test/functional/for_redcloth.rb +4 -10
  41. metadata +13 -14
  42. data/lib/coderay/helpers/gzip.rb +0 -41
@@ -131,7 +131,7 @@ module CodeRay
131
131
 
132
132
  # A Hash of plugion_id => Plugin pairs.
133
133
  def plugin_hash
134
- @plugin_hash ||= make_plugin_hash
134
+ @plugin_hash ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map }
135
135
  end
136
136
 
137
137
  # Returns an array of all .rb files in the plugin path.
@@ -158,7 +158,6 @@ module CodeRay
158
158
  # This is done automatically when plugin_path is called.
159
159
  def load_plugin_map
160
160
  mapfile = path_to '_map'
161
- @plugin_map_loaded = true
162
161
  if File.exist? mapfile
163
162
  require mapfile
164
163
  true
@@ -171,23 +170,16 @@ module CodeRay
171
170
 
172
171
  # Return a plugin hash that automatically loads plugins.
173
172
  def make_plugin_hash
174
- @plugin_map_loaded ||= false
175
173
  Hash.new do |h, plugin_id|
176
174
  id = validate_id(plugin_id)
177
175
  path = path_to id
178
176
  begin
179
177
  require path
180
178
  rescue LoadError => boom
181
- if @plugin_map_loaded
182
- if h.has_key?(:default)
183
- warn '%p could not load plugin %p; falling back to %p' % [self, id, h[:default]]
184
- h[:default]
185
- else
186
- raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
187
- end
179
+ if h.has_key?(:default)
180
+ h[:default]
188
181
  else
189
- load_plugin_map
190
- h[plugin_id]
182
+ raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
191
183
  end
192
184
  else
193
185
  # Plugin should have registered by now
@@ -271,7 +263,6 @@ module CodeRay
271
263
  end
272
264
 
273
265
  def aliases
274
- plugin_host.load_plugin_map
275
266
  plugin_host.plugin_hash.inject [] do |aliases, (key, _)|
276
267
  aliases << key if plugin_host[key] == self
277
268
  aliases
@@ -182,16 +182,9 @@ module CodeRay
182
182
  # Scan the code and returns all tokens in a Tokens object.
183
183
  def tokenize source = nil, options = {}
184
184
  options = @options.merge(options)
185
- @tokens = options[:tokens] || @tokens || Tokens.new
186
- @tokens.scanner = self if @tokens.respond_to? :scanner=
187
- case source
188
- when Array
189
- self.string = self.class.normalize(source.join)
190
- when nil
191
- reset
192
- else
193
- self.string = self.class.normalize(source)
194
- end
185
+
186
+ set_tokens_from_options options
187
+ set_string_from_source source
195
188
 
196
189
  begin
197
190
  scan_tokens @tokens, options
@@ -261,6 +254,22 @@ module CodeRay
261
254
  def setup # :doc:
262
255
  end
263
256
 
257
+ def set_string_from_source source
258
+ case source
259
+ when Array
260
+ self.string = self.class.normalize(source.join)
261
+ when nil
262
+ reset
263
+ else
264
+ self.string = self.class.normalize(source)
265
+ end
266
+ end
267
+
268
+ def set_tokens_from_options options
269
+ @tokens = options[:tokens] || @tokens || Tokens.new
270
+ @tokens.scanner = self if @tokens.respond_to? :scanner=
271
+ end
272
+
264
273
  # This is the central method, and commonly the only one a
265
274
  # subclass implements.
266
275
  #
@@ -277,19 +286,15 @@ module CodeRay
277
286
  @binary_string = nil if defined? @binary_string
278
287
  end
279
288
 
280
- # Scanner error with additional status information
281
- def raise_inspect msg, tokens, state = self.state || 'No state given!', ambit = 30, backtrace = caller
282
- raise ScanError, <<-EOE % [
289
+ SCAN_ERROR_MESSAGE = <<-MESSAGE
283
290
 
284
291
 
285
- ***ERROR in %s: %s (after %d tokens)
292
+ ***ERROR in %s: %s (after %s tokens)
286
293
 
287
294
  tokens:
288
295
  %s
289
296
 
290
- current line: %d column: %d pos: %d
291
- matched: %p state: %p
292
- bol? = %p, eos? = %p
297
+ %s
293
298
 
294
299
  surrounding code:
295
300
  %p ~~ %p
@@ -297,16 +302,43 @@ surrounding code:
297
302
 
298
303
  ***ERROR***
299
304
 
300
- EOE
301
- File.basename(caller[0]),
302
- msg,
303
- tokens.respond_to?(:size) ? tokens.size : 0,
304
- tokens.respond_to?(:last) ? tokens.last(10).map { |t| t.inspect }.join("\n") : '',
305
- line, column, pos,
306
- matched, state, bol?, eos?,
305
+ MESSAGE
306
+
307
+ def raise_inspect_arguments message, tokens, state, ambit
308
+ return File.basename(caller[0]),
309
+ message,
310
+ tokens_size(tokens),
311
+ tokens_last(tokens, 10).map(&:inspect).join("\n"),
312
+ scanner_state_info(state),
307
313
  binary_string[pos - ambit, ambit],
308
- binary_string[pos, ambit],
309
- ], backtrace
314
+ binary_string[pos, ambit]
315
+ end
316
+
317
+ SCANNER_STATE_INFO = <<-INFO
318
+ current line: %d column: %d pos: %d
319
+ matched: %p state: %p
320
+ bol?: %p, eos?: %p
321
+ INFO
322
+
323
+ def scanner_state_info state
324
+ SCANNER_STATE_INFO % [
325
+ line, column, pos,
326
+ matched, state || 'No state given!',
327
+ bol?, eos?,
328
+ ]
329
+ end
330
+
331
+ # Scanner error with additional status information
332
+ def raise_inspect message, tokens, state = self.state, ambit = 30, backtrace = caller
333
+ raise ScanError, SCAN_ERROR_MESSAGE % raise_inspect_arguments(message, tokens, state, ambit), backtrace
334
+ end
335
+
336
+ def tokens_size tokens
337
+ tokens.size if tokens.respond_to?(:size)
338
+ end
339
+
340
+ def tokens_last tokens, n
341
+ tokens.respond_to?(:last) ? tokens.last(n) : []
310
342
  end
311
343
 
312
344
  # Shorthand for scan_until(/\z/).
@@ -148,7 +148,7 @@ module Scanners
148
148
  encoder.text_token match, :char
149
149
  elsif match = scan(/ \\ | $ /x)
150
150
  encoder.end_group :string
151
- encoder.text_token match, :error
151
+ encoder.text_token match, :error unless match.empty?
152
152
  state = :initial
153
153
  label_expected = false
154
154
  else
@@ -160,7 +160,7 @@ module Scanners
160
160
  encoder.text_token match, :char
161
161
  elsif match = scan(/ \\ | $ /x)
162
162
  encoder.end_group :string
163
- encoder.text_token match, :error
163
+ encoder.text_token match, :error unless match.empty?
164
164
  state = :initial
165
165
  label_expected = false
166
166
  else
@@ -7,27 +7,25 @@ module Scanners
7
7
 
8
8
  KINDS_NOT_LOC = [
9
9
  :comment,
10
- :class, :pseudo_class, :type,
11
- :constant, :directive,
10
+ :class, :pseudo_class, :tag,
11
+ :id, :directive,
12
12
  :key, :value, :operator, :color, :float, :string,
13
- :error, :important,
13
+ :error, :important, :type,
14
14
  ] # :nodoc:
15
15
 
16
16
  module RE # :nodoc:
17
17
  Hex = /[0-9a-fA-F]/
18
- Unicode = /\\#{Hex}{1,6}(?:\r\n|\s)?/ # differs from standard because it allows uppercase hex too
19
- Escape = /#{Unicode}|\\[^\r\n\f0-9a-fA-F]/
20
- NMChar = /[-_a-zA-Z0-9]|#{Escape}/
21
- NMStart = /[_a-zA-Z]|#{Escape}/
22
- NL = /\r\n|\r|\n|\f/
23
- String1 = /"(?:[^\n\r\f\\"]|\\#{NL}|#{Escape})*"?/ # TODO: buggy regexp
24
- String2 = /'(?:[^\n\r\f\\']|\\#{NL}|#{Escape})*'?/ # TODO: buggy regexp
18
+ Unicode = /\\#{Hex}{1,6}\b/ # differs from standard because it allows uppercase hex too
19
+ Escape = /#{Unicode}|\\[^\n0-9a-fA-F]/
20
+ NMChar = /[-_a-zA-Z0-9]/
21
+ NMStart = /[_a-zA-Z]/
22
+ String1 = /"(?:[^\n\\"]+|\\\n|#{Escape})*"?/ # TODO: buggy regexp
23
+ String2 = /'(?:[^\n\\']+|\\\n|#{Escape})*'?/ # TODO: buggy regexp
25
24
  String = /#{String1}|#{String2}/
26
25
 
27
26
  HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/
28
- Color = /#{HexColor}/
29
27
 
30
- Num = /-?(?:[0-9]+|[0-9]*\.[0-9]+)/
28
+ Num = /-?(?:[0-9]*\.[0-9]+|[0-9]+)n?/
31
29
  Name = /#{NMChar}+/
32
30
  Ident = /-?#{NMStart}#{NMChar}*/
33
31
  AtKeyword = /@#{Ident}/
@@ -35,16 +33,15 @@ module Scanners
35
33
 
36
34
  reldimensions = %w[em ex px]
37
35
  absdimensions = %w[in cm mm pt pc]
38
- Unit = Regexp.union(*(reldimensions + absdimensions + %w[s]))
36
+ Unit = Regexp.union(*(reldimensions + absdimensions + %w[s dpi dppx deg]))
39
37
 
40
38
  Dimension = /#{Num}#{Unit}/
41
39
 
42
- Comment = %r! /\* (?: .*? \*/ | .* ) !mx
43
- Function = /(?:url|alpha|attr|counters?)\((?:[^)\n\r\f]|\\\))*\)?/
40
+ Function = /(?:url|alpha|attr|counters?)\((?:[^)\n]|\\\))*\)?/
44
41
 
45
- Id = /##{Name}/
42
+ Id = /(?!#{HexColor}\b(?!-))##{Name}/
46
43
  Class = /\.#{Name}/
47
- PseudoClass = /:#{Name}/
44
+ PseudoClass = /::?#{Ident}/
48
45
  AttributeSelector = /\[[^\]]*\]?/
49
46
  end
50
47
 
@@ -52,11 +49,11 @@ module Scanners
52
49
 
53
50
  def setup
54
51
  @state = :initial
55
- @value_expected = nil
52
+ @value_expected = false
56
53
  end
57
54
 
58
55
  def scan_tokens encoder, options
59
- states = Array(options[:state] || @state)
56
+ states = Array(options[:state] || @state).dup
60
57
  value_expected = @value_expected
61
58
 
62
59
  until eos?
@@ -67,13 +64,13 @@ module Scanners
67
64
  elsif case states.last
68
65
  when :initial, :media
69
66
  if match = scan(/(?>#{RE::Ident})(?!\()|\*/ox)
70
- encoder.text_token match, :type
67
+ encoder.text_token match, :tag
71
68
  next
72
69
  elsif match = scan(RE::Class)
73
70
  encoder.text_token match, :class
74
71
  next
75
72
  elsif match = scan(RE::Id)
76
- encoder.text_token match, :constant
73
+ encoder.text_token match, :id
77
74
  next
78
75
  elsif match = scan(RE::PseudoClass)
79
76
  encoder.text_token match, :pseudo_class
@@ -148,17 +145,17 @@ module Scanners
148
145
  start = match[/^\w+\(/]
149
146
  encoder.text_token start, :delimiter
150
147
  if match[-1] == ?)
151
- encoder.text_token match[start.size..-2], :content
148
+ encoder.text_token match[start.size..-2], :content if match.size > start.size + 1
152
149
  encoder.text_token ')', :delimiter
153
150
  else
154
- encoder.text_token match[start.size..-1], :content
151
+ encoder.text_token match[start.size..-1], :content if match.size > start.size
155
152
  end
156
153
  encoder.end_group :function
157
154
 
158
155
  elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox)
159
156
  encoder.text_token match, :float
160
157
 
161
- elsif match = scan(/#{RE::Color}/o)
158
+ elsif match = scan(/#{RE::HexColor}/o)
162
159
  encoder.text_token match, :color
163
160
 
164
161
  elsif match = scan(/! *important/)
@@ -170,7 +167,7 @@ module Scanners
170
167
  elsif match = scan(RE::AtKeyword)
171
168
  encoder.text_token match, :directive
172
169
 
173
- elsif match = scan(/ [+>:;,.=()\/] /x)
170
+ elsif match = scan(/ [+>~:;,.=()\/] /x)
174
171
  if match == ':'
175
172
  value_expected = true
176
173
  elsif match == ';'
@@ -20,7 +20,7 @@ module Scanners
20
20
 
21
21
  line_kind = nil
22
22
  state = :initial
23
- deleted_lines = 0
23
+ deleted_lines_count = 0
24
24
  scanners = Hash.new do |h, lang|
25
25
  h[lang] = Scanners[lang].new '', :keep_tokens => true, :keep_state => true
26
26
  end
@@ -30,7 +30,7 @@ module Scanners
30
30
  until eos?
31
31
 
32
32
  if match = scan(/\n/)
33
- deleted_lines = 0 unless line_kind == :delete
33
+ deleted_lines_count = 0 unless line_kind == :delete
34
34
  if line_kind
35
35
  encoder.end_line line_kind
36
36
  line_kind = nil
@@ -45,7 +45,7 @@ module Scanners
45
45
  if match = scan(/--- |\+\+\+ |=+|_+/)
46
46
  encoder.begin_line line_kind = :head
47
47
  encoder.text_token match, :head
48
- if match = scan(/.*?(?=$|[\t\n\x00]| \(revision)/)
48
+ if match = scan(/[^\x00\n]+?(?=$|[\t\n]| \(revision)/)
49
49
  encoder.text_token match, :filename
50
50
  if options[:highlight_code] && match != '/dev/null'
51
51
  file_type = CodeRay::FileType.fetch(match, :text)
@@ -69,7 +69,7 @@ module Scanners
69
69
  state = :added
70
70
  elsif match = scan(/\\ .*/)
71
71
  encoder.text_token match, :comment
72
- elsif match = scan(/@@(?>[^@\n]*)@@/)
72
+ elsif match = scan(/@@(?>[^@\n]+)@@/)
73
73
  content_scanner.state = :initial unless match?(/\n\+/)
74
74
  content_scanner_entry_state = nil
75
75
  if check(/\n|$/)
@@ -99,37 +99,59 @@ module Scanners
99
99
  end
100
100
  next
101
101
  elsif match = scan(/-/)
102
- deleted_lines += 1
103
- encoder.begin_line line_kind = :delete
104
- encoder.text_token match, :delete
105
- if options[:inline_diff] && deleted_lines == 1 && check(/(?>.*)\n\+(?>.*)$(?!\n\+)/)
106
- content_scanner_entry_state = content_scanner.state
107
- skip(/(.*)\n\+(.*)$/)
108
- head, deletion, insertion, tail = diff self[1], self[2]
109
- pre, deleted, post = content_scanner.tokenize [head, deletion, tail], :tokens => Tokens.new
110
- encoder.tokens pre
111
- unless deleted.empty?
112
- encoder.begin_group :eyecatcher
113
- encoder.tokens deleted
114
- encoder.end_group :eyecatcher
102
+ deleted_lines_count += 1
103
+ if options[:inline_diff] && deleted_lines_count == 1 && (changed_lines_count = 1 + check(/.*(?:\n\-.*)*/).count("\n")) && match?(/(?>.*(?:\n\-.*){#{changed_lines_count - 1}}(?:\n\+.*){#{changed_lines_count}})$(?!\n\+)/)
104
+ deleted_lines = Array.new(changed_lines_count) { |i| skip(/\n\-/) if i > 0; scan(/.*/) }
105
+ inserted_lines = Array.new(changed_lines_count) { |i| skip(/\n\+/) ; scan(/.*/) }
106
+
107
+ deleted_lines_tokenized = []
108
+ inserted_lines_tokenized = []
109
+ for deleted_line, inserted_line in deleted_lines.zip(inserted_lines)
110
+ pre, deleted_part, inserted_part, post = diff deleted_line, inserted_line
111
+ content_scanner_entry_state = content_scanner.state
112
+ deleted_lines_tokenized << content_scanner.tokenize([pre, deleted_part, post], :tokens => Tokens.new)
113
+ content_scanner.state = content_scanner_entry_state || :initial
114
+ inserted_lines_tokenized << content_scanner.tokenize([pre, inserted_part, post], :tokens => Tokens.new)
115
115
  end
116
- encoder.tokens post
117
- encoder.end_line line_kind
118
- encoder.text_token "\n", :space
119
- encoder.begin_line line_kind = :insert
120
- encoder.text_token '+', :insert
121
- content_scanner.state = content_scanner_entry_state || :initial
122
- pre, inserted, post = content_scanner.tokenize [head, insertion, tail], :tokens => Tokens.new
123
- encoder.tokens pre
124
- unless inserted.empty?
125
- encoder.begin_group :eyecatcher
126
- encoder.tokens inserted
127
- encoder.end_group :eyecatcher
116
+
117
+ for pre, deleted_part, post in deleted_lines_tokenized
118
+ encoder.begin_line :delete
119
+ encoder.text_token '-', :delete
120
+ encoder.tokens pre
121
+ unless deleted_part.empty?
122
+ encoder.begin_group :eyecatcher
123
+ encoder.tokens deleted_part
124
+ encoder.end_group :eyecatcher
125
+ end
126
+ encoder.tokens post
127
+ encoder.end_line :delete
128
+ encoder.text_token "\n", :space
129
+ end
130
+
131
+ for pre, inserted_part, post in inserted_lines_tokenized
132
+ encoder.begin_line :insert
133
+ encoder.text_token '+', :insert
134
+ encoder.tokens pre
135
+ unless inserted_part.empty?
136
+ encoder.begin_group :eyecatcher
137
+ encoder.tokens inserted_part
138
+ encoder.end_group :eyecatcher
139
+ end
140
+ encoder.tokens post
141
+ changed_lines_count -= 1
142
+ if changed_lines_count > 0
143
+ encoder.end_line :insert
144
+ encoder.text_token "\n", :space
145
+ end
128
146
  end
129
- encoder.tokens post
147
+
148
+ line_kind = :insert
149
+
130
150
  elsif match = scan(/.*/)
151
+ encoder.begin_line line_kind = :delete
152
+ encoder.text_token '-', :delete
131
153
  if options[:highlight_code]
132
- if deleted_lines == 1
154
+ if deleted_lines_count == 1
133
155
  content_scanner_entry_state = content_scanner.state
134
156
  end
135
157
  content_scanner.tokenize match, :tokens => encoder unless match.empty?
@@ -36,9 +36,12 @@ module Scanners
36
36
 
37
37
  protected
38
38
 
39
+ def setup
40
+ @state = :initial
41
+ end
42
+
39
43
  def scan_tokens encoder, options
40
-
41
- state = :initial
44
+ state = options[:state] || @state
42
45
  inline_block_stack = []
43
46
  inline_block_paren_depth = nil
44
47
  string_delimiter = nil
@@ -223,7 +226,7 @@ module Scanners
223
226
  encoder.text_token match, :content # TODO: Shouldn't this be :error?
224
227
 
225
228
  elsif match = scan(/ \\ | \n /x)
226
- encoder.end_group state
229
+ encoder.end_group state == :regexp ? :regexp : :string
227
230
  encoder.text_token match, :error
228
231
  after_def = value_expected = false
229
232
  state = :initial
@@ -243,7 +246,17 @@ module Scanners
243
246
  end
244
247
 
245
248
  if [:multiline_string, :string, :regexp].include? state
246
- encoder.end_group state
249
+ encoder.end_group state == :regexp ? :regexp : :string
250
+ end
251
+
252
+ if options[:keep_state]
253
+ @state = state
254
+ end
255
+
256
+ until inline_block_stack.empty?
257
+ state, = *inline_block_stack.pop
258
+ encoder.end_group :inline
259
+ encoder.end_group state == :regexp ? :regexp : :string
247
260
  end
248
261
 
249
262
  encoder