coderay 1.0.9 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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