Repository: goccy/go-yaml (go)

Issue:
Double escape backward slash
I looked into this a bit more. Marshalling output from goccy/go-yaml technically matches go/yaml output, see https://go.dev/play/p/d5RQ2vH33hS
```
goccy/go-yaml:
a:
- "\\.js"

gopkg.in/yaml.v2:
a:
- \.js
```

But it seems helmfile ignores escape in double-quoted string, and uses it as is, instead of doing unescape in process.

Pull request description:
Make UseSingleQuote compliant with the YAML spec
Fixes #643

This follows-up on #265 to fix how single-quoted strings are encoded.

That is, the only escape-sequence in a single-quoted string is now `''` which represents `'` literally.

Previously, it treated whatever starting with `\` as a escape sequence, which is incorrect in a single-quoted string.
That resulted in encoding `\.` as `\\.` for example, that broke my use-case.
It's not YAML-compliant anyway so I believe making go-yaml compliant fixes my use-case too.

---

Before submitting your PR, please confirm the following.

- [x] Describe the purpose for which you created this PR. 
  - See above
- [x] Create test code that corresponds to the modification
  - See the updated `enmcoded_test.go` 


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index 47c6dd2..3882850 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -51,6 +51,8 @@ const (
 	MappingValueType
 	// SequenceType type identifier for sequence node
 	SequenceType
+	// SequenceEntryType type identifier for sequence entry node
+	SequenceEntryType
 	// AnchorType type identifier for anchor node
 	AnchorType
 	// AliasType type identifier for alias node
@@ -98,6 +100,8 @@ func (t NodeType) String() string {
 		return "MappingValue"
 	case SequenceType:
 		return "Sequence"
+	case SequenceEntryType:
+		return "SequenceEntry"
 	case AnchorType:
 		return "Anchor"
 	case AliasType:
@@ -148,6 +152,8 @@ func (t NodeType) YAMLName() string {
 		return "value"
 	case SequenceType:
 		return "sequence"
+	case SequenceEntryType:
+		return "value"
 	case AnchorType:
 		return "anchor"
 	case AliasType:
@@ -1351,10 +1357,12 @@ func (n *MappingKeyNode) IsMergeKey() bool {
 // MappingValueNode type of mapping value
 type MappingValueNode struct {
 	*BaseNode
-	Start       *token.Token
-	Key         MapKeyNode
-	Value       Node
-	FootComment *CommentGroupNode
+	Start        *token.Token // delimiter token ':'.
+	CollectEntry *token.Token // collect entry token ','.
+	Key          MapKeyNode
+	Value        Node
+	FootComment  *CommentGroupNode
+	IsFlowStyle  bool
 }
 
 // Replace replace value node.
@@ -1391,6 +1399,7 @@ func (n *MappingValueNode) AddColumn(col int) {
 
 // SetIsFlowStyle set value to IsFlowStyle field recursively.
 func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) {
+	n.IsFlowStyle = isFlow
 	switch value := n.Value.(type) {
 	case *MappingNode:
 		value.SetIsFlowStyle(isFlow)
@@ -1429,7 +1438,7 @@ func (n *MappingValueNode) toString() string {
 	keyComment := n.Key.GetComment()
 	if _, ok := n.Value.(ScalarNode); ok {
 		return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
-	} else if keyIndentLevel < valueIndentLevel {
+	} else if keyIndentLevel < valueIndentLevel && !n.IsFlowStyle {
 		if keyComment != nil {
 			return fmt.Sprintf(
 				"%s%s: %s\n%s",
@@ -1522,6 +1531,7 @@ type SequenceNode struct {
 	IsFlowStyle       bool
 	Values            []Node
 	ValueHeadComments []*CommentGroupNode
+	Entries           []*SequenceEntryNode
 	FootComment       *CommentGroupNode
 }
 
@@ -1667,6 +1677,65 @@ func (n *SequenceNode) MarshalYAML() ([]byte, error) {
 	return []byte(n.String()), nil
 }
 
+// SequenceEntryNode is the sequence entry.
+type SequenceEntryNode struct {
+	*BaseNode
+	HeadComment *CommentGroupNode // head comment.
+	LineComment *CommentGroupNode // line comment e.g.) - # comment.
+	Start       *token.Token      // entry token.
+	Value       Node              // value node.
+}
+
+// String node to text
+func (n *SequenceEntryNode) String() string {
+	return "" // TODO
+}
+
+// GetToken returns token instance
+func (n *SequenceEntryNode) GetToken() *token.Token {
+	return n.Start
+}
+
+// Type returns type of node
+func (n *SequenceEntryNode) Type() NodeType {
+	return SequenceEntryType
+}
+
+// AddColumn add column number to child nodes recursively
+func (n *SequenceEntryNode) AddColumn(col int) {
+	n.Start.AddColumn(col)
+}
+
+// SetComment set line comment.
+func (n *SequenceEntryNode) SetComment(cm *CommentGroupNode) error {
+	n.LineComment = cm
+	return nil
+}
+
+// Comment returns comment token instance
+func (n *SequenceEntryNode) GetComment() *CommentGroupNode {
+	return n.LineComment
+}
+
+// MarshalYAML
+func (n *SequenceEntryNode) MarshalYAML() ([]byte, error) {
+	return []byte(n.String()), nil
+}
+
+func (n *SequenceEntryNode) Read(p []byte) (int, error) {
+	return readNode(p, n)
+}
+
+// SequenceEntry creates SequenceEntryNode instance.
+func SequenceEntry(start *token.Token, value Node, headComment *CommentGroupNode) *SequenceEntryNode {
+	return &SequenceEntryNode{
+		BaseNode:    &BaseNode{},
+		HeadComment: headComment,
+		Start:       start,
+		Value:       value,
+	}
+}
+
 // SequenceMergeValue creates SequenceMergeValueNode instance.
 func SequenceMergeValue(values ...MapNode) *SequenceMergeValueNode {
 	return &SequenceMergeValueNode{
diff --git a/decode.go b/decode.go
index aa8f184..dadb3e7 100644
--- a/decode.go
+++ b/decode.go
@@ -545,75 +545,6 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 	return nil, nil
 }
 
-func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) {
-	d.stepIn()
-	defer d.stepOut()
-	if d.isExceededMaxDepth() {
-		return nil, ErrExceededMaxDepth
-	}
-
-	switch n := node.(type) {
-	case *ast.MappingNode:
-		for idx, v := range n.Values {
-			value, err := d.resolveAlias(v)
-			if err != nil {
-				return nil, err
-			}
-			n.Values[idx], _ = value.(*ast.MappingValueNode)
-		}
-	case *ast.TagNode:
-		value, err := d.resolveAlias(n.Value)
-		if err != nil {
-			return nil, err
-		}
-		n.Value = value
-	case *ast.MappingKeyNode:
-		value, err := d.resolveAlias(n.Value)
-		if err != nil {
-			return nil, err
-		}
-		n.Value = value
-	case *ast.MappingValueNode:
-		if n.Key.IsMergeKey() && n.Value.Type() == ast.AliasType {
-			value, err := d.resolveAlias(n.Value)
-			if err != nil {
-				return nil, err
-			}
-			keyColumn := n.Key.GetToken().Position.Column
-			requiredColumn := keyColumn + 2
-			value.AddColumn(requiredColumn)
-			n.Value = value
-		} else {
-			key, err := d.resolveAlias(n.Key)
-			if err != nil {
-				return nil, err
-			}
-			n.Key, _ = key.(ast.MapKeyNode)
-			value, err := d.resolveAlias(n.Value)
-			if err != nil {
-				return nil, err
-			}
-			n.Value = value
-		}
-	case *ast.SequenceNode:
-		for idx, v := range n.Values {
-			value, err := d.resolveAlias(v)
-			if err != nil {
-				return nil, err
-			}
-			n.Values[idx] = value
-		}
-	case *ast.AliasNode:
-		aliasName := n.Value.GetToken().Value
-		node := d.anchorNodeMap[aliasName]
-		if node == nil {
-			return nil, fmt.Errorf("cannot find anchor by alias name %s", aliasName)
-		}
-		return d.resolveAlias(node)
-	}
-	return node, nil
-}
-
 func (d *Decoder) getMapNode(node ast.Node, isMerge bool) (ast.MapNode, error) {
 	d.stepIn()
 	defer d.stepOut()
@@ -758,36 +689,17 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st
 }
 
 func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) {
-	var err error
-	node, err = d.resolveAlias(node)
-	if err != nil {
-		return nil, err
-	}
-	doc := format.FormatNode(node, d.toCommentMap != nil)
+	doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap)
 	return []byte(doc), nil
 }
 
-func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) {
-	var err error
-	node, err = d.resolveAlias(node)
-	if err != nil {
-		return nil, false, err
-	}
-	if node.Type() == ast.AnchorType {
-		node = node.(*ast.AnchorNode).Value
+func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool) {
+	doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap)
+	var v string
+	if err := Unmarshal([]byte(doc), &v); err != nil {
+		return nil, false
 	}
-	switch n := node.(type) {
-	case *ast.StringNode:
-		return []byte(n.Value), true, nil
-	case *ast.LiteralNode:
-		return []byte(n.Value.GetToken().Value), true, nil
-	default:
-		scalar, ok := n.(ast.ScalarNode)
-		if ok {
-			return []byte(fmt.Sprint(scalar.GetValue())), true, nil
-		}
-	}
-	return nil, false, nil
+	return []byte(v), true
 }
 
 type jsonUnmarshaler interface {
@@ -924,10 +836,7 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
 	}
 
 	if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText {
-		b, ok, err := d.unmarshalableText(src)
-		if err != nil {
-			return err
-		}
+		b, ok := d.unmarshalableText(src)
 		if ok {
 			if err := unmarshaler.UnmarshalText(b); err != nil {
 				return err
diff --git a/internal/format/format.go b/internal/format/format.go
index 1137a51..5a6e63e 100644
--- a/internal/format/format.go
+++ b/internal/format/format.go
@@ -7,20 +7,134 @@ import (
 	"github.com/goccy/go-yaml/token"
 )
 
-func FormatNode(n ast.Node, existsComment bool) string {
-	return newFormatter(n.GetToken(), existsComment).format(n)
+func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node) string {
+	tk := n.GetToken()
+	if tk == nil {
+		return ""
+	}
+	formatter := newFormatter(tk, hasComment(n))
+	formatter.anchorNodeMap = anchorNodeMap
+	return formatter.format(n)
+}
+
+func FormatNode(n ast.Node) string {
+	tk := n.GetToken()
+	if tk == nil {
+		return ""
+	}
+	return newFormatter(tk, hasComment(n)).format(n)
 }
 
-func FormatFile(file *ast.File, existsComment bool) string {
+func FormatFile(file *ast.File) string {
 	if len(file.Docs) == 0 {
 		return ""
 	}
-	return newFormatter(file.Docs[0].GetToken(), existsComment).formatFile(file)
+	tk := file.Docs[0].GetToken()
+	if tk == nil {
+		return ""
+	}
+	return newFormatter(tk, hasCommentFile(file)).formatFile(file)
+}
+
+func hasCommentFile(f *ast.File) bool {
+	for _, doc := range f.Docs {
+		if hasComment(doc.Body) {
+			return true
+		}
+	}
+	return false
+}
+
+func hasComment(n ast.Node) bool {
+	if n == nil {
+		return false
+	}
+	switch nn := n.(type) {
+	case *ast.DocumentNode:
+		return hasComment(nn.Body)
+	case *ast.NullNode:
+		return nn.Comment != nil
+	case *ast.BoolNode:
+		return nn.Comment != nil
+	case *ast.IntegerNode:
+		return nn.Comment != nil
+	case *ast.FloatNode:
+		return nn.Comment != nil
+	case *ast.StringNode:
+		return nn.Comment != nil
+	case *ast.InfinityNode:
+		return nn.Comment != nil
+	case *ast.NanNode:
+		return nn.Comment != nil
+	case *ast.LiteralNode:
+		return nn.Comment != nil
+	case *ast.DirectiveNode:
+		if nn.Comment != nil {
+			return true
+		}
+		for _, value := range nn.Values {
+			if hasComment(value) {
+				return true
+			}
+		}
+	case *ast.TagNode:
+		if nn.Comment != nil {
+			return true
+		}
+		return hasComment(nn.Value)
+	case *ast.MappingNode:
+		if nn.Comment != nil || nn.FootComment != nil {
+			return true
+		}
+		for _, value := range nn.Values {
+			if value.Comment != nil || value.FootComment != nil {
+				return true
+			}
+			if hasComment(value.Key) {
+				return true
+			}
+			if hasComment(value.Value) {
+				return true
+			}
+		}
+	case *ast.MappingKeyNode:
+		return nn.Comment != nil
+	case *ast.MergeKeyNode:
+		return nn.Comment != nil
+	case *ast.SequenceNode:
+		if nn.Comment != nil || nn.FootComment != nil {
+			return true
+		}
+		for _, entry := range nn.Entries {
+			if entry.Comment != nil || entry.HeadComment != nil || entry.LineComment != nil {
+				return true
+			}
+			if hasComment(entry.Value) {
+				return true
+			}
+		}
+	case *ast.AnchorNode:
+		if nn.Comment != nil {
+			return true
+		}
+		if hasComment(nn.Name) || hasComment(nn.Value) {
+			return true
+		}
+	case *ast.AliasNode:
+		if nn.Comment != nil {
+			return true
+		}
+		if hasComment(nn.Value) {
+			return true
+		}
+	}
+	return false
 }
 
 type Formatter struct {
 	existsComment    bool
 	tokenToOriginMap map[*token.Token]string
+	anchorNodeMap    map[string]ast.Node
 }
 
 func newFormatter(tk *token.Token, existsComment bool) *Formatter {
@@ -143,18 +257,9 @@ func (f *Formatter) formatMapping(n *ast.MappingNode) string {
 		ret = f.origin(n.Start)
 	}
 	ret += f.formatCommentGroup(n.Comment)
-	entry := n.Start
 	for _, value := range n.Values {
-		if n.IsFlowStyle {
-			tk := value.GetToken()
-			for tk.Prev != nil && tk != entry {
-				tk = tk.Prev
-				if tk.Type == token.CollectEntryType {
-					ret += f.origin(tk)
-					entry = tk
-					break
-				}
-			}
+		if value.CollectEntry != nil {
+			ret += f.origin(value.CollectEntry)
 		}
 		ret += f.formatMappingValue(value)
 	}
@@ -177,27 +282,12 @@ func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
 		return "[]"
 	}
 
-	var (
-		ret   string
-		entry = n.Start
-	)
+	var ret string
 	if n.IsFlowStyle {
 		ret = f.origin(n.Start)
 	}
-	for idx, value := range n.Values {
-		tk := value.GetToken()
-		for tk.Prev != nil && tk != entry {
-			tk = tk.Prev
-			if tk.Type == token.SequenceEntryType || tk.Type == token.CollectEntryType {
-				ret += f.origin(tk)
-				entry = tk
-				break
-			}
-		}
-		if len(n.ValueHeadComments) > idx {
-			ret += f.formatCommentGroup(n.ValueHeadComments[idx])
-		}
-		ret += f.formatNode(value)
+	for _, entry := range n.Entries {
+		ret += f.formatNode(entry)
 	}
 	if n.IsFlowStyle {
 		ret += f.origin(n.End)
@@ -206,11 +296,28 @@ func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
 	return ret
 }
 
+func (f *Formatter) formatSequenceEntry(n *ast.SequenceEntryNode) string {
+	return f.formatCommentGroup(n.HeadComment) + f.origin(n.Start) + f.formatCommentGroup(n.LineComment) + f.formatNode(n.Value)
+}
+
 func (f *Formatter) formatAnchor(n *ast.AnchorNode) string {
 	return f.origin(n.Start) + f.formatNode(n.Name) + f.formatNode(n.Value)
 }
 
 func (f *Formatter) formatAlias(n *ast.AliasNode) string {
+	if f.anchorNodeMap != nil {
+		node := f.anchorNodeMap[n.Value.GetToken().Value]
+		if node != nil {
+			formatted := f.formatNode(node)
+			// If formatted text contains newline characters, indentation needs to be considered.
+			if strings.Contains(formatted, "\n") {
+				// If the first character is not a newline, the first line should be output without indentation.
+				isIgnoredFirstLine := !strings.HasPrefix(formatted, "\n")
+				formatted = f.addIndentSpace(n.GetToken().Position.IndentNum, formatted, isIgnoredFirstLine)
+			}
+			return formatted
+		}
+	}
 	return f.origin(n.Start) + f.formatNode(n.Value)
 }
 
@@ -248,6 +355,8 @@ func (f *Formatter) formatNode(n ast.Node) string {
 		return f.formatMergeKey(nn)
 	case *ast.SequenceNode:
 		return f.formatSequence(nn)
+	case *ast.SequenceEntryNode:
+		return f.formatSequenceEntry(nn)
 	case *ast.AnchorNode:
 		return f.formatAnchor(nn)
 	case *ast.AliasNode:
@@ -300,7 +409,7 @@ func (f *Formatter) trimIndentSpace(trimIndentNum int, v string) string {
 	}
 	lines := strings.Split(normalizeNewLineChars(v), "\n")
 	out := make([]string, 0, len(lines))
-	for _, line := range strings.Split(v, "\n") {
+	for _, line := range lines {
 		var cnt int
 		out = append(out, strings.TrimLeftFunc(line, func(r rune) bool {
 			cnt++
@@ -310,6 +419,23 @@ func (f *Formatter) trimIndentSpace(trimIndentNum int, v string) string {
 	return strings.Join(out, "\n")
 }
 
+func (f *Formatter) addIndentSpace(indentNum int, v string, isIgnoredFirstLine bool) string {
+	if indentNum == 0 {
+		return v
+	}
+	indent := strings.Repeat(" ", indentNum)
+	lines := strings.Split(normalizeNewLineChars(v), "\n")
+	out := make([]string, 0, len(lines))
+	for idx, line := range lines {
+		if line == "" || (isIgnoredFirstLine && idx == 0) {
+			out = append(out, line)
+			continue
+		}
+		out = append(out, indent+line)
+	}
+	return strings.Join(out, "\n")
+}
+
 // normalizeNewLineChars normalize CRLF and CR to LF.
 func normalizeNewLineChars(v string) string {
 	return strings.ReplaceAll(strings.ReplaceAll(v, "\r\n", "\n"), "\r", "\n")
diff --git a/parser/node.go b/parser/node.go
index c9ebefc..8d35554 100644
--- a/parser/node.go
+++ b/parser/node.go
@@ -14,16 +14,25 @@ func newMappingNode(ctx *context, tk *Token, isFlow bool, values ...*ast.Mapping
 	return node, nil
 }
 
-func newMappingValueNode(ctx *context, tk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) {
-	node := ast.MappingValue(tk.RawToken(), key, value)
+func newMappingValueNode(ctx *context, colonTk, entryTk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) {
+	node := ast.MappingValue(colonTk.RawToken(), key, value)
 	node.SetPath(ctx.path)
+	node.CollectEntry = entryTk.RawToken()
 	if key.GetToken().Position.Line == value.GetToken().Position.Line {
 		// originally key was commented, but now that null value has been added, value must be commented.
-		if err := setLineComment(ctx, value, tk); err != nil {
+		if err := setLineComment(ctx, value, colonTk); err != nil {
+			return nil, err
+		}
+		// set line comment by colonTk or entryTk.
+		if err := setLineComment(ctx, value, entryTk); err != nil {
 			return nil, err
 		}
 	} else {
-		if err := setLineComment(ctx, key, tk); err != nil {
+		if err := setLineComment(ctx, key, colonTk); err != nil {
+			return nil, err
+		}
+		// set line comment by colonTk or entryTk.
+		if err := setLineComment(ctx, key, entryTk); err != nil {
 			return nil, err
 		}
 	}
@@ -218,7 +227,7 @@ func newTagDefaultScalarValueNode(ctx *context, tag *token.Token) (ast.ScalarNod
 }
 
 func setLineComment(ctx *context, node ast.Node, tk *Token) error {
-	if tk.LineComment == nil {
+	if tk == nil || tk.LineComment == nil {
 		return nil
 	}
 	comment := ast.CommentGroup([]*token.Token{tk.LineComment})
diff --git a/parser/parser.go b/parser/parser.go
index 49334a2..3da78fb 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -341,7 +341,9 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 			break
 		}
 
+		var entryTk *Token
 		if tk.Type() == token.CollectEntryType {
+			entryTk = tk
 			ctx.goNext()
 		} else if !isFirst {
 			return nil, errors.ErrSyntax("',' or '}' must be specified", tk.RawToken())
@@ -357,7 +359,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 		mapKeyTk := ctx.currentToken()
 		switch mapKeyTk.GroupType() {
 		case TokenGroupMapKeyValue:
-			value, err := p.parseMapKeyValue(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group)
+			value, err := p.parseMapKeyValue(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group, entryTk)
 			if err != nil {
 				return nil, err
 			}
@@ -375,7 +377,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 				if err != nil {
 					return nil, err
 				}
-				mapValue, err := newMappingValueNode(ctx, colonTk, key, value)
+				mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value)
 				if err != nil {
 					return nil, err
 				}
@@ -390,7 +392,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 				if err != nil {
 					return nil, err
 				}
-				mapValue, err := newMappingValueNode(ctx, colonTk, key, value)
+				mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value)
 				if err != nil {
 					return nil, err
 				}
@@ -408,7 +410,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 			if err != nil {
 				return nil, err
 			}
-			mapValue, err := newMappingValueNode(ctx, mapKeyTk, key, value)
+			mapValue, err := newMappingValueNode(ctx, mapKeyTk, entryTk, key, value)
 			if err != nil {
 				return nil, err
 			}
@@ -435,7 +437,7 @@ func (p *parser) parseMap(ctx *context) (*ast.MappingNode, error) {
 	}
 	var keyValueNode *ast.MappingValueNode
 	if keyTk.GroupType() == TokenGroupMapKeyValue {
-		node, err := p.parseMapKeyValue(ctx.withGroup(keyTk.Group), keyTk.Group)
+		node, err := p.parseMapKeyValue(ctx.withGroup(keyTk.Group), keyTk.Group, nil)
 		if err != nil {
 			return nil, err
 		}
@@ -460,7 +462,7 @@ func (p *parser) parseMap(ctx *context) (*ast.MappingNode, error) {
 		if err != nil {
 			return nil, err
 		}
-		node, err := newMappingValueNode(ctx, keyTk.Group.Last(), key, value)
+		node, err := newMappingValueNode(ctx, keyTk.Group.Last(), nil, key, value)
 		if err != nil {
 			return nil, err
 		}
@@ -549,7 +551,7 @@ func (p *parser) isMapToken(tk *Token) bool {
 	return g.Type == TokenGroupMapKey || g.Type == TokenGroupMapKeyValue
 }
 
-func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup) (*ast.MappingValueNode, error) {
+func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup, entryTk *Token) (*ast.MappingValueNode, error) {
 	if g.Type != TokenGroupMapKeyValue {
 		return nil, errors.ErrSyntax("unexpected map key-value pair", g.RawToken())
 	}
@@ -567,7 +569,7 @@ func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup) (*ast.MappingValu
 	if err != nil {
 		return nil, err
 	}
-	return newMappingValueNode(c, keyGroup.Last(), key, value)
+	return newMappingValueNode(c, keyGroup.Last(), entryTk, key, value)
 }
 
 func (p *parser) parseMapKey(ctx *context, g *TokenGroup) (ast.MapKeyNode, error) {
@@ -1017,10 +1019,12 @@ func (p *parser) parseFlowSequence(ctx *context) (*ast.SequenceNode, error) {
 			break
 		}
 
+		var entryTk *Token
 		if tk.Type() == token.CollectEntryType {
 			if isFirst {
 				return nil, errors.ErrSyntax("expected sequence element, but found ','", tk.RawToken())
 			}
+			entryTk = tk
 			ctx.goNext()
 		} else if !isFirst {
 			return nil, errors.ErrSyntax("',' or ']' must be specified", tk.RawToken())
@@ -1037,11 +1041,19 @@ func (p *parser) parseFlowSequence(ctx *context) (*ast.SequenceNode, error) {
 			break
 		}
 
-		value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), ctx.currentToken())
+		ctx := ctx.withIndex(uint(len(node.Values)))
+		value, err := p.parseToken(ctx, ctx.currentToken())
 		if err != nil {
 			return nil, err
 		}
 		node.Values = append(node.Values, value)
+		seqEntry := ast.SequenceEntry(entryTk.RawToken(), value, nil)
+		if err := setLineComment(ctx, seqEntry, entryTk); err != nil {
+			return nil, err
+		}
+		seqEntry.SetPath(ctx.path)
+		node.Entries = append(node.Entries, seqEntry)
+
 		isFirst = false
 	}
 	if node.End == nil {
@@ -1061,15 +1073,22 @@ func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) {
 	tk := seqTk
 	for tk.Type() == token.SequenceEntryType && tk.Column() == seqTk.Column() {
 		seqTk := tk
-		comment := p.parseHeadComment(ctx)
+		headComment := p.parseHeadComment(ctx)
 		ctx.goNext() // skip sequence entry token
 
-		value, err := p.parseSequenceValue(ctx.withIndex(uint(len(seqNode.Values))), seqTk)
+		ctx := ctx.withIndex(uint(len(seqNode.Values)))
+		value, err := p.parseSequenceValue(ctx, seqTk)
 		if err != nil {
 			return nil, err
 		}
-		seqNode.ValueHeadComments = append(seqNode.ValueHeadComments, comment)
+		seqEntry := ast.SequenceEntry(seqTk.RawToken(), value, headComment)
+		if err := setLineComment(ctx, seqEntry, seqTk); err != nil {
+			return nil, err
+		}
+		seqEntry.SetPath(ctx.path)
+		seqNode.ValueHeadComments = append(seqNode.ValueHeadComments, headComment)
 		seqNode.Values = append(seqNode.Values, value)
+		seqNode.Entries = append(seqNode.Entries, seqEntry)
 
 		if ctx.isComment() {
 			tk = ctx.nextNotCommentToken()
diff --git a/stdlib_quote.go b/stdlib_quote.go
index be50ae6..120448d 100644
--- a/stdlib_quote.go
+++ b/stdlib_quote.go
@@ -53,8 +53,18 @@ func appendQuotedWith(buf []byte, s string, quote byte) []byte {
 
 func appendEscapedRune(buf []byte, r rune, quote byte) []byte {
 	var runeTmp [utf8.UTFMax]byte
-	if r == rune(quote) || r == '\\' { // always backslashed
-		buf = append(buf, '\\')
+	// goccy/go-yaml patch on top of the standard library's appendEscapedRune function.
+	//
+	// We use this to implement the YAML single-quoted string, where the only escape sequence is '', which represents a single quote.
+	// The below snippet from the standard library is for escaping e.g. \ with \\, which is not what we want for the single-quoted string.
+	//
+	// if r == rune(quote) || r == '\\' { // always backslashed
+	// 	buf = append(buf, '\\')
+	// 	buf = append(buf, byte(r))
+	// 	return buf
+	// }
+	if r == rune(quote) {
+		buf = append(buf, byte(r))
 		buf = append(buf, byte(r))
 		return buf
 	}


Test patch:
diff --git a/decode_test.go b/decode_test.go
index 6790395..f26af65 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -3151,6 +3151,41 @@ func TestMapKeyCustomUnmarshaler(t *testing.T) {
 	}
 }
 
+type bytesUnmershalerWithMapAlias struct{}
+
+func (*bytesUnmershalerWithMapAlias) UnmarshalYAML(b []byte) error {
+	expected := strings.TrimPrefix(`
+stuff:
+  bar:
+    - one
+    - two
+
+`, "\n")
+	if string(b) != expected {
+		return fmt.Errorf("failed to decode: expected:\n[%s]\nbut got:\n[%s]\n", expected, string(b))
+	}
+	return nil
+}
+
+func TestBytesUnmarshalerWithMapAlias(t *testing.T) {
+	yml := `
+x-foo: &data
+  bar:
+    - one
+    - two
+
+foo:
+  stuff: *data
+`
+	type T struct {
+		Foo bytesUnmershalerWithMapAlias `yaml:"foo"`
+	}
+	var v T
+	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
+		t.Fatal(err)
+	}
+}
+
 func TestDecoderPreservesDefaultValues(t *testing.T) {
 	type nested struct {
 		Val string `yaml:"val"`
diff --git a/encode_test.go b/encode_test.go
index 3095b00..b4f3913 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -704,7 +704,7 @@ func TestEncoder(t *testing.T) {
 		},
 		// Quote style
 		{
-			`v: '\'a\'b'` + "\n",
+			`v: '''a''b'` + "\n",
 			map[string]string{"v": `'a'b`},
 			[]yaml.EncodeOption{
 				yaml.UseSingleQuote(true),
@@ -717,6 +717,13 @@ func TestEncoder(t *testing.T) {
 				yaml.UseSingleQuote(false),
 			},
 		},
+		{
+			`a: '\.yaml'` + "\n",
+			map[string]string{"a": `\.yaml`},
+			[]yaml.EncodeOption{
+				yaml.UseSingleQuote(true),
+			},
+		},
 	}
 	for _, test := range tests {
 		t.Run(test.source, func(t *testing.T) {
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 487bfe1..cae4bff 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -1715,6 +1715,70 @@ multiple:
 	}
 }
 
+func TestInFlowStyle(t *testing.T) {
+	type inFlowStyle interface {
+		SetIsFlowStyle(bool)
+	}
+
+	tests := []struct {
+		source string
+		expect string
+	}{
+		{
+			`
+  - foo
+  - bar
+  - baz
+`,
+			`[foo, bar, baz]
+`,
+		},
+		{
+			`
+foo: bar
+baz: fizz
+`,
+			`{foo: bar, baz: fizz}
+`,
+		},
+		{
+			`
+foo:
+  - bar
+  - baz
+  - fizz: buzz
+`,
+			`{foo: [bar, baz, {fizz: buzz}]}
+`,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.source, func(t *testing.T) {
+			f, err := parser.ParseBytes([]byte(test.source), parser.ParseComments)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if len(f.Docs) != 1 {
+				t.Fatal("failed to parse content")
+			}
+
+			ifs, ok := f.Docs[0].Body.(inFlowStyle)
+			if !ok {
+				t.Fatalf("failed to get inFlowStyle. got: %T", f.Docs[0].Body)
+			}
+			ifs.SetIsFlowStyle(true)
+
+			got := f.String()
+
+			if got != test.expect {
+				t.Fatalf("failed to parse comment:\nexpected:\n%s\ngot:\n%s", test.expect, got)
+			}
+		})
+	}
+}
+
 func TestNodePath(t *testing.T) {
 	yml := `
 a: # commentA