diff --git a/goyaml.v3/emitterc.go b/goyaml.v3/emitterc.go index 0f47c9c..f560b10 100644 --- a/goyaml.v3/emitterc.go +++ b/goyaml.v3/emitterc.go @@ -226,7 +226,7 @@ func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_ } // Increase the indentation level. -func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool, compact_seq bool) bool { emitter.indents = append(emitter.indents, emitter.indent) if emitter.indent < 0 { if flow { @@ -241,7 +241,10 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool emitter.indent += 2 } else { // Everything else aligns to the chosen indentation. - emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) + emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent) + } + if compact_seq { + emitter.indent = emitter.indent - 2 } } return true @@ -488,7 +491,7 @@ func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_eve if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -534,7 +537,7 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { return false } - if !yaml_emitter_increase_indent(emitter, true, false) { + if !yaml_emitter_increase_indent(emitter, true, false, false) { return false } emitter.flow_level++ @@ -557,7 +560,7 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -602,7 +605,7 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e return false } } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -617,7 +620,7 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { return false } - if !yaml_emitter_increase_indent(emitter, true, false) { + if !yaml_emitter_increase_indent(emitter, true, false, false) { return false } emitter.flow_level++ @@ -643,7 +646,7 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -716,7 +719,7 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e return false } } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -728,7 +731,9 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e // Expect a block item node. func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { - if !yaml_emitter_increase_indent(emitter, false, false) { + seq := emitter.mapping_context && (emitter.column == 0 || !emitter.indention) && + emitter.compact_sequence_indent + if !yaml_emitter_increase_indent(emitter, false, false, seq) { return false } } @@ -752,7 +757,7 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_ if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -764,7 +769,7 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_ // Expect a block key node. func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { - if !yaml_emitter_increase_indent(emitter, false, false) { + if !yaml_emitter_increase_indent(emitter, false, false, false) { return false } } @@ -828,7 +833,7 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_ } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { // An indented block follows, so write the comment right now. emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment @@ -838,7 +843,7 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_ if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, false) { return false } if !yaml_emitter_process_foot_comment(emitter) { @@ -896,7 +901,7 @@ func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool if !yaml_emitter_process_tag(emitter) { return false } - if !yaml_emitter_increase_indent(emitter, true, false) { + if !yaml_emitter_increase_indent(emitter, true, false, false) { return false } if !yaml_emitter_process_scalar(emitter) { @@ -1144,8 +1149,11 @@ func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { } // Write an line comment. -func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { +func yaml_emitter_process_line_comment(emitter *yaml_emitter_t, linebreak bool) bool { if len(emitter.line_comment) == 0 { + if linebreak && !put_break(emitter) { + return false + } return true } if !emitter.whitespace { @@ -1894,7 +1902,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, true) { return false } //emitter.indention = true @@ -1931,7 +1939,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } - if !yaml_emitter_process_line_comment(emitter) { + if !yaml_emitter_process_line_comment(emitter, true) { return false } diff --git a/goyaml.v3/encode_test.go b/goyaml.v3/encode_test.go index aa08acd..9394c4e 100644 --- a/goyaml.v3/encode_test.go +++ b/goyaml.v3/encode_test.go @@ -19,6 +19,7 @@ import ( "bytes" "fmt" "math" + yaml "sigs.k8s.io/yaml/goyaml.v3" "strconv" "strings" "time" @@ -27,117 +28,149 @@ import ( "os" . "gopkg.in/check.v1" - "sigs.k8s.io/yaml/goyaml.v3" ) var marshalIntTest = 123 var marshalTests = []struct { - value interface{} - data string + value interface{} + data string + compact string }{ { nil, "null\n", + "null\n", }, { (*marshalerType)(nil), "null\n", + "null\n", }, { &struct{}{}, "{}\n", + "{}\n", }, { map[string]string{"v": "hi"}, "v: hi\n", + "v: hi\n", }, { map[string]interface{}{"v": "hi"}, "v: hi\n", + "v: hi\n", }, { map[string]string{"v": "true"}, "v: \"true\"\n", + "v: \"true\"\n", }, { map[string]string{"v": "false"}, "v: \"false\"\n", + "v: \"false\"\n", }, { map[string]interface{}{"v": true}, "v: true\n", + "v: true\n", }, { map[string]interface{}{"v": false}, "v: false\n", + "v: false\n", }, { map[string]interface{}{"v": 10}, "v: 10\n", + "v: 10\n", }, { map[string]interface{}{"v": -10}, "v: -10\n", + "v: -10\n", }, { map[string]uint{"v": 42}, "v: 42\n", + "v: 42\n", }, { map[string]interface{}{"v": int64(4294967296)}, "v: 4294967296\n", + "v: 4294967296\n", }, { map[string]int64{"v": int64(4294967296)}, "v: 4294967296\n", + "v: 4294967296\n", }, { map[string]uint64{"v": 4294967296}, "v: 4294967296\n", + "v: 4294967296\n", }, { map[string]interface{}{"v": "10"}, "v: \"10\"\n", + "v: \"10\"\n", }, { map[string]interface{}{"v": 0.1}, "v: 0.1\n", + "v: 0.1\n", }, { map[string]interface{}{"v": float64(0.1)}, "v: 0.1\n", + "v: 0.1\n", }, { map[string]interface{}{"v": float32(0.99)}, "v: 0.99\n", + "v: 0.99\n", }, { map[string]interface{}{"v": -0.1}, "v: -0.1\n", + "v: -0.1\n", }, { map[string]interface{}{"v": math.Inf(+1)}, "v: .inf\n", + "v: .inf\n", }, { map[string]interface{}{"v": math.Inf(-1)}, "v: -.inf\n", + "v: -.inf\n", }, { map[string]interface{}{"v": math.NaN()}, "v: .nan\n", + "v: .nan\n", }, { map[string]interface{}{"v": nil}, "v: null\n", + "v: null\n", }, { map[string]interface{}{"v": ""}, "v: \"\"\n", + "v: \"\"\n", }, { - map[string][]string{"v": []string{"A", "B"}}, + map[string][]string{"v": {"A", "B"}}, "v:\n - A\n - B\n", + "v:\n - A\n - B\n", }, { - map[string][]string{"v": []string{"A", "B\nC"}}, + map[string][]string{"v": {"A", "B\nC"}}, "v:\n - A\n - |-\n B\n C\n", + "v:\n - A\n - |-\n B\n C\n", }, { - map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, + map[string][]interface{}{"v": {"A", 1, map[string][]int{"B": {2, 3}}}}, "v:\n - A\n - 1\n - B:\n - 2\n - 3\n", + "v:\n - A\n - 1\n - B:\n - 2\n - 3\n", }, { map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, "a:\n b: c\n", + "a:\n b: c\n", }, { map[string]interface{}{"a": "-"}, "a: '-'\n", + "a: '-'\n", }, // Simple values. { &marshalIntTest, "123\n", + "123\n", }, // Structures { &struct{ Hello string }{"world"}, "hello: world\n", + "hello: world\n", }, { &struct { A struct { @@ -145,6 +178,7 @@ var marshalTests = []struct { } }{struct{ B string }{"c"}}, "a:\n b: c\n", + "a:\n b: c\n", }, { &struct { A *struct { @@ -152,6 +186,7 @@ var marshalTests = []struct { } }{&struct{ B string }{"c"}}, "a:\n b: c\n", + "a:\n b: c\n", }, { &struct { A *struct { @@ -159,29 +194,37 @@ var marshalTests = []struct { } }{}, "a: null\n", + "a: null\n", }, { &struct{ A int }{1}, "a: 1\n", + "a: 1\n", }, { &struct{ A []int }{[]int{1, 2}}, "a:\n - 1\n - 2\n", + "a:\n - 1\n - 2\n", }, { &struct{ A [2]int }{[2]int{1, 2}}, "a:\n - 1\n - 2\n", + "a:\n - 1\n - 2\n", }, { &struct { B int "a" }{1}, "a: 1\n", + "a: 1\n", }, { &struct{ A bool }{true}, "a: true\n", + "a: true\n", }, { &struct{ A string }{"true"}, "a: \"true\"\n", + "a: \"true\"\n", }, { &struct{ A string }{"off"}, "a: \"off\"\n", + "a: \"off\"\n", }, // Conditional flag @@ -191,43 +234,51 @@ var marshalTests = []struct { B int "b,omitempty" }{1, 0}, "a: 1\n", + "a: 1\n", }, { &struct { A int "a,omitempty" B int "b,omitempty" }{0, 0}, "{}\n", + "{}\n", }, { &struct { A *struct{ X, y int } "a,omitempty,flow" }{&struct{ X, y int }{1, 2}}, "a: {x: 1}\n", + "a: {x: 1}\n", }, { &struct { A *struct{ X, y int } "a,omitempty,flow" }{nil}, "{}\n", + "{}\n", }, { &struct { A *struct{ X, y int } "a,omitempty,flow" }{&struct{ X, y int }{}}, "a: {x: 0}\n", + "a: {x: 0}\n", }, { &struct { A struct{ X, y int } "a,omitempty,flow" }{struct{ X, y int }{1, 2}}, "a: {x: 1}\n", + "a: {x: 1}\n", }, { &struct { A struct{ X, y int } "a,omitempty,flow" }{struct{ X, y int }{0, 1}}, "{}\n", + "{}\n", }, { &struct { A float64 "a,omitempty" B float64 "b,omitempty" }{1, 0}, "a: 1\n", + "a: 1\n", }, { &struct { @@ -240,6 +291,7 @@ var marshalTests = []struct { T4: newTime(time.Date(2098, 1, 9, 10, 40, 47, 0, time.UTC)), }, "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", + "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", }, // Nil interface that implements Marshaler. { @@ -247,6 +299,7 @@ var marshalTests = []struct { "a": nil, }, "a: null\n", + "a: null\n", }, // Flow flag @@ -255,11 +308,13 @@ var marshalTests = []struct { A []int "a,flow" }{[]int{1, 2}}, "a: [1, 2]\n", + "a: [1, 2]\n", }, { &struct { A map[string]string "a,flow" }{map[string]string{"b": "c", "d": "e"}}, "a: {b: c, d: e}\n", + "a: {b: c, d: e}\n", }, { &struct { A struct { @@ -267,11 +322,13 @@ var marshalTests = []struct { } "a,flow" }{struct{ B, D string }{"c", "e"}}, "a: {b: c, d: e}\n", + "a: {b: c, d: e}\n", }, { &struct { A string "a,flow" }{"b\nc"}, "a: \"b\\nc\"\n", + "a: \"b\\nc\"\n", }, // Unexported field @@ -281,6 +338,7 @@ var marshalTests = []struct { A int }{0, 1}, "a: 1\n", + "a: 1\n", }, // Ignored field @@ -290,6 +348,7 @@ var marshalTests = []struct { B int "-" }{1, 2}, "a: 1\n", + "a: 1\n", }, // Struct inlining @@ -299,6 +358,7 @@ var marshalTests = []struct { C inlineB `yaml:",inline"` }{1, inlineB{2, inlineC{3}}}, "a: 1\nb: 2\nc: 3\n", + "a: 1\nb: 2\nc: 3\n", }, // Struct inlining as a pointer { @@ -307,18 +367,21 @@ var marshalTests = []struct { C *inlineB `yaml:",inline"` }{1, &inlineB{2, inlineC{3}}}, "a: 1\nb: 2\nc: 3\n", + "a: 1\nb: 2\nc: 3\n", }, { &struct { A int C *inlineB `yaml:",inline"` }{1, nil}, "a: 1\n", + "a: 1\n", }, { &struct { A int D *inlineD `yaml:",inline"` }{1, &inlineD{&inlineC{3}, 4}}, "a: 1\nc: 3\nd: 4\n", + "a: 1\nc: 3\nd: 4\n", }, // Map inlining @@ -328,18 +391,21 @@ var marshalTests = []struct { C map[string]int `yaml:",inline"` }{1, map[string]int{"b": 2, "c": 3}}, "a: 1\nb: 2\nc: 3\n", + "a: 1\nb: 2\nc: 3\n", }, // Duration { map[string]time.Duration{"a": 3 * time.Second}, "a: 3s\n", + "a: 3s\n", }, // Issue #24: bug in map merging logic. { map[string]string{"a": ""}, "a: \n", + "a: \n", }, // Issue #34: marshal unsupported base 60 floats quoted for compatibility @@ -347,92 +413,110 @@ var marshalTests = []struct { { map[string]string{"a": "1:1"}, "a: \"1:1\"\n", + "a: \"1:1\"\n", }, // Binary data. { map[string]string{"a": "\x00"}, "a: \"\\0\"\n", + "a: \"\\0\"\n", }, { map[string]string{"a": "\x80\x81\x82"}, "a: !!binary gIGC\n", + "a: !!binary gIGC\n", }, { map[string]string{"a": strings.Repeat("\x90", 54)}, "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", + "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", }, // Encode unicode as utf-8 rather than in escaped form. { map[string]string{"a": "你好"}, "a: 你好\n", + "a: 你好\n", }, // Support encoding.TextMarshaler. { map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, "a: 1.2.3.4\n", + "a: 1.2.3.4\n", }, // time.Time gets a timestamp tag. { map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, "a: 2015-02-24T18:19:39Z\n", + "a: 2015-02-24T18:19:39Z\n", }, { map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))}, "a: 2015-02-24T18:19:39Z\n", + "a: 2015-02-24T18:19:39Z\n", }, { // This is confirmed to be properly decoded in Python (libyaml) without a timestamp tag. map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))}, "a: 2015-02-24T18:19:39.123456789-03:00\n", + "a: 2015-02-24T18:19:39.123456789-03:00\n", }, // Ensure timestamp-like strings are quoted. { map[string]string{"a": "2015-02-24T18:19:39Z"}, "a: \"2015-02-24T18:19:39Z\"\n", + "a: \"2015-02-24T18:19:39Z\"\n", }, // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). { map[string]string{"a": "b: c"}, "a: 'b: c'\n", + "a: 'b: c'\n", }, // Containing hash mark ('#') in string should be quoted { map[string]string{"a": "Hello #comment"}, "a: 'Hello #comment'\n", + "a: 'Hello #comment'\n", }, { map[string]string{"a": "你好 #comment"}, "a: '你好 #comment'\n", + "a: '你好 #comment'\n", }, // Ensure MarshalYAML also gets called on the result of MarshalYAML itself. { &marshalerType{marshalerType{true}}, "true\n", + "true\n", }, { &marshalerType{&marshalerType{true}}, "true\n", + "true\n", }, // Check indentation of maps inside sequences inside maps. { map[string]interface{}{"a": map[string]interface{}{"b": []map[string]int{{"c": 1, "d": 2}}}}, "a:\n b:\n - c: 1\n d: 2\n", + "a:\n b:\n - c: 1\n d: 2\n", }, // Strings with tabs were disallowed as literals (issue #471). { map[string]string{"a": "\tB\n\tC\n"}, "a: |\n \tB\n \tC\n", + "a: |\n \tB\n \tC\n", }, // Ensure that strings do not wrap { map[string]string{"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 "}, "a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n", + "a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n", }, // yaml.Node @@ -448,6 +532,7 @@ var marshalTests = []struct { }, }, "value: 'foo'\n", + "value: 'foo'\n", }, { yaml.Node{ Kind: yaml.ScalarNode, @@ -456,6 +541,7 @@ var marshalTests = []struct { Style: yaml.SingleQuotedStyle, }, "'foo'\n", + "'foo'\n", }, // Enforced tagging with shorthand notation (issue #616). @@ -471,6 +557,7 @@ var marshalTests = []struct { }, }, "value: !!str foo\n", + "value: !!str foo\n", }, { &struct { Value yaml.Node @@ -482,6 +569,7 @@ var marshalTests = []struct { }, }, "value: !!map {}\n", + "value: !!map {}\n", }, { &struct { Value yaml.Node @@ -493,6 +581,7 @@ var marshalTests = []struct { }, }, "value: !!seq []\n", + "value: !!seq []\n", }, } @@ -507,6 +596,20 @@ func (s *S) TestMarshal(c *C) { } } +func (s *S) TestEncoderCompactIndents(c *C) { + for i, item := range marshalTests { + c.Logf("test %d. %q", i, item.data) + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.CompactSeqIndent() + err := enc.Encode(item.value) + c.Assert(err, Equals, nil) + err = enc.Close() + c.Assert(err, Equals, nil) + c.Assert(buf.String(), Equals, item.compact) + } +} + func (s *S) TestEncoderSingleDocument(c *C) { for i, item := range marshalTests { c.Logf("test %d. %q", i, item.data) @@ -656,6 +759,51 @@ func (s *S) TestSetIndent(c *C) { c.Assert(buf.String(), Equals, "a:\n b:\n c: d\n") } +func (s *S) TestCompactSeqIndentDefault(c *C) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.CompactSeqIndent() + err := enc.Encode(map[string]interface{}{"a": []string{"b", "c"}}) + c.Assert(err, Equals, nil) + err = enc.Close() + c.Assert(err, Equals, nil) + // The default indent is 4, so these sequence elements get 2 indents as before + c.Assert(buf.String(), Equals, `a: + - b + - c +`) +} + +func (s *S) TestCompactSequenceWithSetIndent(c *C) { + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.CompactSeqIndent() + enc.SetIndent(2) + err := enc.Encode(map[string]interface{}{"a": []string{"b", "c"}}) + c.Assert(err, Equals, nil) + err = enc.Close() + c.Assert(err, Equals, nil) + // The sequence indent is 2, so these sequence elements don't get indented at all + c.Assert(buf.String(), Equals, `a: +- b +- c +`) +} + +func (s *S) TestNewLinePreserved(c *C) { + obj := &marshalerValue{} + obj.Field.value = "a:\n b:\n c: d\n" + data, err := yaml.Marshal(obj) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "_: |\n a:\n b:\n c: d\n") + + obj.Field.value = "\na:\n b:\n c: d\n" + data, err = yaml.Marshal(obj) + c.Assert(err, IsNil) + // the newline at the start of the file should be preserved + c.Assert(string(data), Equals, "_: |4\n\n a:\n b:\n c: d\n") +} + func (s *S) TestSortedOutput(c *C) { order := []interface{}{ false, diff --git a/goyaml.v3/yaml.go b/goyaml.v3/yaml.go index 8cec6da..e218ffa 100644 --- a/goyaml.v3/yaml.go +++ b/goyaml.v3/yaml.go @@ -278,6 +278,16 @@ func (e *Encoder) SetIndent(spaces int) { e.encoder.indent = spaces } +// CompactSeqIndent makes it so that '- ' is considered part of the indentation. +func (e *Encoder) CompactSeqIndent() { + e.encoder.emitter.compact_sequence_indent = true +} + +// DefaultSeqIndent makes it so that '- ' is not considered part of the indentation. +func (e *Encoder) DefaultSeqIndent() { + e.encoder.emitter.compact_sequence_indent = false +} + // Close closes the encoder by writing any remaining data. // It does not write a stream terminating string "...". func (e *Encoder) Close() (err error) { @@ -363,7 +373,7 @@ const ( // Address yaml.Node // } // err := yaml.Unmarshal(data, &person) -// +// // Or by itself: // // var person Node @@ -373,7 +383,7 @@ type Node struct { // Kind defines whether the node is a document, a mapping, a sequence, // a scalar value, or an alias to another node. The specific data type of // scalar nodes may be obtained via the ShortTag and LongTag methods. - Kind Kind + Kind Kind // Style allows customizing the apperance of the node in the tree. Style Style @@ -421,7 +431,6 @@ func (n *Node) IsZero() bool { n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 } - // LongTag returns the long form of the tag that indicates the data type for // the node. If the Tag field isn't explicitly defined, one will be computed // based on the node properties. diff --git a/goyaml.v3/yamlh.go b/goyaml.v3/yamlh.go index 7c6d007..b204c49 100644 --- a/goyaml.v3/yamlh.go +++ b/goyaml.v3/yamlh.go @@ -639,7 +639,6 @@ type yaml_parser_t struct { } type yaml_comment_t struct { - scan_mark yaml_mark_t // Position where scanning for comments started token_mark yaml_mark_t // Position after which tokens will be associated with this comment start_mark yaml_mark_t // Position of '#' comment mark @@ -742,6 +741,8 @@ type yaml_emitter_t struct { indent int // The current indentation level. + compact_sequence_indent bool // Is '- ' is considered part of the indentation for sequence elements? + flow_level int // The current flow level. root_context bool // Is it the document root context?