Skip to content

Commit

Permalink
Merge pull request #82 from k1LoW/multi-line-string
Browse files Browse the repository at this point in the history
Add support for multi-line string encoding
  • Loading branch information
goccy authored Feb 17, 2020
2 parents fee8ac5 + 64a69e0 commit 90ec442
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 2 deletions.
17 changes: 16 additions & 1 deletion ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,14 +420,29 @@ func (n *StringNode) GetValue() interface{} {
return n.Value
}

// String string value to text with quote if required
// String string value to text with quote or literal header if required
func (n *StringNode) String() string {
switch n.Token.Type {
case token.SingleQuoteType:
return fmt.Sprintf(`'%s'`, n.Value)
case token.DoubleQuoteType:
return fmt.Sprintf(`"%s"`, n.Value)
}

lbc := token.DetectLineBreakCharacter(n.Value)
if strings.Contains(n.Value, lbc) {
// This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same.
// It works mostly, but inconsistencies occur if line break characters are mixed.
header := token.LiteralBlockHeader(n.Value)
space := strings.Repeat(" ", n.Token.Position.Column-1)
values := []string{}
for _, v := range strings.Split(n.Value, lbc) {
values = append(values, fmt.Sprintf("%s %s", space, v))
}
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
return fmt.Sprintf("%s%s%s", header, lbc, block)
}

return n.Value
}

Expand Down
27 changes: 26 additions & 1 deletion encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,30 @@ func TestEncoder(t *testing.T) {
"hello: world\n",
map[string]string{"hello": "world"},
},
{
"hello: |\n hello\n world\n",
map[string]string{"hello": "hello\nworld\n"},
},
{
"hello: |-\n hello\n world\n",
map[string]string{"hello": "hello\nworld"},
},
{
"hello: |+\n hello\n world\n\n",
map[string]string{"hello": "hello\nworld\n\n"},
},
{
"hello:\n hello: |\n hello\n world\n",
map[string]map[string]string{"hello": {"hello": "hello\nworld\n"}},
},
{
"hello: |\r hello\r world\n",
map[string]string{"hello": "hello\rworld\r"},
},
{
"hello: |\r\n hello\r\n world\n",
map[string]string{"hello": "hello\r\nworld\r\n"},
},
{
"v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
map[string]interface{}{
Expand Down Expand Up @@ -774,7 +798,8 @@ type FastMarshaler struct {
type TextMarshaler int64
type TextMarshalerContainer struct {
Field TextMarshaler `yaml:"field"`
}
}

func (v SlowMarshaler) MarshalYAML() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("tags:\n")
Expand Down
31 changes: 31 additions & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,22 @@ func IsNeedQuoted(value string) bool {
return false
}

// LiteralBlockHeader detect literal block scalar header
func LiteralBlockHeader(value string) string {
lbc := DetectLineBreakCharacter(value)

switch {
case !strings.Contains(value, lbc):
return ""
case strings.HasSuffix(value, fmt.Sprintf("%s%s", lbc, lbc)):
return "|+"
case strings.HasSuffix(value, lbc):
return "|"
default:
return "|-"
}
}

// New create reserved keyword token or number token and other string token
func New(value string, org string, pos *Position) *Token {
fn := reservedKeywordMap[value]
Expand Down Expand Up @@ -957,3 +973,18 @@ func DocumentEnd(pos *Position) *Token {
Position: pos,
}
}

// DetectLineBreakCharacter detect line break character in only one inside scalar content scope.
func DetectLineBreakCharacter(src string) string {
nc := strings.Count(src, "\n")
rc := strings.Count(src, "\r")
rnc := strings.Count(src, "\r\n")
switch {
case nc == rnc && rc == rnc:
return "\r\n"
case rc > nc:
return "\r"
default:
return "\n"
}
}

0 comments on commit 90ec442

Please sign in to comment.