mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-07 14:25:38 +00:00
Merge b78119772a into e2f1d5ccf7
This commit is contained in:
commit
9cf3f90788
@ -157,9 +157,25 @@ func (dec *tomlDecoder) createInlineTableMap(tomlNode *toml.Node) (*CandidateNod
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commentIsOnNewLine reports whether comment begins on a line after the end of
|
||||||
|
// prev, by scanning the raw input bytes between them for a newline. When the
|
||||||
|
// range cannot be determined it returns true, preserving the historic behaviour
|
||||||
|
// of treating in-array comments as head comments for the next element.
|
||||||
|
func (dec *tomlDecoder) commentIsOnNewLine(prev, comment *toml.Node) bool {
|
||||||
|
prevEnd := prev.Raw.Offset + prev.Raw.Length
|
||||||
|
commentStart := comment.Raw.Offset
|
||||||
|
if commentStart < prevEnd {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
between := dec.parser.Raw(toml.Range{Offset: prevEnd, Length: commentStart - prevEnd})
|
||||||
|
return bytes.IndexByte(between, '\n') >= 0
|
||||||
|
}
|
||||||
|
|
||||||
func (dec *tomlDecoder) createArray(tomlNode *toml.Node) (*CandidateNode, error) {
|
func (dec *tomlDecoder) createArray(tomlNode *toml.Node) (*CandidateNode, error) {
|
||||||
content := make([]*CandidateNode, 0)
|
content := make([]*CandidateNode, 0)
|
||||||
var pendingArrayComments []string
|
var pendingArrayComments []string
|
||||||
|
var prevElem *toml.Node
|
||||||
|
var prevYamlNode *CandidateNode
|
||||||
|
|
||||||
iterator := tomlNode.Children()
|
iterator := tomlNode.Children()
|
||||||
for iterator.Next() {
|
for iterator.Next() {
|
||||||
@ -167,8 +183,19 @@ func (dec *tomlDecoder) createArray(tomlNode *toml.Node) (*CandidateNode, error)
|
|||||||
|
|
||||||
// Handle comments within arrays
|
// Handle comments within arrays
|
||||||
if child.Kind == toml.Comment {
|
if child.Kind == toml.Comment {
|
||||||
// Collect comments to attach to the next array element
|
// A comment on the same line as the preceding element is a trailing
|
||||||
pendingArrayComments = append(pendingArrayComments, string(child.Data))
|
// (inline) comment on that element; a comment on its own line is a
|
||||||
|
// head comment for the next element.
|
||||||
|
if prevYamlNode != nil && !dec.commentIsOnNewLine(prevElem, child) {
|
||||||
|
if prevYamlNode.LineComment == "" {
|
||||||
|
prevYamlNode.LineComment = string(child.Data)
|
||||||
|
} else {
|
||||||
|
prevYamlNode.LineComment = prevYamlNode.LineComment + "\n" + string(child.Data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Collect comments to attach to the next array element
|
||||||
|
pendingArrayComments = append(pendingArrayComments, string(child.Data))
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +211,8 @@ func (dec *tomlDecoder) createArray(tomlNode *toml.Node) (*CandidateNode, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
content = append(content, yamlNode)
|
content = append(content, yamlNode)
|
||||||
|
prevElem = child
|
||||||
|
prevYamlNode = yamlNode
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CandidateNode{
|
return &CandidateNode{
|
||||||
|
|||||||
@ -188,6 +188,10 @@ func (te *tomlEncoder) encodeTopLevelEntry(w io.Writer, path []string, node *Can
|
|||||||
te.wroteRootAttr = false
|
te.wroteRootAttr = false
|
||||||
}
|
}
|
||||||
for _, it := range node.Content {
|
for _, it := range node.Content {
|
||||||
|
// Write head comment before the array-of-tables header
|
||||||
|
if err := te.writeComment(w, it.HeadComment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if _, err := w.Write([]byte("[[" + quotedKey + "]]\n")); err != nil {
|
if _, err := w.Write([]byte("[[" + quotedKey + "]]\n")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -283,10 +287,10 @@ func (te *tomlEncoder) writeArrayAttribute(w io.Writer, key string, seq *Candida
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any array elements have head comments - if so, use multiline format
|
// Check if any array elements have head or line comments - if so, use multiline format
|
||||||
hasElementComments := false
|
hasElementComments := false
|
||||||
for _, it := range seq.Content {
|
for _, it := range seq.Content {
|
||||||
if it.HeadComment != "" {
|
if it.HeadComment != "" || it.LineComment != "" {
|
||||||
hasElementComments = true
|
hasElementComments = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -299,6 +303,13 @@ func (te *tomlEncoder) writeArrayAttribute(w io.Writer, key string, seq *Candida
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, it := range seq.Content {
|
for i, it := range seq.Content {
|
||||||
|
// Separate a head-commented element from the previous one with a blank line
|
||||||
|
if i > 0 && it.HeadComment != "" {
|
||||||
|
if _, err := w.Write([]byte("\n")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write head comment for this element
|
// Write head comment for this element
|
||||||
if it.HeadComment != "" {
|
if it.HeadComment != "" {
|
||||||
commentLines := strings.Split(it.HeadComment, "\n")
|
commentLines := strings.Split(it.HeadComment, "\n")
|
||||||
@ -340,15 +351,17 @@ func (te *tomlEncoder) writeArrayAttribute(w io.Writer, key string, seq *Candida
|
|||||||
// Always add trailing comma in multiline arrays
|
// Always add trailing comma in multiline arrays
|
||||||
itemStr += ","
|
itemStr += ","
|
||||||
|
|
||||||
if _, err := w.Write([]byte(" " + itemStr + "\n")); err != nil {
|
// Append trailing (inline) comment on the same line, if present
|
||||||
return err
|
if it.LineComment != "" {
|
||||||
|
lineComment := strings.TrimSpace(it.LineComment)
|
||||||
|
if !strings.HasPrefix(lineComment, "#") {
|
||||||
|
lineComment = "# " + lineComment
|
||||||
|
}
|
||||||
|
itemStr += " " + lineComment
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add blank line between elements (except after the last one)
|
if _, err := w.Write([]byte(" " + itemStr + "\n")); err != nil {
|
||||||
if i < len(seq.Content)-1 {
|
return err
|
||||||
if _, err := w.Write([]byte("\n")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +559,10 @@ func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *Cand
|
|||||||
te.wroteRootAttr = false
|
te.wroteRootAttr = false
|
||||||
}
|
}
|
||||||
for _, it := range v.Content {
|
for _, it := range v.Content {
|
||||||
|
// Write head comment before the array-of-tables header
|
||||||
|
if err := te.writeComment(w, it.HeadComment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if _, err := w.Write([]byte("[[" + key + "]]\n")); err != nil {
|
if _, err := w.Write([]byte("[[" + key + "]]\n")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -603,6 +620,10 @@ func (te *tomlEncoder) encodeMappingBodyWithPath(w io.Writer, path []string, m *
|
|||||||
if isTomlArrayOfTables(v) {
|
if isTomlArrayOfTables(v) {
|
||||||
dotted := tomlDottedKey(append(append([]string{}, path...), k))
|
dotted := tomlDottedKey(append(append([]string{}, path...), k))
|
||||||
for _, it := range v.Content {
|
for _, it := range v.Content {
|
||||||
|
// Write head comment before the array-of-tables header
|
||||||
|
if err := te.writeComment(w, it.HeadComment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if _, err := w.Write([]byte("[[" + dotted + "]]\n")); err != nil {
|
if _, err := w.Write([]byte("[[" + dotted + "]]\n")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -677,7 +698,7 @@ func (te *tomlEncoder) colorizeToml(input []byte) []byte {
|
|||||||
|
|
||||||
// Table sections - [section] or [[array]]
|
// Table sections - [section] or [[array]]
|
||||||
// Only treat '[' as a table section if it appears at the start of the line
|
// Only treat '[' as a table section if it appears at the start of the line
|
||||||
// (possibly after whitespace). This avoids incorrectly colouring inline arrays like
|
// (possibly after whitespace). This avoids incorrectly colouring inline arrays like
|
||||||
// "ports = [8000, 8001]" as table sections.
|
// "ports = [8000, 8001]" as table sections.
|
||||||
if ch == '[' {
|
if ch == '[' {
|
||||||
isSectionHeader := true
|
isSectionHeader := true
|
||||||
|
|||||||
@ -325,6 +325,33 @@ the_array = [
|
|||||||
]
|
]
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// Bug: a head comment directly above an [[array-of-tables]] header is dropped
|
||||||
|
// on round-trip when other content precedes the array-of-tables.
|
||||||
|
var rtArrayTableHeadComment = `[package]
|
||||||
|
name = "x"
|
||||||
|
|
||||||
|
# the binary
|
||||||
|
[[bin]]
|
||||||
|
name = "y"
|
||||||
|
`
|
||||||
|
|
||||||
|
// Same bug via a nested array-of-tables ([[a.b]]) under a table with attributes.
|
||||||
|
var rtNestedArrayTableHeadComment = `[fruit]
|
||||||
|
name = "apple"
|
||||||
|
# a variety
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "red delicious"
|
||||||
|
`
|
||||||
|
|
||||||
|
// Bug: an inline (trailing) comment on an array item detaches from its item and
|
||||||
|
// floats onto its own line as a head comment on the next item, with a spurious
|
||||||
|
// blank line.
|
||||||
|
var rtArrayItemInlineComment = `default = [
|
||||||
|
"a", # why a
|
||||||
|
"b",
|
||||||
|
]
|
||||||
|
`
|
||||||
|
|
||||||
var expectedSubArrays = `array:
|
var expectedSubArrays = `array:
|
||||||
- subarray:
|
- subarray:
|
||||||
- subsubarray:
|
- subsubarray:
|
||||||
@ -369,7 +396,7 @@ var tomlScenarios = []formatScenario{
|
|||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
description: "blank",
|
description: "blank",
|
||||||
input: `A = "hello`,
|
input: `A = "hello`,
|
||||||
expectedError: `bad file 'sample.yml': basic string not terminated by "`,
|
expectedError: `bad file 'sample.yml': unterminated basic string`,
|
||||||
scenarioType: "decode-error",
|
scenarioType: "decode-error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -700,6 +727,30 @@ var tomlScenarios = []formatScenario{
|
|||||||
expected: tomlTableWithComments,
|
expected: tomlTableWithComments,
|
||||||
scenarioType: "roundtrip",
|
scenarioType: "roundtrip",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
description: "Roundtrip: head comment above array-of-tables",
|
||||||
|
input: rtArrayTableHeadComment,
|
||||||
|
expression: ".",
|
||||||
|
expected: rtArrayTableHeadComment,
|
||||||
|
scenarioType: "roundtrip",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
description: "Roundtrip: head comment above nested array-of-tables",
|
||||||
|
input: rtNestedArrayTableHeadComment,
|
||||||
|
expression: ".",
|
||||||
|
expected: rtNestedArrayTableHeadComment,
|
||||||
|
scenarioType: "roundtrip",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
description: "Roundtrip: inline comment on array item",
|
||||||
|
input: rtArrayItemInlineComment,
|
||||||
|
expression: ".",
|
||||||
|
expected: rtArrayItemInlineComment,
|
||||||
|
scenarioType: "roundtrip",
|
||||||
|
},
|
||||||
// Encode (YAML → TOML) scenarios - verify readable table sections are produced
|
// Encode (YAML → TOML) scenarios - verify readable table sections are produced
|
||||||
{
|
{
|
||||||
description: "Encode: Simple mapping produces table section",
|
description: "Encode: Simple mapping produces table section",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user