Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pkg/yqlib/decoder_toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,30 @@ func (dec *tomlDecoder) createInlineTableMap(tomlNode *toml.Node) (*CandidateNod

func (dec *tomlDecoder) createArray(tomlNode *toml.Node) (*CandidateNode, error) {
content := make([]*CandidateNode, 0)
var pendingArrayComments []string

iterator := tomlNode.Children()
for iterator.Next() {
child := iterator.Node()

// Handle comments within arrays
if child.Kind == toml.Comment {
// Collect comments to attach to the next array element
pendingArrayComments = append(pendingArrayComments, string(child.Data))
continue
}

yamlNode, err := dec.decodeNode(child)
if err != nil {
return nil, err
}

// Attach any pending comments to this array element
if len(pendingArrayComments) > 0 {
yamlNode.HeadComment = strings.Join(pendingArrayComments, "\n")
pendingArrayComments = make([]string, 0)
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use nil or [:0] instead of make([]string, 0) to reset the slice. The current approach allocates a new slice unnecessarily. Simply setting to nil or reslicing would be more efficient.

Suggested change
pendingArrayComments = make([]string, 0)
pendingArrayComments = pendingArrayComments[:0]

Copilot uses AI. Check for mistakes.
}

content = append(content, yamlNode)
}

Expand Down
75 changes: 75 additions & 0 deletions pkg/yqlib/encoder_toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,81 @@ func (te *tomlEncoder) writeArrayAttribute(w io.Writer, key string, seq *Candida
return err
}

// Check if any array elements have head comments - if so, use multiline format
hasElementComments := false
for _, it := range seq.Content {
if it.HeadComment != "" {
hasElementComments = true
break
}
}

if hasElementComments {
// Write multiline array format with comments
if _, err := w.Write([]byte(key + " = [\n")); err != nil {
return err
}

for i, it := range seq.Content {
// Write head comment for this element
if it.HeadComment != "" {
commentLines := strings.Split(it.HeadComment, "\n")
for _, commentLine := range commentLines {
if strings.TrimSpace(commentLine) != "" {
if !strings.HasPrefix(strings.TrimSpace(commentLine), "#") {
commentLine = "# " + commentLine
}
if _, err := w.Write([]byte(" " + commentLine + "\n")); err != nil {
Comment on lines +245 to +249
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable commentLine is being modified after trimming, but the original untrimmed value is used in the write. This could lead to confusion. Consider storing the trimmed value in a separate variable or modifying commentLine before the prefix check.

Suggested change
if strings.TrimSpace(commentLine) != "" {
if !strings.HasPrefix(strings.TrimSpace(commentLine), "#") {
commentLine = "# " + commentLine
}
if _, err := w.Write([]byte(" " + commentLine + "\n")); err != nil {
trimmed := strings.TrimSpace(commentLine)
if trimmed != "" {
if !strings.HasPrefix(trimmed, "#") {
trimmed = "# " + trimmed
}
if _, err := w.Write([]byte(" " + trimmed + "\n")); err != nil {

Copilot uses AI. Check for mistakes.
return err
}
}
}
}

// Write the element value
var itemStr string
switch it.Kind {
case ScalarNode:
itemStr = te.formatScalar(it)
case SequenceNode:
nested, err := te.sequenceToInlineArray(it)
if err != nil {
return err
}
itemStr = nested
case MappingNode:
inline, err := te.mappingToInlineTable(it)
if err != nil {
return err
}
itemStr = inline
case AliasNode:
return fmt.Errorf("aliases are not supported in TOML")
default:
return fmt.Errorf("unsupported array item kind: %v", it.Kind)
}

// Always add trailing comma in multiline arrays
itemStr += ","

if _, err := w.Write([]byte(" " + itemStr + "\n")); err != nil {
return err
}

// Add blank line between elements (except after the last one)
if i < len(seq.Content)-1 {
if _, err := w.Write([]byte("\n")); err != nil {
return err
}
}
}

if _, err := w.Write([]byte("]\n")); err != nil {
return err
}
return nil
}

// Join scalars or nested arrays recursively into TOML array syntax
items := make([]string, 0, len(seq.Content))
for _, it := range seq.Content {
Expand Down
16 changes: 16 additions & 0 deletions pkg/yqlib/toml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,16 @@ var subArrays = `
[[array.subarray.subsubarray]]
`

var tomlTableWithComments = `[section]
the_array = [
# comment
"value 1",

# comment
"value 2",
]
`

var expectedSubArrays = `array:
- subarray:
- subsubarray:
Expand Down Expand Up @@ -598,6 +608,12 @@ var tomlScenarios = []formatScenario{
expected: sampleFromWeb,
scenarioType: "roundtrip",
},
{
skipDoc: true,
input: tomlTableWithComments,
expected: tomlTableWithComments,
scenarioType: "roundtrip",
},
}

func testTomlScenario(t *testing.T, s formatScenario) {
Expand Down
Loading