Class: RbYAML::Emitter

Inherits:
Object show all
Defined in:
lib/rbyaml/emitter.rb

Constant Summary collapse

DEFAULT_TAG_PREFIXES =
{
  "!" => "!",
  "tag:yaml.org,2002:" => "!!"
}
ESCAPE_REPLACEMENTS =
{
  ?\0   =>   "0",
  ?\x07 =>   "a",
  ?\x08 =>   "b",
  ?\x09 =>   "t",
  ?\x0A =>   "n",
  ?\x0B =>   "v",
  ?\x0C =>   "f",
  ?\x0D =>   "r",
  ?\x1B =>   "e",
  ?"    =>   "\"",
  ?\\   =>   "\\",
  ?\x85 =>   "N",
  ?\xA0 =>   "_"
}

Instance Method Summary collapse

Constructor Details

#initialize(stream, canonical = nil, indent = nil, width = nil, line_break = nil) ⇒ Emitter

Returns a new instance of Emitter.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rbyaml/emitter.rb', line 23

def initialize(stream, canonical=nil, indent=nil, width=nil,line_break=nil)
  # The stream should have the methods `write` and possibly `flush`.
  @stream = stream

  # Emitter is a state machine with a stack of states to handle nested
  # structures.
  @states = []
  @state = :expect_stream_start

  # Current event and the event queue.
  @events = []
  @event = nil

  # The current indentation level and the stack of previous indents.
  @indents = []
  @indent = nil

  # Flow level.
  @flow_level = 0

  # Contexts.
  @root_context = false
  @sequence_context = false
  @mapping_context = false
  @simple_key_context = false

  # Characteristics of the last emitted character:
  #  - current position.
  #  - is it a whitespace?
  #  - is it an indention character
  #    (indentation space, '-', '?', or ':')?
  @line = 0
  @column = 0
  @whitespace = true
  @indention = true

  # Formatting details.
  @canonical = canonical
  @best_indent = 2
  @best_indent = indent if indent && indent!=0 && 1 < indent && indent < 10

  @best_width = 80
  @best_width = width if width && width != 0 && width > @best_indent*2
  
  @best_line_break = "\n"
  @best_line_break = line_break if ["\r", "\n", "\r\n"].include?(line_break)

  # Tag prefixes.
  @tag_prefixes = nil

  # Prepared anchor and tag.
  @prepared_anchor = nil
  @prepared_tag = nil

  # Scalar analysis and style.
  @analysis = nil
  @style = nil
end

Instance Method Details

#analyze_scalar(scalar) ⇒ Object



592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
# File 'lib/rbyaml/emitter.rb', line 592

def analyze_scalar(scalar)
  # Empty scalar is a special case.
  return ScalarAnalysis.new(scalar,true,false,false,true,true,true,false) if scalar.nil? || scalar.empty?
  # Indicators and special characters.
  block_indicators = false
  flow_indicators = false
  line_breaks = false
  special_characters = false

  # Whitespaces.
  inline_spaces = false          # non-space space+ non-space
  inline_breaks = false          # non-space break+ non-space
  leading_spaces = false         # ^ space+ (non-space | $)
  leading_breaks = false         # ^ break+ (non-space | $)
  trailing_spaces = false        # (^ | non-space) space+ $
  trailing_breaks = false        # (^ | non-space) break+ $
  inline_breaks_spaces = false   # non-space break+ space+ non-space
  mixed_breaks_spaces = false    # anything else

  # Check document indicators.
  if /^(---|\.\.\.)/ =~ scalar
    block_indicators = true
    flow_indicators = true
  end

  # First character or preceded by a whitespace.
  preceeded_by_space = true

  # Last character or followed by a whitespace.
  followed_by_space = scalar.length == 1 || "\0 \t\r\n\x85".include?(scalar[1])

  # The current series of whitespaces contain plain spaces.
  spaces = false

  # The current series of whitespaces contain line breaks.
  breaks = false

  # The current series of whitespaces contain a space followed by a
  # break.
  mixed = false

  # The current series of whitespaces start at the beginning of the
  # scalar.
  leading = false
  
  index = 0
  while index < scalar.length
    ch = scalar[index]
    
    # Check for indicators.
    
    if index == 0
      # Leading indicators are special characters.
      if "#,[]{}#&*!|>'\"%@`".include?(ch) 
        flow_indicators = true
        block_indicators = true
      end
      if "?:".include?(ch)
        flow_indicators = true
        if followed_by_space
          block_indicators = true
        end
      end
      if ch == ?- && followed_by_space
        flow_indicators = true
        block_indicators = true
      end
    else
      # Some indicators cannot appear within a scalar as well.
      flow_indicators = true if ",?[]{}".include?(ch)
      if ch == ?:
        flow_indicators = true
        block_indicators = true if followed_by_space
      end
      if ch == ?# && preceeded_by_space
          flow_indicators = true
        block_indicators = true
      end
    end
    # Check for line breaks, special, and unicode characters.
    line_breaks = true if "\n\x85".include?(ch)
    if !(ch == ?\n || (?\x20 <= ch && ch <= ?\x7E))
      special_characters = true
    end
    # Spaces, line breaks, and how they are mixed. State machine.
    
    # Start or continue series of whitespaces.
    if " \n\x85".include?(ch)
      if spaces && breaks
        mixed = true if ch != 32      # break+ (space+ break+)    => mixed
      elsif spaces
        if ch != 32      # (space+ break+)   => mixed
          breaks = true
          mixed = true
        end
      elsif breaks
        spaces = true if ch == 32      # break+ space+
      else
        leading = (index == 0)
        if ch == 32      # space+
          spaces = true
        else               # break+
          breaks = true
        end
      end
      # Series of whitespaces ended with a non-space.
    elsif spaces || breaks
      if leading
        if spaces && breaks
          mixed_breaks_spaces = true
        elsif spaces
          leading_spaces = true
        elsif breaks
          leading_breaks = true
        end
      else
        if mixed
          mixed_breaks_spaces = true
        elsif spaces && breaks
          inline_breaks_spaces = true
        elsif spaces
          inline_spaces = true
        elsif breaks
          inline_breaks = true
        end
      end
      spaces = breaks = mixed = leading = false
    end
    
    # Series of whitespaces reach the end.
    if (spaces || breaks) && (index == scalar.length-1)
      if spaces && breaks
        mixed_breaks_spaces = true
      elsif spaces
        trailing_spaces = true
        leading_spaces = true if leading
      elsif breaks
        trailing_breaks = true
        leading_breaks = true if leading
      end    
      spaces = breaks = mixed = leading = false
    end
    # Prepare for the next character.
    index += 1
    preceeded_by_space = "\0 \t\r\n\x85".include?(ch)
    followed_by_space = index+1 >= scalar.length || "\0 \t\r\n\x85".include?(scalar[index+1])
  end
  # Let's decide what styles are allowed.
  allow_flow_plain = true
  allow_block_plain = true
  allow_single_quoted = true
  allow_double_quoted = true
  allow_block = true
  # Leading and trailing whitespace are bad for plain scalars. We also
  # do not want to mess with leading whitespaces for block scalars.
  allow_flow_plain = allow_block_plain = allow_block = false if leading_spaces || leading_breaks || trailing_spaces

  # Trailing breaks are fine for block scalars, but unacceptable for
  # plain scalars.
  allow_flow_plain = allow_block_plain = false if trailing_breaks

  # The combination of (space+ break+) is only acceptable for block
  # scalars.
  allow_flow_plain = allow_block_plain = allow_single_quoted = false if inline_breaks_spaces

  # Mixed spaces and breaks, as well as special character are only
  # allowed for double quoted scalars.
  allow_flow_plain = allow_block_plain = allow_single_quoted = allow_block = false if mixed_breaks_spaces || special_characters

  # We don't emit multiline plain scalars.
  allow_flow_plain = allow_block_plain = false if line_breaks

  # Flow indicators are forbidden for flow plain scalars.
  allow_flow_plain = false if flow_indicators

  # Block indicators are forbidden for block plain scalars.
  allow_block_plain = false if block_indicators

  ScalarAnalysis.new(scalar,false,line_breaks,allow_flow_plain,allow_block_plain,allow_single_quoted,allow_double_quoted,allow_block)
end

#check_empty_documentObject



439
440
441
442
443
# File 'lib/rbyaml/emitter.rb', line 439

def check_empty_document
  return false if !@event.__is_document_start || @events.empty?
  event = @events.first
  event.__is_scalar && event.anchor.nil? && event.tag.nil? && event.implicit && event.value == ""
end

#check_empty_mappingObject



435
436
437
# File 'lib/rbyaml/emitter.rb', line 435

def check_empty_mapping
  @event.__is_mapping_start && !@events.empty? && @events.first.__is_mapping_end
end

#check_empty_sequenceObject

Checkers.



431
432
433
# File 'lib/rbyaml/emitter.rb', line 431

def check_empty_sequence
  @event.__is_sequence_start && !@events.empty? && @events.first.__is_sequence_end
end

#check_simple_keyObject



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/rbyaml/emitter.rb', line 445

def check_simple_key
  length = 0
  if @event.__is_node && !@event.anchor.nil?
    @prepared_anchor = prepare_anchor(@event.anchor) if @prepared_anchor.nil?
    length += @prepared_anchor.length
  end
  if (@event.__is_scalar || @event.__is_collection_start) && !@event.tag.nil?
    @prepared_tag = prepare_tag(@event.tag) if @prepared_tag.nil?
    length += @prepared_tag.length
  end
  if @event.__is_scalar
    @analysis = analyze_scalar(@event.value) if @analysis.nil?
    length += @analysis.scalar.length
  end

  (length < 128 && (@event.__is_alias || (@event.__is_scalar && !@analysis.empty && !@analysis.multiline) || 
                    check_empty_sequence || check_empty_mapping))
end

#choose_scalar_styleObject



501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rbyaml/emitter.rb', line 501

def choose_scalar_style
  @analysis = analyze_scalar(@event.value) if @analysis.nil?
  return '"' if @event.style == '"' || @canonical            
  if !@event.style && @event.implicit[0]
    if !(@simple_key_context && (@analysis.empty || @analysis.multiline)) && ((@flow_level!=0 && @analysis.allow_flow_plain) || (@flow_level == 0 && @analysis.allow_block_plain))
      return ""
    end
  end

  if !@event.style && @event.implicit && (!(@simple_key_context && (@analysis.empty || @analysis.multiline)) && 
                                          (@flow_level!=0 && @analysis.allow_flow_plain || (@flow_level==0 && @analysis.allow_block_plain)))
    return ""
  end
  return @event.style if @event.style && /^[|>]$/ =~ @event.style && @flow_level==0 && @analysis.allow_block
  return "'" if (!@event.style || @event.style == "'") && (@analysis.allow_single_quoted && !(@simple_key_context && @analysis.multiline))
  return '"'
end

#determine_chomp(text) ⇒ Object



952
953
954
955
956
# File 'lib/rbyaml/emitter.rb', line 952

def determine_chomp(text)
  tail = text[-2..-1]
  tail = " "+tail while tail.length < 2
  "\n\x85".include?(tail[-1])? ("\n\x85".include?(tail[-2])? "+" : "" ) : "-"
end

#emit(event) ⇒ Object



82
83
84
85
86
87
88
89
# File 'lib/rbyaml/emitter.rb', line 82

def emit(event)
  @events << event
  while !need_more_events
    @event = @events.shift
    send(@state)
    @event = nil
  end
end

#expect_aliasObject

Raises:



246
247
248
249
250
# File 'lib/rbyaml/emitter.rb', line 246

def expect_alias
  raise EmitterError.new("anchor is not specified for alias") if @event.anchor.nil?
  process_anchor("*")
  @state = @states.pop
end

#expect_block_mappingObject

Block mapping handlers.



390
391
392
393
# File 'lib/rbyaml/emitter.rb', line 390

def expect_block_mapping
  increase_indent(false)
  @state = :expect_first_block_mapping_key
end

#expect_block_mapping_key(first = false) ⇒ Object



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/rbyaml/emitter.rb', line 399

def expect_block_mapping_key(first=false)
  if !first && MappingEndEvent === @event
    @indent = @indents.pop
    @state = @states.pop
  else
    write_indent
    if check_simple_key
      @states << :expect_block_mapping_simple_value
      expect_node(false,false,true,true)
    else
      write_indicator("?", true, false, true)
      @states << :expect_block_mapping_value
      expect_node(false,false,true)
    end
  end
end

#expect_block_mapping_simple_valueObject



416
417
418
419
420
# File 'lib/rbyaml/emitter.rb', line 416

def expect_block_mapping_simple_value
  write_indicator(":", false)
  @states << :expect_block_mapping_key
  expect_node(false,false,true)
end

#expect_block_mapping_valueObject



422
423
424
425
426
427
# File 'lib/rbyaml/emitter.rb', line 422

def expect_block_mapping_value
  write_indent
  write_indicator(":",true,false,true)
  @states << :expect_block_mapping_key
  expect_node(false,false,true)
end

#expect_block_sequenceObject

Block sequence handlers.



366
367
368
369
370
# File 'lib/rbyaml/emitter.rb', line 366

def expect_block_sequence
  indentless = @mapping_context && !@indention
  increase_indent(false,indentless)
  @state = :expect_first_block_sequence_item
end

#expect_block_sequence_item(first = false) ⇒ Object



376
377
378
379
380
381
382
383
384
385
386
# File 'lib/rbyaml/emitter.rb', line 376

def expect_block_sequence_item(first=false)
  if !first && SequenceEndEvent === @event
    @indent = @indents.pop
    @state = @states.pop
  else
    write_indent
    write_indicator("-", true, false, true)
    @states << :expect_block_sequence_item
    expect_node(false,true)
  end
end

#expect_document_endObject



195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/rbyaml/emitter.rb', line 195

def expect_document_end
  if DocumentEndEvent === @event
    write_indent
    if @event.explicit
      write_indicator("...", true)
      write_indent
    end
    flush_stream
    @state = :expect_document_start
  else
    raise EmitterError.new("expected DocumentEndEvent, but got #{@event}")
  end
end

#expect_document_rootObject



209
210
211
212
# File 'lib/rbyaml/emitter.rb', line 209

def expect_document_root
  @states << :expect_document_end
  expect_node(true)
end

#expect_document_start(first = false) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/rbyaml/emitter.rb', line 160

def expect_document_start(first=false)
  if DocumentStartEvent === @event
    if @event.version
      version_text = prepare_version(@event.version)
      write_version_directive(version_text)
    end
    @tag_prefixes = Emitter::DEFAULT_TAG_PREFIXES.dup
    if @event.tags
      handles = @event.tags.keys
      handles.sort!
      for handle in handles
        prefix = @event.tags[handle]
        @tag_prefixes[prefix] = handle
        handle_text = prepare_tag_handle(handle)
        prefix_text = prepare_tag_prefix(prefix)
        write_tag_directive(handle_text, prefix_text)
      end
    end
    implicit = first && !@event.explicit && !@canonical && !@event.version && !@event.tags && !check_empty_document
    if !implicit
      write_indent
      write_indicator("---",true)
      if @canonical
        write_indent
      end
    end
    @state = :expect_document_root
  elsif StreamEndEvent === @event
    write_stream_end
    @state = :expect_nothing
  else
    raise EmitterError.new("expected DocumentStartEvent, but got #{@event}")
  end
end

#expect_first_block_mapping_keyObject



395
396
397
# File 'lib/rbyaml/emitter.rb', line 395

def expect_first_block_mapping_key
  expect_block_mapping_key(true)
end

#expect_first_block_sequence_itemObject



372
373
374
# File 'lib/rbyaml/emitter.rb', line 372

def expect_first_block_sequence_item
  expect_block_sequence_item(true)
end

#expect_first_document_startObject

Document handlers.



156
157
158
# File 'lib/rbyaml/emitter.rb', line 156

def expect_first_document_start
  expect_document_start(true)
end

#expect_first_flow_mapping_keyObject



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/rbyaml/emitter.rb', line 308

def expect_first_flow_mapping_key
  if MappingEndEvent === @event
    @indent = @indents.pop
    @flow_level -= 1
    write_indicator("}", false)
    @state = @states.pop
  else
    write_indent if @canonical || @column > @best_width
    if !@canonical && check_simple_key
      @states << :expect_flow_mapping_simple_value
      expect_node(false,false,true,true)
    else
      write_indicator("?", true)
      @states << :expect_flow_mapping_value
      expect_node(false,false,true)
    end
  end
end

#expect_first_flow_sequence_itemObject



268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/rbyaml/emitter.rb', line 268

def expect_first_flow_sequence_item
  if SequenceEndEvent === @event
    @indent = @indents.pop
    @flow_level -= 1
    write_indicator("]", false)
    @state = @states.pop
  else
    write_indent if @canonical || @column > @best_width
    @states << :expect_flow_sequence_item
    expect_node(false,true)
  end
end

#expect_flow_mappingObject

Flow mapping handlers.



301
302
303
304
305
306
# File 'lib/rbyaml/emitter.rb', line 301

def expect_flow_mapping
  write_indicator("{", true, true)
  @flow_level += 1
  increase_indent(true)
  @state = :expect_first_flow_mapping_key
end

#expect_flow_mapping_keyObject



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/rbyaml/emitter.rb', line 327

def expect_flow_mapping_key
  if MappingEndEvent === @event
    @indent = @indents.pop
    @flow_level -= 1
    if @canonical
      write_indicator(",", false)
      write_indent
    end
    write_indicator("}", false)
    @state = @states.pop
  else
    write_indicator(",", false)
    write_indent if @canonical || @column > @best_width
    if !@canonical && check_simple_key
      @states << :expect_flow_mapping_simple_value
      expect_node(false,false,true,true)
    else
      write_indicator("?", true)
      @states << :expect_flow_mapping_value
      expect_node(false,false,true)
    end
  end
end

#expect_flow_mapping_simple_valueObject



351
352
353
354
355
# File 'lib/rbyaml/emitter.rb', line 351

def expect_flow_mapping_simple_value
  write_indicator(":", false)
  @states << :expect_flow_mapping_key
  expect_node(false,false,true)
end

#expect_flow_mapping_valueObject



357
358
359
360
361
362
# File 'lib/rbyaml/emitter.rb', line 357

def expect_flow_mapping_value
  write_indent if @canonical || @column > @best_width
  write_indicator(":", true)
  @states << :expect_flow_mapping_key
  expect_node(false,false,true)
end

#expect_flow_sequenceObject

Flow sequence handlers.



261
262
263
264
265
266
# File 'lib/rbyaml/emitter.rb', line 261

def expect_flow_sequence
  write_indicator("[", true, true)
  @flow_level += 1
  increase_indent(true)
  @state = :expect_first_flow_sequence_item
end

#expect_flow_sequence_itemObject



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/rbyaml/emitter.rb', line 281

def expect_flow_sequence_item
  if SequenceEndEvent === @event
    @indent =  @indents.pop
    @flow_level -= 1
    if @canonical
      write_indicator(",",false)
      write_indent
    end
    write_indicator("]",false)
    @state = @states.pop
  else
    write_indicator(",", false)
    write_indent if @canonical or @column > @best_width
    @states << :expect_flow_sequence_item
    expect_node(false,true)
  end
end

#expect_node(root = false, sequence = false, mapping = false, simple_key = false) ⇒ Object

Node handlers.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/rbyaml/emitter.rb', line 216

def expect_node(root=false, sequence=false, mapping=false, simple_key=false)
  @root_context = root
  @sequence_context = sequence
  @mapping_context = mapping
  @simple_key_context = simple_key
  if AliasEvent === @event
    expect_alias
  elsif ScalarEvent === @event || CollectionStartEvent === @event
    process_anchor("&")
    process_tag
    if ScalarEvent === @event
      expect_scalar
    elsif SequenceStartEvent === @event
      if @flow_level!=0 || @canonical || @event.flow_style || check_empty_sequence
        expect_flow_sequence
      else
        expect_block_sequence
      end
    elsif MappingStartEvent === @event
      if @flow_level!=0 || @canonical || @event.flow_style || check_empty_mapping
        expect_flow_mapping
      else
        expect_block_mapping
      end
    end
  else
    raise EmitterError.new("expected NodeEvent, but got #{@event}")
  end
end

#expect_nothingObject

Raises:



150
151
152
# File 'lib/rbyaml/emitter.rb', line 150

def expect_nothing
  raise EmitterError.new("expected nothing, but got #{@event}")
end

#expect_scalarObject



252
253
254
255
256
257
# File 'lib/rbyaml/emitter.rb', line 252

def expect_scalar
  increase_indent(true)
  process_scalar
  @indent = @indents.pop
  @state = @states.pop
end

#expect_stream_startObject

Stream handlers.



141
142
143
144
145
146
147
148
# File 'lib/rbyaml/emitter.rb', line 141

def expect_stream_start
  if StreamStartEvent === @event
    write_stream_start
    @state = :expect_first_document_start
  else
    raise EmitterError.new("expected StreamStartEvent, but got #{@event}")
  end
end

#flush_streamObject

Writers.



775
776
777
# File 'lib/rbyaml/emitter.rb', line 775

def flush_stream
  @stream.flush if @stream.respond_to?(:flush)
end

#increase_indent(flow = false, indentless = false) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rbyaml/emitter.rb', line 124

def increase_indent(flow=false, indentless=false)
  @indents << @indent
  if @indent.nil?
    if flow
      @indent = @best_indent
    else
      @indent = 0
    end
  elsif !indentless
    @indent += @best_indent
  end
end

#need_events(count) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rbyaml/emitter.rb', line 107

def need_events(count)
  level = 0
  for event in @events[1..-1]
    if DocumentStartEvent === event || CollectionStartEvent === event
      level += 1
    elsif DocumentEndEvent === event || CollectionEndEvent === event
      level -= 1
    elsif StreamEndEvent === event
      level = -1
    end
    if level < 0
      return false
    end
  end
  @events.length < count+1
end

#need_more_eventsObject

In some cases, we wait for a few next events before emitting.



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rbyaml/emitter.rb', line 93

def need_more_events
  return true if @events.empty?
  event = @events.first
  if DocumentStartEvent === event
    need_events(1)
  elsif SequenceStartEvent === event
    need_events(2)
  elsif MappingStartEvent === event
    need_events(3)
  else
    false
  end
end

#prepare_anchor(anchor) ⇒ Object

Raises:



586
587
588
589
590
# File 'lib/rbyaml/emitter.rb', line 586

def prepare_anchor(anchor)
  raise EmitterError.new("anchor must not be empty") if anchor.nil? || anchor.empty?
  raise EmitterError.new("invalid character #{$&} in the anchor: #{anchor}") if /[^-\w]/ =~ anchor
  anchor
end

#prepare_tag(tag) ⇒ Object

Raises:



563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
# File 'lib/rbyaml/emitter.rb', line 563

def prepare_tag(tag)
  raise EmitterError.new("tag must not be empty") if tag.nil? || tag.empty?
  return tag if tag == "!"
  handle = nil
  suffix = tag
  for prefix in @tag_prefixes.keys
    if Regexp.new("^"+Regexp.escape(prefix)) =~ tag && (prefix == "!" || prefix.length < tag.length)
      handle = @tag_prefixes[prefix]
      suffix = tag[prefix.length..-1]
    end
  end
  chunks = []
  start = ending = 0
  ending += 1 while ending < suffix.length
  chunks << suffix[start...ending] if start < ending
  suffix_text = chunks.to_s
  if handle
    "#{handle}#{suffix_text}"
  else
    "!<#{suffix_text}>"
  end
end

#prepare_tag_handle(handle) ⇒ Object

Raises:



546
547
548
549
550
551
# File 'lib/rbyaml/emitter.rb', line 546

def prepare_tag_handle(handle)
  raise EmitterError.new("tag handle must not be empty") if handle.nil? || handle.empty?
  raise EmitterError("tag handle must start and end with '!': #{handle}") if handle[0] != ?! || handle[-1] != ?!
  raise EmitterError.new("invalid character #{$&} in the tag handle: #{handle}") if /[^-\w]/ =~ handle[1...-1]
  handle
end

#prepare_tag_prefix(prefix) ⇒ Object

Raises:



553
554
555
556
557
558
559
560
561
# File 'lib/rbyaml/emitter.rb', line 553

def prepare_tag_prefix(prefix)
  raise EmitterError.new("tag prefix must not be empty") if prefix.nil? || prefix.empty?
  chunks = []
  start = ending = 0
  ending = 1 if prefix[0] == ?!
  ending += 1 while ending < prefix.length
  chunks << prefix[start...ending] if start < ending
  chunks.to_s
end

#prepare_version(version) ⇒ Object

Analyzers.

Raises:



540
541
542
543
544
# File 'lib/rbyaml/emitter.rb', line 540

def prepare_version(version)
  major, minor = version
  raise EmitterError.new("unsupported YAML version: #{major}.#{minor}") if major != 1
  "#{major}.#{minor}"
end

#process_anchor(indicator) ⇒ Object

Anchor, Tag, and Scalar processors.



467
468
469
470
471
472
473
474
475
# File 'lib/rbyaml/emitter.rb', line 467

def process_anchor(indicator)
  if @event.anchor.nil?
    @prepared_anchor = nil
    return nil
  end
  @prepared_anchor = prepare_anchor(@event.anchor) if @prepared_anchor.nil?
  write_indicator(indicator+@prepared_anchor, true) if @prepared_anchor && !@prepared_anchor.empty?
  @prepared_anchor = nil
end

#process_scalarObject



519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/rbyaml/emitter.rb', line 519

def process_scalar
  @analysis = analyze_scalar(@event.value) if @analysis.nil?
  @style = choose_scalar_style if @style.nil?
  split = !@simple_key_context
  if @style == '"'
    write_double_quoted(@analysis.scalar, split)
  elsif @style == "'"
    write_single_quoted(@analysis.scalar, split)
  elsif @style == ">"
    write_folded(@analysis.scalar)
  elsif @style == "|"
    write_literal(@analysis.scalar)
  else
    write_plain(@analysis.scalar, split)
  end
  @analysis = nil
  @style = nil
end

#process_tagObject

Raises:



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/rbyaml/emitter.rb', line 477

def process_tag
  tag = @event.tag
  if ScalarEvent === @event
    @style = choose_scalar_style if @style.nil?
    if ((!@canonical || tag.nil?) && ((@style == "" && @event.implicit[0]) || (@style != "" && @event.implicit[1])))
      @prepared_tag = nil
      return
    end
    if @event.implicit[0] && tag.nil?
      tag = "!"
      @prepared_tag = nil
    end
  else
    if (!@canonical || tag.nil?) && @event.implicit
      @prepared_tag = nil
      return
    end
  end
  raise EmitterError.new("tag is not specified") if tag.nil?
  @prepared_tag = prepare_tag(tag) if @prepared_tag.nil?
  write_indicator(@prepared_tag, true) if @prepared_tag && !@prepared_tag.empty?
  @prepared_tag = nil
end

#write_double_quoted(text, split = true) ⇒ Object



909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
# File 'lib/rbyaml/emitter.rb', line 909

def write_double_quoted(text, split=true)
  write_indicator('"', true)
  start = ending = 0
  while ending <= text.length
    ch = nil
    ch = text[ending] if ending < text.length
    if ch.nil? || "\"\\\x85".include?(ch) || !(?\x20 <= ch && ch <= ?\x7E)
      if start < ending
        data = text[start...ending]
        @column += data.length
        @stream.write(data)
        start = ending
      end
      if !ch.nil?
        if ESCAPE_REPLACEMENTS.include?(ch)
          data = "\\"+ESCAPE_REPLACEMENTS[ch]
        elsif ch <= ?\xFF
          data = "\\x%02X" % ch
        end
        @column += data.length
        @stream.write(data)
        start = ending+1
      end
    end
    if (0 < ending && ending < text.length-1) && (ch == 32 || start >= ending) && @column+(ending-start) > @best_width && split
      data = text[start...ending]+"\\"
      start = ending if start < ending
      @column += data.length
      @stream.write(data)
      write_indent
      @whitespace = false
      @indention = false
      if text[start] == 32
        data = "\\"
        @column += data.length
        @stream.write(data)
      end
    end
    ending += 1
  end
  write_indicator('"', false)
end

#write_folded(text) ⇒ Object



958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
# File 'lib/rbyaml/emitter.rb', line 958

def write_folded(text)
  chomp = determine_chomp(text)
  write_indicator(">"+chomp, true)
  write_indent
  leading_space = false
  spaces = false
  breaks = false
  start = ending = 0
  while ending <= text.length
    ch = nil
    ch = text[ending] if ending < text.length
    if breaks
      if ch.nil? || !"\n\x85".include?(ch)
        write_line_break if !leading_space && !ch.nil? && ch != 32 && text[start] == ?\n                        
        leading_space = ch == 32
        (text[start...ending]).each_byte { |br|
          if br == ?\n
            write_line_break
          else
            write_line_break(br)
          end
        }
        write_indent if !ch.nil?
        start = ending
      end
    elsif spaces
      if ch != 32
        if start+1 == ending && @column > @best_width
          write_indent
        else
          data = text[start...ending]
          @column += data.length
          @stream.write(data)
        end
        start = ending
      end
    else
      if ch.nil? || " \n\x85".include?(ch)
        data = text[start...ending]
        @stream.write(data)
        write_line_break if ch.nil?
        start = ending
      end
    end
    if !ch.nil?
      breaks = "\n\x85".include?(ch)
      spaces = ch == 32
    end
    ending += 1
  end
end

#write_indentObject



799
800
801
802
803
804
805
806
807
808
# File 'lib/rbyaml/emitter.rb', line 799

def write_indent
  indent = @indent || 0
  write_line_break if !@indention || @column > indent || (@column == indent && !@whitespace)
  if @column < indent
    @whitespace = true
    data = " "*(indent-@column)
    @column = indent
    @stream.write(data)
  end
end

#write_indicator(indicator, need_whitespace, whitespace = false, indention = false) ⇒ Object



786
787
788
789
790
791
792
793
794
795
796
797
# File 'lib/rbyaml/emitter.rb', line 786

def write_indicator(indicator, need_whitespace,whitespace=false,indention=false)
  if @whitespace || !need_whitespace
    data = indicator
  else
    data = " "+indicator
  end
  
  @whitespace = whitespace
  @indention = @indention && indention
  @column += data.length
  @stream.write(data)
end

#write_line_break(data = nil) ⇒ Object



810
811
812
813
814
815
816
817
# File 'lib/rbyaml/emitter.rb', line 810

def write_line_break(data=nil)
  data = @best_line_break if data.nil?
  @whitespace = true
  @indention = true
  @line += 1
  @column = 0
  @stream.write(data)
end

#write_literal(text) ⇒ Object



1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
# File 'lib/rbyaml/emitter.rb', line 1010

def write_literal(text)
  chomp = determine_chomp(text)
  write_indicator("|"+chomp, true)
  write_indent
  breaks = false
  start = ending = 0
  while ending <= text.length
    ch = nil
    ch = text[ending] if ending < text.length
    if breaks
      if ch.nil? || !"\n\x85".include?(ch)
        (text[start...ending]).each_byte { |br|
          if br == ?\n
            write_line_break
          else
            write_line_break(br)
          end
        }
        write_indent if !ch.nil?
        start = ending
      end
    else
      if ch.nil? || "\n\x85".include?(ch)
        data = text[start...ending]
        @stream.write(data)
        write_line_break if ch.nil?
        start = ending
      end
    end
    breaks = "\n\x85".include?(ch) if !ch.nil?
    ending += 1
  end
end

#write_plain(text, split = true) ⇒ Object



1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
# File 'lib/rbyaml/emitter.rb', line 1044

def write_plain(text, split=true)
  return nil if text.nil? || text.empty?
  if !@whitespace
    data = " "
    @column += data.length
    @stream.write(data)
  end
  @writespace = false
  @indention = false
  spaces = false
  breaks = false
  start = ending = 0
  while ending <= text.length
    ch = nil
    ch = text[ending] if ending < text.length
    if spaces
      if ch != 32
        if start+1 == ending && @column > @best_width && split
          write_indent
          @writespace = false
          @indention = false
        else
          data = text[start...ending]
          @column += data.length
          @stream.write(data)
        end
        start = ending
      end
    elsif breaks
      if !"\n\x85".include?(ch)
        write_line_break if text[start] == ?\n
        (text[start...ending]).each_byte { |br|
          if br == ?\n
            write_line_break
          else
            write_line_break(br)
          end
        }
        write_indent
        @whitespace = false
        @indention = false
        start = ending
      end
    else
      if ch.nil? || " \n\x85".include?(ch)
        data = text[start...ending]
        @column += data.length
        @stream.write(data)
        start = ending
      end
    end
    if !ch.nil?
      spaces = ch == 32
      breaks = "\n\x85".include?(ch)
    end
    ending += 1
  end
end

#write_single_quoted(text, split = true) ⇒ Object

Scalar streams.



834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
# File 'lib/rbyaml/emitter.rb', line 834

def write_single_quoted(text, split=true)
  write_indicator("'",true)
  spaces = false
  breaks = false
  start = ending = 0
  while ending <= text.length
    ch = nil
    ch = text[ending] if ending < text.length
    if spaces
      if ch.nil? || ch != 32
        if start+1 == ending && @column > @best_width && split && start != 0 && ending != text.length
          write_indent
        else
          data = text[start...ending]
          @column += data.length
          @stream.write(data)
        end
        start = ending
      end
    elsif breaks
      if ch.nil? or !"\n\x85".include?(ch)
        write_line_break if text[start] == ?\n
        (text[start...ending]).each_byte { |br|
          if br == ?\n
            write_line_break
          else
            write_line_break(br)
          end
        }
        write_indent
        start = ending
      end
    else
      if ch.nil? || "' \n\x85".include?(ch)
        if start < ending
          data = text[start...ending]
          @column += data.length
          @stream.write(data)
          start = ending
        end
        if ch == ?'
          data = "''"
          @column += 2
          @stream.write(data)
          start = ending + 1
        end
      end
    end
    
    if !ch.nil?
      spaces = ch == 32
      breaks = "\n\x85".include?(ch)
    end
    
    ending += 1
  end
  write_indicator("'", false)
end

#write_stream_endObject



782
783
784
# File 'lib/rbyaml/emitter.rb', line 782

def write_stream_end
  flush_stream
end

#write_stream_startObject



779
780
# File 'lib/rbyaml/emitter.rb', line 779

def write_stream_start
end

#write_tag_directive(handle_text, prefix_text) ⇒ Object



826
827
828
829
830
# File 'lib/rbyaml/emitter.rb', line 826

def write_tag_directive(handle_text, prefix_text)
  data = "%TAG #{handle_text} #{prefix_text}"
  @stream.write(data)
  write_line_break
end

#write_version_directive(version_text) ⇒ Object



820
821
822
823
824
# File 'lib/rbyaml/emitter.rb', line 820

def write_version_directive(version_text)
  data = "%YAML #{version_text}"
  @stream.write(data)
  write_line_break
end