mirror of
https://github.com/mikefarah/yq.git
synced 2026-09-01 14:14:52 +08:00
TOML encoder: prefer readable table sections over inline tables (#2649)
* Initial plan
* Fix TOML encoder to prefer readable table sections over inline tables
When converting from YAML/JSON to TOML, the encoder now always uses
readable TOML table section syntax ([section]) instead of compact inline
hash table syntax (key = { ... }), which better matches TOML's goal as
a human-focused configuration format.
Changes:
- decoder_toml.go: Mark inline TOML tables with FlowStyle so round-trips
can be distinguished from YAML flow mappings
- encoder_toml.go:
- encodeTopLevelEntry: use FlowStyle check instead of EncodeSeparate to
decide inline vs table section (all block mappings now become tables)
- encodeSeparateMapping: count FlowStyle children as attributes; use
recursive encodeSeparateMapping for nested non-flow mappings
- encodeMappingBodyWithPath: emit non-flow child mappings as sub-table
sections instead of inline tables
- toml_test.go: add encode (YAML→TOML) test scenarios, update roundtrip
expectations for inline tables (now expanded to table sections)
Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/4824a219-6d5e-42e7-bca1-a8a277bf8c6a
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
* Fix TOML roundtrip: use TomlInline flag instead of FlowStyle to preserve inline tables
FlowStyle affected YAML decode output (causing inline tables to appear as
YAML flow mappings). Replace it with a new TOML-specific TomlInline bool
on CandidateNode that:
- Is set by the TOML decoder for inline tables (not FlowStyle)
- Is copied by UpdateAttributesFrom so it survives DeeplyAssign merges
- Is checked by the TOML encoder alongside FlowStyle (for YAML flow maps)
- Has no effect on the YAML encoder, preserving existing TOML→YAML output
TOML roundtrip tests are restored to their original expected values (inline
tables stay inline, table sections stay as sections).
Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/f59bdf62-6d16-4664-991b-38eb87c9d81c
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
* Refactor EncodeSeparate+TomlInline into a single EncodeHint enum
Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/24db9a8f-601d-4ccf-ada7-129ed3226bb6
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
* Fix stale comment in hasStructuralChildren
Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/24db9a8f-601d-4ccf-ada7-129ed3226bb6
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
* Remove unused hasStructuralChildren method from tomlEncoder
Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/2c234b77-28e9-4995-ba6f-9d213ec551a0
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
This commit is contained in:
co-authored by
mikefarah
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
parent
c47fe40a30
commit
2927a28283
+23
-50
@@ -184,12 +184,9 @@ func (te *tomlEncoder) encodeTopLevelEntry(w io.Writer, path []string, node *Can
|
||||
// Regular array attribute
|
||||
return te.writeArrayAttribute(w, path[len(path)-1], node)
|
||||
case MappingNode:
|
||||
// Inline table if not EncodeSeparate, else emit separate tables/arrays of tables for children under this path
|
||||
if !node.EncodeSeparate {
|
||||
// If children contain mappings or arrays of mappings, prefer separate sections
|
||||
if te.hasEncodeSeparateChild(node) || te.hasStructuralChildren(node) {
|
||||
return te.encodeSeparateMapping(w, path, node)
|
||||
}
|
||||
// Use inline table syntax for nodes explicitly marked as TOML inline tables
|
||||
// or YAML flow mappings. All other mappings become readable TOML table sections.
|
||||
if node.EncodeHint == EncodeHintInline || node.Style&FlowStyle != 0 {
|
||||
return te.writeInlineTableAttribute(w, path[len(path)-1], node)
|
||||
}
|
||||
return te.encodeSeparateMapping(w, path, node)
|
||||
@@ -451,7 +448,8 @@ func (te *tomlEncoder) writeTableHeader(w io.Writer, path []string, m *Candidate
|
||||
// encodeSeparateMapping handles a mapping that should be encoded as table sections.
|
||||
// It emits the table header for this mapping if it has any content, then processes children.
|
||||
func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *CandidateNode) error {
|
||||
// Check if this mapping has any non-mapping, non-array-of-tables children (i.e., attributes)
|
||||
// Check if this mapping has any non-mapping, non-array-of-tables children (i.e., attributes).
|
||||
// Inline mapping children also count as attributes since they render as key = { ... }.
|
||||
hasAttrs := false
|
||||
for i := 0; i < len(m.Content); i += 2 {
|
||||
v := m.Content[i+1]
|
||||
@@ -459,6 +457,10 @@ func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *Cand
|
||||
hasAttrs = true
|
||||
break
|
||||
}
|
||||
if v.Kind == MappingNode && (v.EncodeHint == EncodeHintInline || v.Style&FlowStyle != 0) {
|
||||
hasAttrs = true
|
||||
break
|
||||
}
|
||||
if v.Kind == SequenceNode {
|
||||
// Check if it's NOT an array of tables
|
||||
allMaps := true
|
||||
@@ -486,18 +488,14 @@ func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *Cand
|
||||
return nil
|
||||
}
|
||||
|
||||
// No attributes, just nested structures - process children
|
||||
// No attributes, just nested table structures - process children recursively
|
||||
for i := 0; i < len(m.Content); i += 2 {
|
||||
k := m.Content[i].Value
|
||||
v := m.Content[i+1]
|
||||
switch v.Kind {
|
||||
case MappingNode:
|
||||
// Emit [path.k]
|
||||
newPath := append(append([]string{}, path...), k)
|
||||
if err := te.writeTableHeader(w, newPath, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := te.encodeMappingBodyWithPath(w, newPath, v); err != nil {
|
||||
if err := te.encodeSeparateMapping(w, newPath, v); err != nil {
|
||||
return err
|
||||
}
|
||||
case SequenceNode:
|
||||
@@ -535,39 +533,6 @@ func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *Cand
|
||||
return nil
|
||||
}
|
||||
|
||||
func (te *tomlEncoder) hasEncodeSeparateChild(m *CandidateNode) bool {
|
||||
for i := 0; i < len(m.Content); i += 2 {
|
||||
v := m.Content[i+1]
|
||||
if v.Kind == MappingNode && v.EncodeSeparate {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (te *tomlEncoder) hasStructuralChildren(m *CandidateNode) bool {
|
||||
for i := 0; i < len(m.Content); i += 2 {
|
||||
v := m.Content[i+1]
|
||||
// Only consider it structural if mapping has EncodeSeparate or is non-empty
|
||||
if v.Kind == MappingNode && v.EncodeSeparate {
|
||||
return true
|
||||
}
|
||||
if v.Kind == SequenceNode {
|
||||
allMaps := true
|
||||
for _, it := range v.Content {
|
||||
if it.Kind != MappingNode {
|
||||
allMaps = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allMaps {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// encodeMappingBodyWithPath encodes attributes and nested arrays of tables using full dotted path context
|
||||
func (te *tomlEncoder) encodeMappingBodyWithPath(w io.Writer, path []string, m *CandidateNode) error {
|
||||
// First, attributes (scalars and non-map arrays)
|
||||
@@ -621,13 +586,21 @@ func (te *tomlEncoder) encodeMappingBodyWithPath(w io.Writer, path []string, m *
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, child mappings that are not marked EncodeSeparate get inlined as attributes
|
||||
// Finally, child mappings: inline-hint or flow-style ones become inline table attributes,
|
||||
// while all others are emitted as separate sub-table sections.
|
||||
for i := 0; i < len(m.Content); i += 2 {
|
||||
k := m.Content[i].Value
|
||||
v := m.Content[i+1]
|
||||
if v.Kind == MappingNode && !v.EncodeSeparate {
|
||||
if err := te.writeInlineTableAttribute(w, k, v); err != nil {
|
||||
return err
|
||||
if v.Kind == MappingNode {
|
||||
if v.EncodeHint == EncodeHintInline || v.Style&FlowStyle != 0 {
|
||||
if err := te.writeInlineTableAttribute(w, k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
subPath := append(append([]string{}, path...), k)
|
||||
if err := te.encodeSeparateMapping(w, subPath, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user