Skip to content

Commit

Permalink
Merge pull request #265 from vikstrous2/single-quote
Browse files Browse the repository at this point in the history
add UseSingleQuote option
  • Loading branch information
goccy committed Nov 18, 2021
2 parents ecc53fd + e8c5eef commit 562004d
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 1 deletion.
7 changes: 6 additions & 1 deletion encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Encoder struct {
opts []EncodeOption
indent int
indentSequence bool
singleQuote bool
isFlowStyle bool
isJSONStyle bool
useJSONMarshaler bool
Expand Down Expand Up @@ -412,7 +413,11 @@ func (e *Encoder) isNeedQuoted(v string) bool {

func (e *Encoder) encodeString(v string, column int) ast.Node {
if e.isNeedQuoted(v) {
v = strconv.Quote(v)
if e.singleQuote {
v = quoteWith(v, '\'')
} else {
v = strconv.Quote(v)
}
}
return ast.String(token.New(v, v, e.pos(column)))
}
Expand Down
15 changes: 15 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,21 @@ func TestEncoder(t *testing.T) {
map[string]time.Duration{"v": 30 * time.Second},
nil,
},
// Quote style
{
`v: '\'a\'b'` + "\n",
map[string]string{"v": `'a'b`},
[]yaml.EncodeOption{
yaml.UseSingleQuote(true),
},
},
{
`v: "'a'b"` + "\n",
map[string]string{"v": `'a'b`},
[]yaml.EncodeOption{
yaml.UseSingleQuote(false),
},
},
}
for _, test := range tests {
var buf bytes.Buffer
Expand Down
8 changes: 8 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ func IndentSequence(indent bool) EncodeOption {
}
}

// UseSingleQuote determines if single or double quotes should be preferred for strings.
func UseSingleQuote(sq bool) EncodeOption {
return func(e *Encoder) error {
e.singleQuote = sq
return nil
}
}

// Flow encoding by flow style
func Flow(isFlowStyle bool) EncodeOption {
return func(e *Encoder) error {
Expand Down
103 changes: 103 additions & 0 deletions stdlib_quote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copied and trimmed down from https://github.com/golang/go/blob/e3769299cd3484e018e0e2a6e1b95c2b18ce4f41/src/strconv/quote.go
// We want to use the standard library's private "quoteWith" function rather than write our own so that we get robust unicode support.
// Every private function called by quoteWith was copied.
// There are 2 modifications to simplify the code:
// 1. The unicode.IsPrint function was substituted for the custom implementation of IsPrint
// 2. All code paths reachable only when ASCIIonly or grphicOnly are set to true were removed.

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package yaml

import (
"unicode"
"unicode/utf8"
)

const (
lowerhex = "0123456789abcdef"
)

func quoteWith(s string, quote byte) string {
return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote))
}

func appendQuotedWith(buf []byte, s string, quote byte) []byte {
// Often called with big strings, so preallocate. If there's quoting,
// this is conservative but still helps a lot.
if cap(buf)-len(buf) < len(s) {
nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1)
copy(nBuf, buf)
buf = nBuf
}
buf = append(buf, quote)
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
r, width = utf8.DecodeRuneInString(s)
}
if width == 1 && r == utf8.RuneError {
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
continue
}
buf = appendEscapedRune(buf, r, quote)
}
buf = append(buf, quote)
return buf
}

func appendEscapedRune(buf []byte, r rune, quote byte) []byte {
var runeTmp [utf8.UTFMax]byte
if r == rune(quote) || r == '\\' { // always backslashed
buf = append(buf, '\\')
buf = append(buf, byte(r))
return buf
}
if unicode.IsPrint(r) {
n := utf8.EncodeRune(runeTmp[:], r)
buf = append(buf, runeTmp[:n]...)
return buf
}
switch r {
case '\a':
buf = append(buf, `\a`...)
case '\b':
buf = append(buf, `\b`...)
case '\f':
buf = append(buf, `\f`...)
case '\n':
buf = append(buf, `\n`...)
case '\r':
buf = append(buf, `\r`...)
case '\t':
buf = append(buf, `\t`...)
case '\v':
buf = append(buf, `\v`...)
default:
switch {
case r < ' ':
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[byte(r)>>4])
buf = append(buf, lowerhex[byte(r)&0xF])
case r > utf8.MaxRune:
r = 0xFFFD
fallthrough
case r < 0x10000:
buf = append(buf, `\u`...)
for s := 12; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
default:
buf = append(buf, `\U`...)
for s := 28; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
}
}
return buf
}

0 comments on commit 562004d

Please sign in to comment.