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
@@ -1,13 +1,13 @@
1
1
  module CodeRay
2
2
  module Scanners
3
-
3
+
4
4
  # HTML Scanner
5
5
  #
6
6
  # Alias: +xhtml+
7
7
  #
8
8
  # See also: Scanners::XML
9
9
  class HTML < Scanner
10
-
10
+
11
11
  register_for :html
12
12
 
13
13
  KINDS_NOT_LOC = [
@@ -33,7 +33,8 @@ module Scanners
33
33
  )
34
34
 
35
35
  IN_ATTRIBUTE = WordList::CaseIgnoring.new(nil).
36
- add(EVENT_ATTRIBUTES, :script)
36
+ add(EVENT_ATTRIBUTES, :script).
37
+ add(['style'], :style)
37
38
 
38
39
  ATTR_NAME = /[\w.:-]+/ # :nodoc:
39
40
  TAG_END = /\/?>/ # :nodoc:
@@ -75,9 +76,14 @@ module Scanners
75
76
  def scan_java_script encoder, code
76
77
  if code && !code.empty?
77
78
  @java_script_scanner ||= Scanners::JavaScript.new '', :keep_tokens => true
78
- # encoder.begin_group :inline
79
79
  @java_script_scanner.tokenize code, :tokens => encoder
80
- # encoder.end_group :inline
80
+ end
81
+ end
82
+
83
+ def scan_css encoder, code, state = [:initial]
84
+ if code && !code.empty?
85
+ @css_scanner ||= Scanners::CSS.new '', :keep_tokens => true
86
+ @css_scanner.tokenize code, :tokens => encoder, :state => state
81
87
  end
82
88
  end
83
89
 
@@ -99,7 +105,15 @@ module Scanners
99
105
  case state
100
106
 
101
107
  when :initial
102
- if match = scan(/<!--(?:.*?-->|.*)/m)
108
+ if match = scan(/<!\[CDATA\[/)
109
+ encoder.text_token match, :inline_delimiter
110
+ if match = scan(/.*?\]\]>/m)
111
+ encoder.text_token match[0..-4], :plain
112
+ encoder.text_token ']]>', :inline_delimiter
113
+ elsif match = scan(/.+/)
114
+ encoder.text_token match, :error
115
+ end
116
+ elsif match = scan(/<!--(?:.*?-->|.*)/m)
103
117
  encoder.text_token match, :comment
104
118
  elsif match = scan(/<!(\w+)(?:.*?>|.*)|\]>/m)
105
119
  encoder.text_token match, :doctype
@@ -110,7 +124,7 @@ module Scanners
110
124
  elsif match = scan(/<\/[-\w.:]*>?/m)
111
125
  in_tag = nil
112
126
  encoder.text_token match, :tag
113
- elsif match = scan(/<(?:(script)|[-\w.:]+)(>)?/m)
127
+ elsif match = scan(/<(?:(script|style)|[-\w.:]+)(>)?/m)
114
128
  encoder.text_token match, :tag
115
129
  in_tag = self[1]
116
130
  if self[2]
@@ -161,17 +175,21 @@ module Scanners
161
175
  encoder.text_token match, :attribute_value
162
176
  state = :attribute
163
177
  elsif match = scan(/["']/)
164
- if in_attribute == :script
165
- encoder.begin_group :inline
166
- encoder.text_token match, :inline_delimiter
178
+ if in_attribute == :script || in_attribute == :style
179
+ encoder.begin_group :string
180
+ encoder.text_token match, :delimiter
167
181
  if scan(/javascript:[ \t]*/)
168
182
  encoder.text_token matched, :comment
169
183
  end
170
184
  code = scan_until(match == '"' ? /(?="|\z)/ : /(?='|\z)/)
171
- scan_java_script encoder, code
185
+ if in_attribute == :script
186
+ scan_java_script encoder, code
187
+ else
188
+ scan_css encoder, code, [:block]
189
+ end
172
190
  match = scan(/["']/)
173
- encoder.text_token match, :inline_delimiter if match
174
- encoder.end_group :inline
191
+ encoder.text_token match, :delimiter if match
192
+ encoder.end_group :string
175
193
  state = :attribute
176
194
  in_attribute = nil
177
195
  else
@@ -206,19 +224,23 @@ module Scanners
206
224
 
207
225
  when :in_special_tag
208
226
  case in_tag
209
- when 'script'
227
+ when 'script', 'style'
210
228
  encoder.text_token match, :space if match = scan(/[ \t]*\n/)
211
229
  if scan(/(\s*<!--)(?:(.*?)(-->)|(.*))/m)
212
230
  code = self[2] || self[4]
213
231
  closing = self[3]
214
232
  encoder.text_token self[1], :comment
215
233
  else
216
- code = scan_until(/(?=(?:\n\s*)?<\/script>)|\z/)
234
+ code = scan_until(/(?=(?:\n\s*)?<\/#{in_tag}>)|\z/)
217
235
  closing = false
218
236
  end
219
237
  unless code.empty?
220
238
  encoder.begin_group :inline
221
- scan_java_script encoder, code
239
+ if in_tag == 'script'
240
+ scan_java_script encoder, code
241
+ else
242
+ scan_css encoder, code
243
+ end
222
244
  encoder.end_group :inline
223
245
  end
224
246
  encoder.text_token closing, :comment if closing
@@ -147,7 +147,7 @@ module Scanners
147
147
  elsif match = scan(/ \\ | $ /x)
148
148
  encoder.end_group state
149
149
  state = :initial
150
- encoder.text_token match, :error
150
+ encoder.text_token match, :error unless match.empty?
151
151
  else
152
152
  raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
153
153
  end
@@ -54,10 +54,17 @@ module Scanners
54
54
 
55
55
  protected
56
56
 
57
+ def setup
58
+ @state = :initial
59
+ end
60
+
57
61
  def scan_tokens encoder, options
58
62
 
59
- state = :initial
60
- string_delimiter = nil
63
+ state, string_delimiter = options[:state] || @state
64
+ if string_delimiter
65
+ encoder.begin_group state
66
+ end
67
+
61
68
  value_expected = true
62
69
  key_expected = false
63
70
  function_expected = false
@@ -72,9 +79,10 @@ module Scanners
72
79
  value_expected = true if !value_expected && match.index(?\n)
73
80
  encoder.text_token match, :space
74
81
 
75
- elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
82
+ elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .*() ) !mx)
76
83
  value_expected = true
77
84
  encoder.text_token match, :comment
85
+ state = :open_multi_line_comment if self[1]
78
86
 
79
87
  elsif check(/\.?\d/)
80
88
  key_expected = value_expected = false
@@ -175,20 +183,36 @@ module Scanners
175
183
  encoder.text_token match, :content
176
184
  elsif match = scan(/ \\ | $ /x)
177
185
  encoder.end_group state
178
- encoder.text_token match, :error
186
+ encoder.text_token match, :error unless match.empty?
187
+ string_delimiter = nil
179
188
  key_expected = value_expected = false
180
189
  state = :initial
181
190
  else
182
- raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
191
+ raise_inspect "else case #{string_delimiter} reached; %p not handled." % peek(1), encoder
183
192
  end
184
193
 
194
+ when :open_multi_line_comment
195
+ if match = scan(%r! .*? \*/ !mx)
196
+ state = :initial
197
+ else
198
+ match = scan(%r! .+ !mx)
199
+ end
200
+ value_expected = true
201
+ encoder.text_token match, :comment if match
202
+
185
203
  else
186
- raise_inspect 'Unknown state', encoder
204
+ #:nocov:
205
+ raise_inspect 'Unknown state: %p' % [state], encoder
206
+ #:nocov:
187
207
 
188
208
  end
189
209
 
190
210
  end
191
211
 
212
+ if options[:keep_state]
213
+ @state = state, string_delimiter
214
+ end
215
+
192
216
  if [:string, :regexp].include? state
193
217
  encoder.end_group state
194
218
  end
@@ -14,15 +14,21 @@ module Scanners
14
14
 
15
15
  ESCAPE = / [bfnrt\\"\/] /x # :nodoc:
16
16
  UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc:
17
+ KEY = / (?> (?: [^\\"]+ | \\. )* ) " \s* : /x
17
18
 
18
19
  protected
19
20
 
21
+ def setup
22
+ @state = :initial
23
+ end
24
+
20
25
  # See http://json.org/ for a definition of the JSON lexic/grammar.
21
26
  def scan_tokens encoder, options
27
+ state = options[:state] || @state
22
28
 
23
- state = :initial
24
- stack = []
25
- key_expected = false
29
+ if [:string, :key].include? state
30
+ encoder.begin_group state
31
+ end
26
32
 
27
33
  until eos?
28
34
 
@@ -32,18 +38,11 @@ module Scanners
32
38
  if match = scan(/ \s+ /x)
33
39
  encoder.text_token match, :space
34
40
  elsif match = scan(/"/)
35
- state = key_expected ? :key : :string
41
+ state = check(/#{KEY}/o) ? :key : :string
36
42
  encoder.begin_group state
37
43
  encoder.text_token match, :delimiter
38
44
  elsif match = scan(/ [:,\[{\]}] /x)
39
45
  encoder.text_token match, :operator
40
- case match
41
- when ':' then key_expected = false
42
- when ',' then key_expected = true if stack.last == :object
43
- when '{' then stack << :object; key_expected = true
44
- when '[' then stack << :array
45
- when '}', ']' then stack.pop # no error recovery, but works for valid JSON
46
- end
47
46
  elsif match = scan(/ true | false | null /x)
48
47
  encoder.text_token match, :value
49
48
  elsif match = scan(/ -? (?: 0 | [1-9]\d* ) /x)
@@ -70,7 +69,7 @@ module Scanners
70
69
  encoder.text_token match, :content
71
70
  elsif match = scan(/ \\ | $ /x)
72
71
  encoder.end_group state
73
- encoder.text_token match, :error
72
+ encoder.text_token match, :error unless match.empty?
74
73
  state = :initial
75
74
  else
76
75
  raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
@@ -82,6 +81,10 @@ module Scanners
82
81
  end
83
82
  end
84
83
 
84
+ if options[:keep_state]
85
+ @state = state
86
+ end
87
+
85
88
  if [:string, :key].include? state
86
89
  encoder.end_group state
87
90
  end
@@ -0,0 +1,280 @@
1
+ # encoding: utf-8
2
+
3
+ module CodeRay
4
+ module Scanners
5
+
6
+ # Scanner for the Lua[http://lua.org] programming lanuage.
7
+ #
8
+ # The language’s complete syntax is defined in
9
+ # {the Lua manual}[http://www.lua.org/manual/5.2/manual.html],
10
+ # which is what this scanner tries to conform to.
11
+ class Lua < Scanner
12
+
13
+ register_for :lua
14
+ file_extension 'lua'
15
+ title 'Lua'
16
+
17
+ # Keywords used in Lua.
18
+ KEYWORDS = %w[and break do else elseif end
19
+ for function goto if in
20
+ local not or repeat return
21
+ then until while
22
+ ]
23
+
24
+ # Constants set by the Lua core.
25
+ PREDEFINED_CONSTANTS = %w[false true nil]
26
+
27
+ # The expressions contained in this array are parts of Lua’s `basic'
28
+ # library. Although it’s not entirely necessary to load that library,
29
+ # it is highly recommended and one would have to provide own implementations
30
+ # of some of these expressions if one does not do so. They however aren’t
31
+ # keywords, neither are they constants, but nearly predefined, so they
32
+ # get tagged as `predefined' rather than anything else.
33
+ #
34
+ # This list excludes values of form `_UPPERCASE' because the Lua manual
35
+ # requires such identifiers to be reserved by Lua anyway and they are
36
+ # highlighted directly accordingly, without the need for specific
37
+ # identifiers to be listed here.
38
+ PREDEFINED_EXPRESSIONS = %w[
39
+ assert collectgarbage dofile error getmetatable
40
+ ipairs load loadfile next pairs pcall print
41
+ rawequal rawget rawlen rawset select setmetatable
42
+ tonumber tostring type xpcall
43
+ ]
44
+
45
+ # Automatic token kind selection for normal words.
46
+ IDENT_KIND = CodeRay::WordList.new(:ident).
47
+ add(KEYWORDS, :keyword).
48
+ add(PREDEFINED_CONSTANTS, :predefined_constant).
49
+ add(PREDEFINED_EXPRESSIONS, :predefined)
50
+
51
+ protected
52
+
53
+ # Scanner initialization.
54
+ def setup
55
+ @state = :initial
56
+ @brace_depth = 0
57
+ end
58
+
59
+ # CodeRay entry hook. Starts parsing.
60
+ def scan_tokens(encoder, options)
61
+ state = options[:state] || @state
62
+ brace_depth = @brace_depth
63
+ num_equals = nil
64
+
65
+ until eos?
66
+ case state
67
+
68
+ when :initial
69
+ if match = scan(/\-\-\[\=*\[/) #--[[ long (possibly multiline) comment ]]
70
+ num_equals = match.count("=") # Number must match for comment end
71
+ encoder.begin_group(:comment)
72
+ encoder.text_token(match, :delimiter)
73
+ state = :long_comment
74
+
75
+ elsif match = scan(/--.*$/) # --Lua comment
76
+ encoder.text_token(match, :comment)
77
+
78
+ elsif match = scan(/\[=*\[/) # [[ long (possibly multiline) string ]]
79
+ num_equals = match.count("=") # Number must match for comment end
80
+ encoder.begin_group(:string)
81
+ encoder.text_token(match, :delimiter)
82
+ state = :long_string
83
+
84
+ elsif match = scan(/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/) # ::goto_label::
85
+ encoder.text_token(match, :label)
86
+
87
+ elsif match = scan(/_[A-Z]+/) # _UPPERCASE are names reserved for Lua
88
+ encoder.text_token(match, :predefined)
89
+
90
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # Normal letters (or letters followed by digits)
91
+ kind = IDENT_KIND[match]
92
+
93
+ # Extra highlighting for entities following certain keywords
94
+ if kind == :keyword and match == "function"
95
+ state = :function_expected
96
+ elsif kind == :keyword and match == "goto"
97
+ state = :goto_label_expected
98
+ elsif kind == :keyword and match == "local"
99
+ state = :local_var_expected
100
+ end
101
+
102
+ encoder.text_token(match, kind)
103
+
104
+ elsif match = scan(/\{/) # Opening table brace {
105
+ encoder.begin_group(:map)
106
+ encoder.text_token(match, brace_depth >= 1 ? :inline_delimiter : :delimiter)
107
+ brace_depth += 1
108
+ state = :map
109
+
110
+ elsif match = scan(/\}/) # Closing table brace }
111
+ if brace_depth == 1
112
+ brace_depth = 0
113
+ encoder.text_token(match, :delimiter)
114
+ encoder.end_group(:map)
115
+ elsif brace_depth == 0 # Mismatched brace
116
+ encoder.text_token(match, :error)
117
+ else
118
+ brace_depth -= 1
119
+ encoder.text_token(match, :inline_delimiter)
120
+ encoder.end_group(:map)
121
+ state = :map
122
+ end
123
+
124
+ elsif match = scan(/["']/) # String delimiters " and '
125
+ encoder.begin_group(:string)
126
+ encoder.text_token(match, :delimiter)
127
+ start_delim = match
128
+ state = :string
129
+
130
+ # ↓Prefix hex number ←|→ decimal number
131
+ elsif match = scan(/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
132
+ encoder.text_token(match, :float)
133
+
134
+ # ↓Prefix hex number ←|→ decimal number
135
+ elsif match = scan(/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
136
+ encoder.text_token(match, :integer)
137
+
138
+ elsif match = scan(/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x) # Operators
139
+ encoder.text_token(match, :operator)
140
+
141
+ elsif match = scan(/\s+/) # Space
142
+ encoder.text_token(match, :space)
143
+
144
+ else # Invalid stuff. Note that Lua doesn’t accept multibyte chars outside of strings, hence these are also errors.
145
+ encoder.text_token(getch, :error)
146
+ end
147
+
148
+ # It may be that we’re scanning a full-blown subexpression of a table
149
+ # (tables can contain full expressions in parts).
150
+ # If this is the case, return to :map scanning state.
151
+ state = :map if state == :initial && brace_depth >= 1
152
+
153
+ when :function_expected
154
+ if match = scan(/\(.*?\)/m) # x = function() # "Anonymous" function without explicit name
155
+ encoder.text_token(match, :operator)
156
+ state = :initial
157
+ elsif match = scan(/[a-zA-Z_] (?:[a-zA-Z0-9_\.] (?!\.\d))* [\.\:]/x) # function tbl.subtbl.foo() | function tbl:foo() # Colon only allowed as last separator
158
+ encoder.text_token(match, :ident)
159
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # function foo()
160
+ encoder.text_token(match, :function)
161
+ state = :initial
162
+ elsif match = scan(/\s+/) # Between the `function' keyword and the ident may be any amount of whitespace
163
+ encoder.text_token(match, :space)
164
+ else
165
+ encoder.text_token(getch, :error)
166
+ state = :initial
167
+ end
168
+
169
+ when :goto_label_expected
170
+ if match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
171
+ encoder.text_token(match, :label)
172
+ state = :initial
173
+ elsif match = scan(/\s+/) # Between the `goto' keyword and the label may be any amount of whitespace
174
+ encoder.text_token(match, :space)
175
+ else
176
+ encoder.text_token(getch, :error)
177
+ end
178
+
179
+ when :local_var_expected
180
+ if match = scan(/function/) # local function ...
181
+ encoder.text_token(match, :keyword)
182
+ state = :function_expected
183
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
184
+ encoder.text_token(match, :local_variable)
185
+ elsif match = scan(/,/)
186
+ encoder.text_token(match, :operator)
187
+ elsif match = scan(/\=/)
188
+ encoder.text_token(match, :operator)
189
+ # After encountering the equal sign, arbitrary expressions are
190
+ # allowed again, so just return to the main state for further
191
+ # parsing.
192
+ state = :initial
193
+ elsif match = scan(/\n/)
194
+ encoder.text_token(match, :space)
195
+ state = :initial
196
+ elsif match = scan(/\s+/)
197
+ encoder.text_token(match, :space)
198
+ else
199
+ encoder.text_token(getch, :error)
200
+ end
201
+
202
+ when :long_comment
203
+ if match = scan(/.*?(?=\]={#{num_equals}}\])/m)
204
+ encoder.text_token(match, :content)
205
+
206
+ delim = scan(/\]={#{num_equals}}\]/)
207
+ encoder.text_token(delim, :delimiter)
208
+ else # No terminator found till EOF
209
+ encoder.text_token(rest, :error)
210
+ terminate
211
+ end
212
+ encoder.end_group(:comment)
213
+ state = :initial
214
+
215
+ when :long_string
216
+ if match = scan(/.*?(?=\]={#{num_equals}}\])/m) # Long strings do not interpret any escape sequences
217
+ encoder.text_token(match, :content)
218
+
219
+ delim = scan(/\]={#{num_equals}}\]/)
220
+ encoder.text_token(delim, :delimiter)
221
+ else # No terminator found till EOF
222
+ encoder.text_token(rest, :error)
223
+ terminate
224
+ end
225
+ encoder.end_group(:string)
226
+ state = :initial
227
+
228
+ when :string
229
+ if match = scan(/[^\\#{start_delim}\n]+/) # Everything except \ and the start delimiter character is string content (newlines are only allowed if preceeded by \ or \z)
230
+ encoder.text_token(match, :content)
231
+ elsif match = scan(/\\(?:['"abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m)
232
+ encoder.text_token(match, :char)
233
+ elsif match = scan(Regexp.compile(start_delim))
234
+ encoder.text_token(match, :delimiter)
235
+ encoder.end_group(:string)
236
+ state = :initial
237
+ elsif match = scan(/\n/) # Lua forbids unescaped newlines in normal non-long strings
238
+ encoder.text_token("\\n\n", :error) # Visually appealing error indicator--otherwise users may wonder whether the highlighter cannot highlight multine strings
239
+ encoder.end_group(:string)
240
+ state = :initial
241
+ else
242
+ encoder.text_token(getch, :error)
243
+ end
244
+
245
+ when :map
246
+ if match = scan(/[,;]/)
247
+ encoder.text_token(match, :operator)
248
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]* (?=\s*=)/x)
249
+ encoder.text_token(match, :key)
250
+ encoder.text_token(scan(/\s+/), :space) if check(/\s+/)
251
+ encoder.text_token(scan(/\=/), :operator)
252
+ state = :initial
253
+ elsif match = scan(/\s+/m)
254
+ encoder.text_token(match, :space)
255
+ else
256
+ # Note this clause doesn’t advance the scan pointer, it’s a kind of
257
+ # "retry with other options" (the :initial state then of course
258
+ # advances the pointer).
259
+ state = :initial
260
+ end
261
+ else
262
+ raise
263
+ end
264
+
265
+ end
266
+
267
+ if options[:keep_state]
268
+ @state = state
269
+ end
270
+
271
+ encoder.end_group :string if [:string].include? state
272
+ brace_depth.times { encoder.end_group :map }
273
+
274
+ encoder
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+ end