coderay 0.8.357 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/lib/README +4 -3
  2. data/lib/coderay.rb +2 -1
  3. data/lib/coderay/encoder.rb +41 -15
  4. data/lib/coderay/encoders/_map.rb +3 -1
  5. data/lib/coderay/encoders/comment_filter.rb +43 -0
  6. data/lib/coderay/encoders/div.rb +2 -3
  7. data/lib/coderay/encoders/filter.rb +75 -0
  8. data/lib/coderay/encoders/html.rb +20 -3
  9. data/lib/coderay/encoders/html/css.rb +1 -1
  10. data/lib/coderay/encoders/html/numerization.rb +11 -2
  11. data/lib/coderay/encoders/html/output.rb +10 -1
  12. data/lib/coderay/encoders/json.rb +69 -0
  13. data/lib/coderay/encoders/lines_of_code.rb +90 -0
  14. data/lib/coderay/encoders/page.rb +1 -2
  15. data/lib/coderay/encoders/span.rb +2 -3
  16. data/lib/coderay/encoders/term.rb +137 -0
  17. data/lib/coderay/encoders/text.rb +4 -4
  18. data/lib/coderay/encoders/token_class_filter.rb +84 -0
  19. data/lib/coderay/encoders/xml.rb +1 -0
  20. data/lib/coderay/for_redcloth.rb +9 -4
  21. data/lib/coderay/helpers/file_type.rb +54 -15
  22. data/lib/coderay/helpers/plugin.rb +21 -3
  23. data/lib/coderay/helpers/word_list.rb +19 -4
  24. data/lib/coderay/scanner.rb +33 -2
  25. data/lib/coderay/scanners/_map.rb +10 -4
  26. data/lib/coderay/scanners/c.rb +61 -23
  27. data/lib/coderay/scanners/cpp.rb +228 -0
  28. data/lib/coderay/scanners/css.rb +9 -1
  29. data/lib/coderay/scanners/debug.rb +1 -0
  30. data/lib/coderay/scanners/delphi.rb +2 -2
  31. data/lib/coderay/scanners/diff.rb +1 -0
  32. data/lib/coderay/scanners/groovy.rb +263 -0
  33. data/lib/coderay/scanners/html.rb +9 -2
  34. data/lib/coderay/scanners/java.rb +18 -14
  35. data/lib/coderay/scanners/java_script.rb +42 -13
  36. data/lib/coderay/scanners/json.rb +7 -1
  37. data/lib/coderay/scanners/nitro_xhtml.rb +4 -0
  38. data/lib/coderay/scanners/php.rb +526 -0
  39. data/lib/coderay/scanners/plaintext.rb +4 -1
  40. data/lib/coderay/scanners/python.rb +285 -0
  41. data/lib/coderay/scanners/rhtml.rb +3 -0
  42. data/lib/coderay/scanners/ruby.rb +29 -11
  43. data/lib/coderay/scanners/ruby/patterns.rb +26 -20
  44. data/lib/coderay/scanners/scheme.rb +3 -0
  45. data/lib/coderay/scanners/sql.rb +162 -0
  46. data/lib/coderay/scanners/xml.rb +1 -1
  47. data/lib/coderay/scanners/yaml.rb +4 -1
  48. data/lib/coderay/styles/cycnus.rb +11 -7
  49. data/lib/coderay/token_classes.rb +4 -1
  50. data/lib/coderay/tokens.rb +50 -46
  51. metadata +14 -4
  52. data/lib/coderay/encoders/tokens.rb +0 -44
@@ -5,6 +5,14 @@ module Scanners
5
5
 
6
6
  register_for :css
7
7
 
8
+ KINDS_NOT_LOC = [
9
+ :comment,
10
+ :class, :pseudo_class, :type,
11
+ :constant, :directive,
12
+ :key, :value, :operator, :color, :float,
13
+ :error, :important,
14
+ ]
15
+
8
16
  module RE
9
17
  NonASCII = /[\x80-\xFF]/
10
18
  Hex = /[0-9a-fA-F]/
@@ -58,7 +66,7 @@ module Scanners
58
66
  elsif case states.last
59
67
  when :initial, :media
60
68
  if scan(/(?>#{RE::Ident})(?!\()|\*/ox)
61
- kind = :keyword
69
+ kind = :type
62
70
  elsif scan RE::Class
63
71
  kind = :class
64
72
  elsif scan RE::Id
@@ -7,6 +7,7 @@ module Scanners
7
7
  include Streamable
8
8
  register_for :debug
9
9
  file_extension 'raydebug'
10
+ title 'CodeRay Token Dump'
10
11
 
11
12
  protected
12
13
  def scan_tokens tokens, options
@@ -30,11 +30,11 @@ module Scanners
30
30
  'virtual', 'write', 'writeonly'
31
31
  ]
32
32
 
33
- IDENT_KIND = CaseIgnoringWordList.new(:ident, caching=true).
33
+ IDENT_KIND = CaseIgnoringWordList.new(:ident).
34
34
  add(RESERVED_WORDS, :reserved).
35
35
  add(DIRECTIVES, :directive)
36
36
 
37
- NAME_FOLLOWS = CaseIgnoringWordList.new(false, caching=true).
37
+ NAME_FOLLOWS = CaseIgnoringWordList.new(false).
38
38
  add(%w(procedure function .))
39
39
 
40
40
  private
@@ -4,6 +4,7 @@ module Scanners
4
4
  class Diff < Scanner
5
5
 
6
6
  register_for :diff
7
+ title 'diff output'
7
8
 
8
9
  def scan_tokens tokens, options
9
10
 
@@ -0,0 +1,263 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ load :java
5
+
6
+ class Groovy < Java
7
+
8
+ include Streamable
9
+ register_for :groovy
10
+
11
+ # TODO: Check this!
12
+ GROOVY_KEYWORDS = %w[
13
+ as assert def in
14
+ ]
15
+ KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
16
+ case instanceof new return throw typeof while as assert in
17
+ ]
18
+ GROOVY_MAGIC_VARIABLES = %w[ it ]
19
+
20
+ IDENT_KIND = Java::IDENT_KIND.dup.
21
+ add(GROOVY_KEYWORDS, :keyword).
22
+ add(GROOVY_MAGIC_VARIABLES, :local_variable)
23
+
24
+ ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
25
+ UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # no 4-byte unicode chars? U[a-fA-F0-9]{8}
26
+ REGEXP_ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | \d | [bBdDsSwW\/] /x
27
+
28
+ # TODO: interpretation inside ', ", /
29
+ STRING_CONTENT_PATTERN = {
30
+ "'" => /(?>\\[^\\'\n]+|[^\\'\n]+)+/,
31
+ '"' => /[^\\$"\n]+/,
32
+ "'''" => /(?>[^\\']+|'(?!''))+/,
33
+ '"""' => /(?>[^\\$"]+|"(?!""))+/,
34
+ '/' => /[^\\$\/\n]+/,
35
+ }
36
+
37
+ def scan_tokens tokens, options
38
+
39
+ state = :initial
40
+ inline_block_stack = []
41
+ inline_block_paren_depth = nil
42
+ string_delimiter = nil
43
+ import_clause = class_name_follows = last_token = after_def = false
44
+ value_expected = true
45
+
46
+ until eos?
47
+
48
+ kind = nil
49
+ match = nil
50
+
51
+ case state
52
+
53
+ when :initial
54
+
55
+ if match = scan(/ \s+ | \\\n /x)
56
+ tokens << [match, :space]
57
+ if match.index ?\n
58
+ import_clause = after_def = false
59
+ value_expected = true unless value_expected
60
+ end
61
+ next
62
+
63
+ elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
64
+ value_expected = true
65
+ after_def = false
66
+ kind = :comment
67
+
68
+ elsif bol? && scan(/ \#!.* /x)
69
+ kind = :doctype
70
+
71
+ elsif import_clause && scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox)
72
+ after_def = value_expected = false
73
+ kind = :include
74
+
75
+ elsif match = scan(/ #{IDENT} | \[\] /ox)
76
+ kind = IDENT_KIND[match]
77
+ value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
78
+ if last_token == '.'
79
+ kind = :ident
80
+ elsif class_name_follows
81
+ kind = :class
82
+ class_name_follows = false
83
+ elsif after_def && check(/\s*[({]/)
84
+ kind = :method
85
+ after_def = false
86
+ elsif kind == :ident && last_token != '?' && check(/:/)
87
+ kind = :key
88
+ else
89
+ class_name_follows = true if match == 'class' || (import_clause && match == 'as')
90
+ import_clause = match == 'import'
91
+ after_def = true if match == 'def'
92
+ end
93
+
94
+ elsif scan(/;/)
95
+ import_clause = after_def = false
96
+ value_expected = true
97
+ kind = :operator
98
+
99
+ elsif scan(/\{/)
100
+ class_name_follows = after_def = false
101
+ value_expected = true
102
+ kind = :operator
103
+ if !inline_block_stack.empty?
104
+ inline_block_paren_depth += 1
105
+ end
106
+
107
+ # TODO: ~'...', ~"..." and ~/.../ style regexps
108
+ elsif match = scan(/ \.\.<? | \*?\.(?!\d)@? | \.& | \?:? | [,?:(\[] | -[->] | \+\+ |
109
+ && | \|\| | \*\*=? | ==?~ | <=?>? | [-+*%^~&|>=!]=? | <<<?=? | >>>?=? /x)
110
+ value_expected = true
111
+ value_expected = :regexp if match == '~'
112
+ after_def = false
113
+ kind = :operator
114
+
115
+ elsif match = scan(/ [)\]}] /x)
116
+ value_expected = after_def = false
117
+ if !inline_block_stack.empty? && match == '}'
118
+ inline_block_paren_depth -= 1
119
+ if inline_block_paren_depth == 0 # closing brace of inline block reached
120
+ tokens << [match, :inline_delimiter]
121
+ tokens << [:close, :inline]
122
+ state, string_delimiter, inline_block_paren_depth = inline_block_stack.pop
123
+ next
124
+ end
125
+ end
126
+
127
+ elsif check(/[\d.]/)
128
+ after_def = value_expected = false
129
+ if scan(/0[xX][0-9A-Fa-f]+/)
130
+ kind = :hex
131
+ elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
132
+ kind = :oct
133
+ elsif scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/)
134
+ kind = :float
135
+ elsif scan(/\d+[lLgG]?/)
136
+ kind = :integer
137
+ end
138
+
139
+ elsif match = scan(/'''|"""/)
140
+ after_def = value_expected = false
141
+ state = :multiline_string
142
+ tokens << [:open, :string]
143
+ string_delimiter = match
144
+ kind = :delimiter
145
+
146
+ # TODO: record.'name'
147
+ elsif match = scan(/["']/)
148
+ after_def = value_expected = false
149
+ state = match == '/' ? :regexp : :string
150
+ tokens << [:open, state]
151
+ string_delimiter = match
152
+ kind = :delimiter
153
+
154
+ elsif value_expected && (match = scan(/\//))
155
+ after_def = value_expected = false
156
+ tokens << [:open, :regexp]
157
+ state = :regexp
158
+ string_delimiter = '/'
159
+ kind = :delimiter
160
+
161
+ elsif scan(/ @ #{IDENT} /ox)
162
+ after_def = value_expected = false
163
+ kind = :annotation
164
+
165
+ elsif scan(/\//)
166
+ after_def = false
167
+ value_expected = true
168
+ kind = :operator
169
+
170
+ else
171
+ getch
172
+ kind = :error
173
+
174
+ end
175
+
176
+ when :string, :regexp, :multiline_string
177
+ if scan(STRING_CONTENT_PATTERN[string_delimiter])
178
+ kind = :content
179
+
180
+ elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/)
181
+ tokens << [match, :delimiter]
182
+ if state == :regexp
183
+ # TODO: regexp modifiers? s, m, x, i?
184
+ modifiers = scan(/[ix]+/)
185
+ tokens << [modifiers, :modifier] if modifiers && !modifiers.empty?
186
+ end
187
+ state = :string if state == :multiline_string
188
+ tokens << [:close, state]
189
+ string_delimiter = nil
190
+ after_def = value_expected = false
191
+ state = :initial
192
+ next
193
+
194
+ elsif (state == :string || state == :multiline_string) &&
195
+ (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
196
+ if string_delimiter[0] == ?' && !(match == "\\\\" || match == "\\'")
197
+ kind = :content
198
+ else
199
+ kind = :char
200
+ end
201
+ elsif state == :regexp && scan(/ \\ (?: #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
202
+ kind = :char
203
+
204
+ elsif match = scan(/ \$ #{IDENT} /mox)
205
+ tokens << [:open, :inline]
206
+ tokens << ['$', :inline_delimiter]
207
+ match = match[1..-1]
208
+ tokens << [match, IDENT_KIND[match]]
209
+ tokens << [:close, :inline]
210
+ next
211
+ elsif match = scan(/ \$ \{ /x)
212
+ tokens << [:open, :inline]
213
+ tokens << ['${', :inline_delimiter]
214
+ inline_block_stack << [state, string_delimiter, inline_block_paren_depth]
215
+ inline_block_paren_depth = 1
216
+ state = :initial
217
+ next
218
+
219
+ elsif scan(/ \$ /mx)
220
+ kind = :content
221
+
222
+ elsif scan(/ \\. /mx)
223
+ kind = :content
224
+
225
+ elsif scan(/ \\ | \n /x)
226
+ tokens << [:close, state]
227
+ kind = :error
228
+ after_def = value_expected = false
229
+ state = :initial
230
+
231
+ else
232
+ raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
233
+ end
234
+
235
+ else
236
+ raise_inspect 'Unknown state', tokens
237
+
238
+ end
239
+
240
+ match ||= matched
241
+ if $DEBUG and not kind
242
+ raise_inspect 'Error token %p in line %d' %
243
+ [[match, kind], line], tokens
244
+ end
245
+ raise_inspect 'Empty token', tokens unless match
246
+
247
+ last_token = match unless [:space, :comment, :doctype].include? kind
248
+
249
+ tokens << [match, kind]
250
+
251
+ end
252
+
253
+ if [:multiline_string, :string, :regexp].include? state
254
+ tokens << [:close, state]
255
+ end
256
+
257
+ tokens
258
+ end
259
+
260
+ end
261
+
262
+ end
263
+ end
@@ -6,6 +6,13 @@ module Scanners
6
6
 
7
7
  include Streamable
8
8
  register_for :html
9
+
10
+ KINDS_NOT_LOC = [
11
+ :comment, :doctype, :preprocessor,
12
+ :tag, :attribute_name, :operator,
13
+ :attribute_value, :delimiter, :content,
14
+ :plain, :entity, :error
15
+ ]
9
16
 
10
17
  ATTR_NAME = /[\w.:-]+/
11
18
  ATTR_VALUE_UNQUOTED = ATTR_NAME
@@ -68,9 +75,9 @@ module Scanners
68
75
  kind = :preprocessor
69
76
  elsif scan(/<\?.*?\?>|<%.*?%>/m)
70
77
  kind = :comment
71
- elsif scan(/<\/[-\w_.:]*>/m)
78
+ elsif scan(/<\/[-\w.:]*>/m)
72
79
  kind = :tag
73
- elsif match = scan(/<[-\w_.:]+>?/m)
80
+ elsif match = scan(/<[-\w.:]+>?/m)
74
81
  kind = :tag
75
82
  state = :attribute unless match[-1] == ?>
76
83
  elsif scan(/[^<>&]+/)
@@ -7,30 +7,33 @@ module Scanners
7
7
  register_for :java
8
8
  helper :builtin_types
9
9
 
10
- # TODO: Check this!
10
+ # http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
11
11
  KEYWORDS = %w[
12
- break case catch continue default do else
13
- false finally for if instanceof new null
14
- return switch throw true try typeof while
15
- debugger export import package
12
+ assert break case catch continue default do else
13
+ finally for if instanceof import new package
14
+ return switch throw try typeof while
15
+ debugger export
16
16
  ]
17
-
17
+ RESERVED = %w[ const goto ]
18
+ CONSTANTS = %w[ false null true ]
18
19
  MAGIC_VARIABLES = %w[ this super ]
19
20
  TYPES = %w[
20
- boolean byte char class interface double enum float String int long short void
21
- ] << '[]'
21
+ boolean byte char class double enum float int interface long
22
+ short void
23
+ ] << '[]' # because int[] should be highlighted as a type
22
24
  DIRECTIVES = %w[
23
25
  abstract extends final implements native private protected public
24
- static strictfp synchronized threadsafe throws transient volatile
26
+ static strictfp synchronized throws transient volatile
25
27
  ]
26
28
 
27
- # Reserved for future use.
28
-
29
29
  IDENT_KIND = WordList.new(:ident).
30
30
  add(KEYWORDS, :keyword).
31
+ add(RESERVED, :reserved).
32
+ add(CONSTANTS, :pre_constant).
31
33
  add(MAGIC_VARIABLES, :local_variable).
32
34
  add(TYPES, :type).
33
35
  add(BuiltinTypes::List, :pre_type).
36
+ add(BuiltinTypes::List.select { |builtin| builtin[/(Error|Exception)$/] }, :exception).
34
37
  add(DIRECTIVES, :directive)
35
38
 
36
39
  ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
@@ -61,8 +64,9 @@ module Scanners
61
64
  tokens << [match, :space]
62
65
  next
63
66
 
64
- elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
65
- kind = :comment
67
+ elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
68
+ tokens << [match, :comment]
69
+ next
66
70
 
67
71
  elsif import_clause && scan(/ #{IDENT} (?: \. #{IDENT} )* /ox)
68
72
  kind = :include
@@ -79,7 +83,7 @@ module Scanners
79
83
  class_name_follows = true if match == 'class' || match == 'interface'
80
84
  end
81
85
 
82
- elsif scan(/ \.(?!\d) | [,?:(\[)\]}] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? | <<<?=? | >>>?=? /x)
86
+ elsif scan(/ \.(?!\d) | [,?:()\[\]}] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? | <<<?=? | >>>?=? /x)
83
87
  kind = :operator
84
88
 
85
89
  elsif scan(/;/)
@@ -11,14 +11,17 @@ module Scanners
11
11
  # The actual JavaScript keywords.
12
12
  KEYWORDS = %w[
13
13
  break case catch continue default delete do else
14
- false finally for function if in instanceof new null
15
- return switch throw true try typeof var void while with
14
+ finally for function if in instanceof new
15
+ return switch throw try typeof var void while with
16
+ ]
17
+ PREDEFINED_CONSTANTS = %w[
18
+ false null true undefined
16
19
  ]
17
20
 
18
21
  MAGIC_VARIABLES = %w[ this arguments ] # arguments was introduced in JavaScript 1.4
19
22
 
20
23
  KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
21
- case delete in instanceof new return throw typeof while with
24
+ case delete in instanceof new return throw typeof with
22
25
  ]
23
26
 
24
27
  # Reserved for future use.
@@ -31,6 +34,7 @@ module Scanners
31
34
 
32
35
  IDENT_KIND = WordList.new(:ident).
33
36
  add(RESERVED_WORDS, :reserved).
37
+ add(PREDEFINED_CONSTANTS, :pre_constant).
34
38
  add(MAGIC_VARIABLES, :local_variable).
35
39
  add(KEYWORDS, :keyword)
36
40
 
@@ -53,6 +57,7 @@ module Scanners
53
57
  string_delimiter = nil
54
58
  value_expected = true
55
59
  key_expected = false
60
+ function_expected = false
56
61
 
57
62
  until eos?
58
63
 
@@ -72,7 +77,7 @@ module Scanners
72
77
  value_expected = true
73
78
  kind = :comment
74
79
 
75
- elsif check(/\d/)
80
+ elsif check(/\.?\d/)
76
81
  key_expected = value_expected = false
77
82
  if scan(/0[xX][0-9A-Fa-f]+/)
78
83
  kind = :hex
@@ -83,29 +88,42 @@ module Scanners
83
88
  elsif scan(/\d+/)
84
89
  kind = :integer
85
90
  end
91
+
92
+ elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim)
93
+ # FIXME: scan over nested tags
94
+ xml_scanner.tokenize match
95
+ value_expected = false
96
+ next
86
97
 
87
98
  elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x)
88
99
  value_expected = true
89
100
  last_operator = match[-1]
90
101
  key_expected = (last_operator == ?{) || (last_operator == ?,)
102
+ function_expected = false
91
103
  kind = :operator
92
104
 
93
105
  elsif scan(/ [)\]}]+ /x)
94
- key_expected = value_expected = false
106
+ function_expected = key_expected = value_expected = false
95
107
  kind = :operator
96
108
 
97
109
  elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x)
98
110
  kind = IDENT_KIND[match]
99
111
  value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
112
+ # TODO: labels
100
113
  if kind == :ident
101
- if match.index(?$)
114
+ if match.index(?$) # $ allowed inside an identifier
102
115
  kind = :predefined
116
+ elsif function_expected
117
+ kind = :function
118
+ elsif check(/\s*[=:]\s*function\b/)
119
+ kind = :function
103
120
  elsif key_expected && check(/\s*:/)
104
121
  kind = :key
105
122
  end
106
123
  end
124
+ function_expected = (kind == :keyword) && (match == 'function')
107
125
  key_expected = false
108
-
126
+
109
127
  elsif match = scan(/["']/)
110
128
  if key_expected && check(KEY_CHECK_PATTERN[match])
111
129
  state = :key
@@ -158,25 +176,25 @@ module Scanners
158
176
  elsif scan(/\\./m)
159
177
  kind = :content
160
178
  elsif scan(/ \\ | $ /x)
161
- tokens << [:close, :delimiter]
179
+ tokens << [:close, state]
162
180
  kind = :error
163
181
  key_expected = value_expected = false
164
182
  state = :initial
165
183
  else
166
- raise_inspect "else case \" reached; %p not handled." % peek(1), tokens, state
184
+ raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
167
185
  end
168
186
 
169
187
  else
170
- raise_inspect 'Unknown state', tokens, state
188
+ raise_inspect 'Unknown state', tokens
171
189
 
172
190
  end
173
191
 
174
192
  match ||= matched
175
193
  if $DEBUG and not kind
176
194
  raise_inspect 'Error token %p in line %d' %
177
- [[match, kind], line], tokens, state
195
+ [[match, kind], line], tokens
178
196
  end
179
- raise_inspect 'Empty token', tokens, state unless match
197
+ raise_inspect 'Empty token', tokens unless match
180
198
 
181
199
  tokens << [match, kind]
182
200
 
@@ -189,7 +207,18 @@ module Scanners
189
207
  tokens
190
208
  end
191
209
 
192
- end
210
+ protected
193
211
 
212
+ def reset_instance
213
+ super
214
+ @xml_scanner.reset if defined? @xml_scanner
215
+ end
216
+
217
+ def xml_scanner
218
+ @xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => true
219
+ end
220
+
221
+ end
222
+
194
223
  end
195
224
  end