compound-ex4
Version:
Compound-ex4 - MVC framework for NodeJS (ExpressJs 4 version), fork compoundjs(https://github.com/1602/compound)
968 lines (822 loc) • 30.5 kB
text/coffeescript
events = require './events'
util = require './util'
{YAMLError} = require './errors'
class @EmitterError extends YAMLError
###
Emitter expects events obeying the following grammar:
stream ::= STREAM-START document* STREAM-END
document ::= DOCUMENT-START node DOCUMENT-END
node ::= SCALA | sequence | mapping
sequence ::= SEQUENCE-START node* SEQUENCE-END
mapping ::= MAPPING-START (node node)* MAPPING-END
###
class @Emitter
C_WHITESPACE = '\0 \t\r\n\x85\u2028\u2029'
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' : '_'
'\u2028': 'L'
'\u2029': 'P'
constructor: (@stream, options) ->
# Encoding can be overriden by STREAM-START
@encoding = null
# 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 = null
# The current indentation level and the stack of previous indents.
@indents = []
@indent = null
# 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 indentation character (indentation space, '-', '?', or ':')?
@line = 0
@column = 0
@whitespace = true
@indentation = true
# Whether the document requires an explicit document indicator.
@open_ended = false
# Formatting details
{ @canonical, @allow_unicode } = options
@canonical ?= false
@allow_unicode ?= true
@best_indent = if 1 < options.indent and options.indent < 10 then options.indent else 2
@best_width = if options.width > @indent * 2 then options.width else 80
@best_line_break = if options.line_break in [ '\r', '\n', '\r\n' ] then options.line_break else '\n'
# Tag prefixes.
@tag_prefixes = null
# Prepared anchor and tag
@prepared_anchor = null
@prepared_tag = null
# Scalar analysis and style.
@analysis = null
@style = null
###
Reset the state attributes (to clear self-references)
###
dispose: ->
@states = []
@state = null
emit: (event) ->
@events.push event
until @need_more_events()
@event = @events.shift()
@state()
@event = null
###
In some cases, we wait for a few next events before emitting.
###
need_more_events: ->
return true if @events.length is 0
event = @events[0]
if event instanceof events.DocumentStartEvent then @need_events 1
else if event instanceof events.SequenceStartEvent then @need_events 2
else if event instanceof events.MappingStartEvent then @need_events 3
else false
need_events: (count) ->
level = 0
for event in @events[1..]
if event instanceof events.DocumentStartEvent or event instanceof events.CollectionStartEvent
level++
else if event instanceof events.DocumentEndEvent or event instanceof events.CollectionEndEvent
level--
else if event instanceof events.StreamEndEvent
level = -1
return false if level < 0
@events.length < count + 1
increase_indent: (options = {}) ->
@indents.push @indent
if not @indent?
@indent = if options.flow then @best_indent else 0
else if not options.indentless
@indent += @best_indent
# Stream states
expect_stream_start: ->
if @event instanceof events.StreamStartEvent
@encoding = @event.encoding if @event.encoding and 'encoding' not of @stream
@write_stream_start()
@state = @expect_first_document_start
else
@error 'expected StreamStartEvent, but got', @event
expect_nothing: ->
@error 'expected nothing, but got', @event
# Document states
expect_first_document_start: ->
@expect_document_start true
expect_document_start: (first = false) ->
if @event instanceof events.DocumentStartEvent
if (@event.version or @event.tags) and @open_ended
@write_indicator '...', true
@write_indent()
if @event.version
@write_version_directive @prepare_version @event.version
@tag_prefixes = util.clone DEFAULT_TAG_PREFIXES
if @event.tags
for handle in (k for own k of @event.tags).sort()
prefix = @event.tags[handle]
@tag_prefixes[prefix] = handle
@write_tag_directive @prepare_tag_handle(handle), @prepare_tag_prefix(prefix)
explicit = not first or @event.explicit or @canonical or @event.version or @event.tags or @check_empty_document()
if explicit
@write_indent()
@write_indicator '---', true
@write_indent() if @canonical
@state = @expect_document_root
else if @event instanceof events.StreamEndEvent
if @open_ended
@write_indicator '...', true
@write_indent()
@write_stream_end()
@state = @expect_nothing
else
@error 'expected DocumentStartEvent, but got', @event
expect_document_end: ->
if @event instanceof events.DocumentEndEvent
@write_indent()
if @event.explicit
@write_indicator '...', true
@write_indent()
@flush_stream()
@state = @expect_document_start
else
@error 'expected DocumentEndEvent, but got', @event
expect_document_root: ->
@states.push @expect_document_end
@expect_node root: true
# Node states
expect_node: (expect = {}) ->
@root_context = !!expect.root
@sequence_context = !!expect.sequence
@mapping_context = !!expect.mapping
@simple_key_context = !!expect.simple_key
if @event instanceof events.AliasEvent
@expect_alias()
else if @event instanceof events.ScalarEvent or @event instanceof events.CollectionStartEvent
@process_anchor '&'
@process_tag()
if @event instanceof events.ScalarEvent
@expect_scalar()
else if @event instanceof events.SequenceStartEvent
if @flow_level or @canonical or @event.flow_style or @check_empty_sequence()
@expect_flow_sequence()
else
@expect_block_sequence()
else if @event instanceof events.MappingStartEvent
if @flow_level or @canonical or @event.flow_style or @check_empty_mapping()
@expect_flow_mapping()
else
@expect_block_mapping()
else
@error 'expected NodeEvent, but got', @event
expect_alias: ->
@error 'anchor is not specified for alias' unless @event.anchor
@process_anchor '*'
@state = @states.pop()
expect_scalar: ->
@increase_indent flow: true
@process_scalar()
@indent = @indents.pop()
@state = @states.pop()
# Flow sequence states
expect_flow_sequence: ->
@write_indicator '[', true, whitespace: true
@flow_level++
@increase_indent flow: true
@state = @expect_first_flow_sequence_item
expect_first_flow_sequence_item: ->
if @event instanceof events.SequenceEndEvent
@indent = @indents.pop()
@flow_level--
@write_indicator ']', false
@state = @states.pop()
else
@write_indent() if @canonical or @column > @best_width
@states.push @expect_flow_sequence_item
@expect_node sequence: true
expect_flow_sequence_item: ->
if @event instanceof events.SequenceEndEvent
@indent = @indents.pop()
@flow_level--
if @canonical
@write_indicator ',', false
@write_indent()
@write_indicator ']', false
@state = @states.pop()
else
@write_indicator ',', false
@write_indent() if @canonical or @column > @best_width
@states.push @expect_flow_sequence_item
@expect_node sequence: true
# Flow mapping states
expect_flow_mapping: ->
@write_indicator '{', true, whitespace: true
@flow_level++
@increase_indent flow: true
@state = @expect_first_flow_mapping_key
expect_first_flow_mapping_key: ->
if @event instanceof events.MappingEndEvent
@indent = @indents.pop()
@flow_level--
@write_indicator '}', false
@state = @states.pop()
else
@write_indent() if @canonical or @column > @best_width
if not @canonical and @check_simple_key()
@states.push @expect_flow_mapping_simple_value
@expect_node mapping: true, simple_key: true
else
@write_indicator '?', true
@states.push @expect_flow_mapping_value
@expect_node mapping: true
expect_flow_mapping_key: ->
if @event instanceof events.MappingEndEvent
@indent = @indents.pop()
@flow_level--
if @canonical
@write_indicator ',', false
@write_indent()
@write_indicator '}', false
@state = @states.pop()
else
@write_indicator ',', false
@write_indent() if @canonical or @column > @best_width
if not @canonical and @check_simple_key()
@states.push @expect_flow_mapping_simple_value
@expect_node mapping: true, simple_key: true
else
@write_indicator '?', true
@states.push @expect_flow_mapping_value
@expect_node mapping: true
expect_flow_mapping_simple_value: ->
@write_indicator ':', false
@states.push @expect_flow_mapping_key
@expect_node mapping: true
expect_flow_mapping_value: ->
@write_indent() if @canonical or @column > @best_width
@write_indicator ':', true
@states.push @expect_flow_mapping_key
@expect_node mapping: true
# Block sequence states
expect_block_sequence: ->
indentless = @mapping_context and not @indentation
@increase_indent { indentless }
@state = @expect_first_block_sequence_item
expect_first_block_sequence_item: ->
@expect_block_sequence_item true
expect_block_sequence_item: (first = false) ->
if not first and @event instanceof events.SequenceEndEvent
@indent = @indents.pop()
@state = @states.pop()
else
@write_indent()
@write_indicator '-', true, indentation: true
@states.push @expect_block_sequence_item
@expect_node sequence: true
# Block mapping states
expect_block_mapping: ->
@increase_indent()
@state = @expect_first_block_mapping_key
expect_first_block_mapping_key: ->
@expect_block_mapping_key true
expect_block_mapping_key: (first = false) ->
if not first and @event instanceof events.MappingEndEvent
@indent = @indents.pop()
@state = @states.pop()
else
@write_indent()
if @check_simple_key()
@states.push @expect_block_mapping_simple_value
@expect_node mapping: true, simple_key: true
else
@write_indicator '?', true, indentation: true
@states.push @expect_block_mapping_value
@expect_node mapping: true
expect_block_mapping_simple_value: ->
@write_indicator ':', false
@states.push @expect_block_mapping_key
@expect_node mapping: true
expect_block_mapping_value: ->
@write_indent()
@write_indicator ':', true, indentation: true
@states.push @expect_block_mapping_key
@expect_node mapping: true
# Checkers
check_empty_document: ->
return false if @event not instanceof events.DocumentStartEvent or @events.length is 0
event = @events[0]
event instanceof events.ScalarEvent and not event.anchor? and not event.tag? and event.implicit and event.value is ''
check_empty_sequence: ->
@event instanceof events.SequenceStartEvent and @events[0] instanceof events.SequenceEndEvent
check_empty_mapping: ->
@event instanceof events.MappingStartEvent and @events[0] instanceof events.MappingEndEvent
check_simple_key: ->
length = 0
if @event instanceof events.NodeEvent and @event.anchor?
@prepared_anchor ?= @prepare_anchor @event.anchor
length += @prepared_anchor.length
if @event.tag? and (@event instanceof events.ScalarEvent or @event instanceof events.CollectionStartEvent)
@prepared_tag ?= @prepare_tag @event.tag
length += @prepared_tag.length
if @event instanceof events.ScalarEvent
@analysis ?= @analyze_scalar @event.value
length += @analysis.scalar.length
length < 128 and (
@event instanceof events.AliasEvent or
(@event instanceof events.ScalarEvent and not @analysis.empty and not @analysis.multiline) or
@check_empty_sequence() or @check_empty_mapping()
)
# Anchor, Tag and Scalar processors
process_anchor: (indicator) ->
unless @event.anchor?
@prepared_anchor = null
return
@prepared_anchor ?= @prepare_anchor @event.anchor
@write_indicator "#{indicator}#{@prepared_anchor}", true if @prepared_anchor
@prepared_anchor = null
process_tag: ->
tag = @event.tag
if @event instanceof events.ScalarEvent
@style ?= @choose_scalar_style()
if (not @canonical or not tag?) and ((@style is '' and @event.implicit[0]) or \
(@style isnt '' and @event.implicit[1]))
@prepared_tag = null
return
if @event.implicit[0] and not tag?
tag = '!'
@prepared_tag = null
else if (not @canonical or not tag?) and @event.implicit
@prepared_tag = null
return
@error 'tag is not specified' if not tag?
@prepared_tag ?= @prepare_tag tag
@write_indicator @prepared_tag, true
@prepared_tag = null
process_scalar: ->
@analysis ?= @analyze_scalar @event.value
@style ?= @choose_scalar_style()
split = not @simple_key_context
switch @style
when '"' then @write_double_quoted @analysis.scalar, split
when "'" then @write_single_quoted @analysis.scalar, split
when '>' then @write_folded @analysis.scalar
when '|' then @write_literal @analysis.scalar
else @write_plain @analysis.scalar, split
@analysis = null
@style = null
choose_scalar_style: ->
@analysis ?= @analyze_scalar @event.value
return '"' if @event.style is '"' or @canonical
return '' if not @event.style and @event.implicit[0] \
and not (@simple_key_context and (@analysis.empty or @analysis.multiline)) \
and ((@flow_level and @analysis.allow_flow_plain) \
or (not @flow_level and @analysis.allow_block_plain))
return @event.style if @event.style and @event.style in '|>' and not @flow_level \
and not @simple_key_context and @analysis.allow_block
return "'" if (not @event.style or @event.style is "'") and @analysis.allow_single_quoted \
and not (@simple_key_context and @analysis.multiline)
return '"'
# Analyzers
prepare_version: ([ major, minor ]) ->
version = "#{major}.#{minor}"
if major is 1 then version else @error 'unsupported YAML version', version
prepare_tag_handle: (handle) ->
unless handle
@error 'tag handle must not be empty'
if handle[0] isnt '!' or handle[-1..] isnt '!'
@error "tag handle must start and end with '!':", handle
for char in handle[1...-1]
unless '0' <= char <= '9' or 'A' <= char <= 'Z' or 'a' <= char <= 'z' or char in '-_'
@error "invalid character '#{char}' in the tag handle:", handle
handle
prepare_tag_prefix: (prefix) ->
@error 'tag prefix must not be empty' unless prefix
chunks = []
start = 0
end = +(prefix[0] is '!')
while end < prefix.length
char = prefix[end]
if '0' <= char <= '9' or 'A' <= char <= 'Z' or 'a' <= char <= 'z' or char in '-;/?!:@&=+$,_.~*\'()[]'
end++
else
chunks.push prefix[start...end] if start < end
start = end = end + 1
chunks.push char
chunks.push prefix[start...end] if start < end
chunks.join ''
prepare_tag: (tag) ->
@error 'tag must not be empty' unless tag
return tag if tag is '!'
handle = null
suffix = tag
for prefix in (k for own k of @tag_prefixes).sort()
if tag.indexOf(prefix) is 0 and (prefix is '!' or prefix.length < tag.length)
handle = @tag_prefixes[prefix]
suffix = tag[prefix.length..]
chunks = []
start = end = 0
while end < suffix.length
char = suffix[end]
if '0' <= char <= '9' or 'A' <= char <= 'Z' or 'a' <= char <= 'z' or \
char in '-;/?!:@&=+$,_.~*\'()[]' or (char is '!' and handle isnt '!')
end++
else
chunks.push suffix[start...end] if start < end
start = end = end + 1
chunks.push char
chunks.push suffix[start...end] if start < end
suffix_text = chunks.join ''
if handle then "#{handle}#{suffix_text}" else "!<#{suffix_text}>"
prepare_anchor: (anchor) ->
@error 'anchor must not be empty' unless anchor
for char in anchor
unless '0' <= char <= '9' or 'A' <= char <= 'Z' or 'a' <= char <= 'z' or char in '-_'
@error "invalid character '#{char}' in the anchor:", anchor
anchor
analyze_scalar: (scalar) ->
# Empty scalar is a special case.
unless scalar
new ScalarAnalysis(scalar, true, false, false, true, true, true, false)
# Indicators and special characters.
block_indicators = false
flow_indicators = false
line_breaks = false
special_characters = false
unicode_characters = false
# Important whitespace combinations
leading_space = false
leading_break = false
trailing_space = false
trailing_break = false
break_space = false
space_break = false
# Check document indicators.
if scalar.indexOf('---') is 0 or scalar.indexOf('...') is 0
block_indicators = true
flow_indicators = true
# First character or preceded by a whitespace.
preceded_by_whitespace = true
# Last character or followed by a whitespace.
followed_by_whitespace = scalar.length is 1 or scalar[1] in '\0 \t\r\n\x85\u2028\u2029'
# The previous character is a space.
previous_space = false
# The previous character is a break
previous_break = false
index = 0
for char, index in scalar
# Check for indicators.
if index is 0
# Leading indicators are special characters.
if char in '#,[]{}&*!|>\'"%@`' or (char is '-' and followed_by_whitespace)
flow_indicators = true
block_indicators = true
else if char in '?:'
flow_indicators = true
block_indicators = true if followed_by_whitespace
else
# Some indicators cannot appear within a scalar as well.
if char in ',?[]{}'
flow_indicators = true
else if char is ':'
flow_indicators = true
block_indicators = true if followed_by_whitespace
else if char is '#' and preceded_by_whitespace
flow_indicators = true
block_indicators = true
# Check for line breaks, special, and unicode characters.
if char in '\n\x85\u2028\u2029'
line_breaks = true
unless char is '\n' or '\x20' <= char <= '\x7e'
if char isnt '\uFEFF' and (char is '\x85' or '\xA0' <= char <= '\uD7FF' or '\uE000' <= char <= '\uFFFD')
unicode_characters = true
special_characters = true unless @allow_unicode
else
special_characters = true
# Detect important whitespace combinations.
if char is ' '
leading_space = true if index is 0
trailing_space = true if index == scalar.length - 1
break_space = true if previous_break
previous_break = false
previous_space = true
else if char in '\n\x85\u2028\u2029'
leading_break = true if index is 0
trailing_break = true if index == scalar.length - 1
space_break = true if previous_space
previous_break = true
previous_space = false
else
previous_break = false
previous_space = false
# Prepare for the next character.
preceded_by_whitespace = char in C_WHITESPACE
followed_by_whitespace = index + 2 >= scalar.length or scalar[index + 2] in C_WHITESPACE
# 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 whitespaces are bad for plain scalars.
if leading_space or leading_break or trailing_space or trailing_break
allow_flow_plain = allow_block_plain = false
# We do not permit trailing spaces for block scalars.
if trailing_space
allow_block = false
# Spaces at the beginning of a new line are only acceptable for block scalars.
if break_space
allow_flow_plain = allow_block_plain = allow_single_quoted = false
# Spaces followed by breaks, as well as special character are only allowed for double quoted
# scalars.
if space_break or special_characters
allow_flow_plain = allow_block_plain = allow_single_quoted = allow_block = false
# Although the plain scalar writer supports breaks, we never emit multiline plain scalars.
if line_breaks
allow_flow_plain = allow_block_plain = false
# Flow indicators are forbidden for flow plain scalars.
if flow_indicators
allow_flow_plain = false
# Block indicators are forbidden for block plain scalars.
if block_indicators
allow_block_plain = false
new ScalarAnalysis scalar, false, line_breaks, allow_flow_plain, allow_block_plain,
allow_single_quoted, allow_double_quoted, allow_block
# Writers
###
Write BOM if needed.
###
write_stream_start: ->
if @encoding and @encoding.indexOf('utf-16') is 0
@stream.write '\uFEFF', @encoding
write_stream_end: ->
@flush_stream()
write_indicator: (indicator, need_whitespace, options = {}) ->
data = if @whitespace or not need_whitespace
indicator
else
' ' + indicator
@whitespace = !!options.whitespace
@indentation and= !!options.indentation
@column += data.length
@open_ended = false
@stream.write data, @encoding
write_indent: ->
indent = @indent ? 0
if not @indentation or @column > indent or (@column == indent and not @whitespace)
@write_line_break()
if @column < indent
@whitespace = true
data = new Array(indent - @column + 1).join ' '
@column = indent
@stream.write data, @encoding
write_line_break: (data) ->
@whitespace = true
@indentation = true
@line += 1
@column = 0
@stream.write data ? @best_line_break, @encoding
write_version_directive: (version_text) ->
@stream.write "%YAML #{version_text}", @encoding
@write_line_break()
write_tag_directive: (handle_text, prefix_text) ->
@stream.write "%TAG #{handle_text} #{prefix_text}", @encoding
@write_line_break()
write_single_quoted: (text, split = true) ->
@write_indicator "'", true
spaces = false
breaks = false
start = end = 0
while end <= text.length
char = text[end]
if spaces
if not char? or char isnt ' '
if start + 1 == end and @column > @best_width and split and start isnt 0 and end != text.length
@write_indent()
else
data = text[start...end]
@column += data.length
@stream.write data, @encoding
start = end
else if breaks
if not char? or char not in '\n\x85\u2028\u2029'
@write_line_break() if text[start] is '\n'
for br in text[start...end]
if br is '\n'
@write_line_break()
else
@write_line_break br
@write_indent()
start = end
else if (not char? or char in ' \n\x85\u2028\u2029' or char is "'") and start < end
data = text[start...end]
@column += data.length
@stream.write data, @encoding
start = end
if char is "'"
@column += 2
@stream.write "''", @encoding
start = end + 1
if char?
spaces = char is ' '
breaks = char in '\n\x85\u2028\u2029'
end++
@write_indicator "'", false
write_double_quoted: (text, split = true) ->
@write_indicator '"', true
start = end = 0
while end <= text.length
char = text[end]
if not char? or char in '"\\\x85\u2028\u2029\uFEFF' or \
not ('\x20' <= char <= '\x7E' or (@allow_unicode and \
('\xA0' <= char <= '\uD7FF' or '\uE000' <= char <= '\uFFFD')))
if start < end
data = text[start...end]
@column += data.length
@stream.write data, @encoding
start = end
if char?
data = if char of ESCAPE_REPLACEMENTS
'\\' + ESCAPE_REPLACEMENTS[char]
else if char <= '\xFF'
"\\x#{util.pad_left util.to_hex(char), '0', 2}"
else if char <= '\uFFFF'
"\\u#{util.pad_left util.to_hex(char), '0', 4}"
else
"\\U#{util.pad_left util.to_hex(char), '0', 16}"
@column += data.length
@stream.write data, @encoding
start = end + 1
if split and 0 < end < text.length - 1 and (char is ' ' or start >= end) and \
@column + (end - start) > @best_width
data = "#{text[start...end]}\\"
start = end if start < end
@column += data.length
@stream.write data, @encoding
@write_indent()
@whitespace = false
@indentation = false
if text[start] is ' '
data = '\\'
@column += data.length
@stream.write data, @encoding
end++
@write_indicator '"', false
write_folded: (text) ->
hints = @determine_block_hints text
@write_indicator ">#{hints}", true
@open_ended = true if hints[-1..] is '+'
@write_line_break()
leading_space = true
breaks = true
spaces = false
start = end = 0
while end <= text.length
char = text[end]
if breaks
if not char? or char not in '\n\x85\u2028\u2029'
if not leading_space and char? and char isnt ' ' and text[start] is '\n'
@write_line_break()
leading_space = char is ' '
for br in text[start...end]
if br is '\n'
@write_line_break()
else
@write_line_break br
@write_indent() if char?
start = end
else if spaces
if char isnt ' '
if start + 1 == end and @column > @best_width
@write_indent()
else
data = text[start...end]
@column += data.length
@stream.write data, @encoding
start = end
else if not char? or char in ' \n\x85\u2028\u2029'
data = text[start...end]
@column += data.length
@stream.write data, @encoding
@write_line_break() if not char?
start = end
if char?
breaks = char in '\n\x85\u2028\u2029'
spaces = char is ' '
end++
write_literal: (text) ->
hints = @determine_block_hints text
@write_indicator "|#{hints}", true
@open_ended = true if hints[-1..] is '+'
@write_line_break()
breaks = true
start = end = 0
while end <= text.length
char = text[end]
if breaks
if not char? or char not in '\n\x85\u2028\u2029'
for br in text[start...end]
if br is '\n'
@write_line_break()
else
@write_line_break br
@write_indent() if char?
start = end
else
if not char? or char in '\n\x85\u2028\u2029'
data = text[start...end]
@stream.write data, @encoding
@write_line_break() if not char?
start = end
breaks = char in '\n\x85\u2028\u2029' if char?
end++
write_plain: (text, split = true) ->
unless text then return
@open_ended = true if @root_context
unless @whitespace
data = ' '
@column += data.length
@stream.write data, @encoding
@whitespace = false
@indentation = false
spaces = false
breaks = false
start = end = 0
while end <= text.length
char = text[end]
if spaces
if char isnt ' '
if start + 1 == end and @column > @best_width and split
@write_indent()
@whitespace = false
@indentation = false
else
data = text[start...end]
@column += data.length
@stream.write data, @encoding
start = end
else if breaks
if char not in '\n\x85\u2028\u2029'
@write_line_break() if text[start] is '\n'
for br in text[start...end]
if br is '\n'
@write_line_break()
else
@write_line_break br
@write_indent()
@whitespace = false
@indentation = false
start = end
else
if not char? or char in ' \n\x85\u2028\u2029'
data = text[start...end]
@column += data.length
@stream.write data, @encoding
start = end
if char?
spaces = char is ' '
breaks = char in '\n\x85\u2028\u2029'
end++
determine_block_hints: (text) ->
hints = ''
[ first, ..., penultimate, last ] = text
if first in ' \n\x85\u2028\u2029'
hints += @best_indent
if last not in '\n\x85\u2028\u2029'
hints += '-'
else if text.length is 1 or penultimate in '\n\x85\u2028\u2029'
hints += '+'
hints
flush_stream: ->
@stream.flush?()
###
Helper for common error pattern.
###
error: (message, context) ->
context = context?.constructor?.name ? util.inspect context if context
throw new exports.EmitterError "#{message}#{if context then " #{context}" else ''}"
class ScalarAnalysis
constructor: (@scalar, @empty, @multiline, @allow_flow_plain, @allow_block_plain,
@allow_single_quoted, @allow_double_quoted, @allow_block) ->