Adding to array copies styling of previous elements #722

This commit is contained in:
Mike Farah 2022-02-04 09:24:48 +11:00
parent 992fe066aa
commit d00153de71
3 changed files with 102 additions and 139 deletions

View File

@ -9,29 +9,6 @@ Add behaves differently according to the type of the LHS:
Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`. Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`.
## Concatenate and assign arrays
Given a sample.yml file of:
```yaml
a:
val: thing
b:
- cat
- dog
```
then
```bash
yq '.a.b += ["cow"]' sample.yml
```
will output
```yaml
a:
val: thing
b:
- cat
- dog
- cow
```
## Concatenate arrays ## Concatenate arrays
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml
@ -54,6 +31,28 @@ will output
- 4 - 4
``` ```
## Concatenate to existing array
Note that the styling of `a` is kept.
Given a sample.yml file of:
```yaml
a: [1,2]
b:
- 3
- 4
```
then
```bash
yq '.a += .b' sample.yml
```
will output
```yaml
a: [1, 2, 3, 4]
b:
- 3
- 4
```
## Concatenate null to array ## Concatenate null to array
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml
@ -71,6 +70,22 @@ will output
- 2 - 2
``` ```
## Append to existing array
Note that the styling is copied from existing array elements
Given a sample.yml file of:
```yaml
a: ['dog']
```
then
```bash
yq '.a += "cat"' sample.yml
```
will output
```yaml
a: ['dog', 'cat']
```
## Add new object to array ## Add new object to array
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml
@ -87,76 +102,6 @@ will output
- cat: meow - cat: meow
``` ```
## Add string to array
Given a sample.yml file of:
```yaml
a:
- 1
- 2
```
then
```bash
yq '.a + "hello"' sample.yml
```
will output
```yaml
- 1
- 2
- hello
```
## Append to array
Given a sample.yml file of:
```yaml
a:
- 1
- 2
b:
- 3
- 4
```
then
```bash
yq '.a = .a + .b' sample.yml
```
will output
```yaml
a:
- 1
- 2
- 3
- 4
b:
- 3
- 4
```
## Append another array using +=
Given a sample.yml file of:
```yaml
a:
- 1
- 2
b:
- 3
- 4
```
then
```bash
yq '.a += .b' sample.yml
```
will output
```yaml
a:
- 1
- 2
- 3
- 4
b:
- 3
- 4
```
## Relative append ## Relative append
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml
@ -195,7 +140,7 @@ b: meow
``` ```
then then
```bash ```bash
yq '.a = .a + .b' sample.yml yq '.a += .b' sample.yml
``` ```
will output will output
```yaml ```yaml

View File

@ -18,16 +18,23 @@ func addAssignOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
return compoundAssignFunction(d, context, expressionNode, createAddOp) return compoundAssignFunction(d, context, expressionNode, createAddOp)
} }
func toNodes(candidate *CandidateNode) []*yaml.Node { func toNodes(candidate *CandidateNode, lhs *CandidateNode) ([]*yaml.Node, error) {
if candidate.Node.Tag == "!!null" { if candidate.Node.Tag == "!!null" {
return []*yaml.Node{} return []*yaml.Node{}, nil
}
clone, err := candidate.Copy()
if err != nil {
return nil, err
} }
switch candidate.Node.Kind { switch candidate.Node.Kind {
case yaml.SequenceNode: case yaml.SequenceNode:
return candidate.Node.Content return clone.Node.Content, nil
default: default:
return []*yaml.Node{candidate.Node} if len(lhs.Node.Content) > 0 {
clone.Node.Style = lhs.Node.Content[0].Style
}
return []*yaml.Node{clone.Node}, nil
} }
} }
@ -54,7 +61,10 @@ func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *Candida
case yaml.MappingNode: case yaml.MappingNode:
addMaps(target, lhs, rhs) addMaps(target, lhs, rhs)
case yaml.SequenceNode: case yaml.SequenceNode:
addSequences(target, lhs, rhs) if err := addSequences(target, lhs, rhs); err != nil {
return nil, err
}
case yaml.ScalarNode: case yaml.ScalarNode:
if rhs.Node.Kind != yaml.ScalarNode { if rhs.Node.Kind != yaml.ScalarNode {
return nil, fmt.Errorf("%v (%v) cannot be added to a %v", rhs.Node.Tag, rhs.Path, lhsNode.Tag) return nil, fmt.Errorf("%v (%v) cannot be added to a %v", rhs.Node.Tag, rhs.Path, lhsNode.Tag)
@ -139,7 +149,7 @@ func addScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) error {
return nil return nil
} }
func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) { func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
target.Node.Kind = yaml.SequenceNode target.Node.Kind = yaml.SequenceNode
if len(lhs.Node.Content) > 0 { if len(lhs.Node.Content) > 0 {
target.Node.Style = lhs.Node.Style target.Node.Style = lhs.Node.Style
@ -147,7 +157,15 @@ func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode)
target.Node.Tag = lhs.Node.Tag target.Node.Tag = lhs.Node.Tag
target.Node.Content = make([]*yaml.Node, len(lhs.Node.Content)) target.Node.Content = make([]*yaml.Node, len(lhs.Node.Content))
copy(target.Node.Content, lhs.Node.Content) copy(target.Node.Content, lhs.Node.Content)
target.Node.Content = append(target.Node.Content, toNodes(rhs)...)
extraNodes, err := toNodes(rhs, lhs)
if err != nil {
return err
}
target.Node.Content = append(target.Node.Content, extraNodes...)
return nil
} }
func addMaps(target *CandidateNode, lhsC *CandidateNode, rhsC *CandidateNode) { func addMaps(target *CandidateNode, lhsC *CandidateNode, rhsC *CandidateNode) {

View File

@ -30,14 +30,7 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: 0\n", "D0, P[], (doc)::a: 0\n",
}, },
}, },
{
description: "Concatenate and assign arrays",
document: `{a: {val: thing, b: [cat,dog]}}`,
expression: ".a.b += [\"cow\"]",
expected: []string{
"D0, P[], (doc)::{a: {val: thing, b: [cat, dog, cow]}}\n",
},
},
{ {
description: "Concatenate arrays", description: "Concatenate arrays",
document: `{a: [1,2], b: [3,4]}`, document: `{a: [1,2], b: [3,4]}`,
@ -46,6 +39,16 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[a], (!!seq)::[1, 2, 3, 4]\n", "D0, P[a], (!!seq)::[1, 2, 3, 4]\n",
}, },
}, },
{
description: "Concatenate to existing array",
subdescription: "Note that the styling of `a` is kept.",
document: "a: [1,2]\nb:\n - 3\n - 4",
dontFormatInputForDoc: true,
expression: `.a += .b`,
expected: []string{
"D0, P[], (doc)::a: [1, 2, 3, 4]\nb:\n - 3\n - 4\n",
},
},
{ {
skipDoc: true, skipDoc: true,
expression: `[1] + ([2], [3])`, expression: `[1] + ([2], [3])`,
@ -72,12 +75,23 @@ var addOperatorScenarios = []expressionScenario{
}, },
}, },
{ {
skipDoc: true, description: "Append to existing array",
description: "Concatenate to existing array", subdescription: "Note that the styling is copied from existing array elements",
document: `{a: [dog]}`, dontFormatInputForDoc: true,
expression: `.a + "cat"`, document: `a: ['dog']`,
expression: `.a += "cat"`,
expected: []string{ expected: []string{
"D0, P[a], (!!seq)::[dog, cat]\n", "D0, P[], (doc)::a: ['dog', 'cat']\n",
},
},
{
skipDoc: true,
description: "Concatenate to existing array",
subdescription: "does not modify original",
document: `{a: ['dog'], b: cat}`,
expression: `.a = .a + .b`,
expected: []string{
"D0, P[], (doc)::{a: ['dog', 'cat'], b: cat}\n",
}, },
}, },
{ {
@ -116,6 +130,16 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[a], (!!map)::{c: dog, b: cat}\n", "D0, P[a], (!!map)::{c: dog, b: cat}\n",
}, },
}, },
{
skipDoc: true,
description: "Concatenate to existing object",
subdescription: "matches stylig",
document: "a:\n c: dog",
expression: `.a + {"b": "cat"}`,
expected: []string{
"D0, P[a], (!!map)::c: dog\nb: cat\n",
},
},
{ {
skipDoc: true, skipDoc: true,
description: "Concatenate to empty object in place", description: "Concatenate to empty object in place",
@ -142,30 +166,6 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[a], (!!seq)::[{dog: woof}, {cat: meow}]\n", "D0, P[a], (!!seq)::[{dog: woof}, {cat: meow}]\n",
}, },
}, },
{
description: "Add string to array",
document: `{a: [1,2]}`,
expression: `.a + "hello"`,
expected: []string{
"D0, P[a], (!!seq)::[1, 2, hello]\n",
},
},
{
description: "Append to array",
document: `{a: [1,2], b: [3,4]}`,
expression: `.a = .a + .b`,
expected: []string{
"D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n",
},
},
{
description: "Append another array using +=",
document: `{a: [1,2], b: [3,4]}`,
expression: `.a += .b`,
expected: []string{
"D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n",
},
},
{ {
description: "Relative append", description: "Relative append",
document: `a: { a1: {b: [cat]}, a2: {b: [dog]}, a3: {} }`, document: `a: { a1: {b: [cat]}, a2: {b: [dog]}, a3: {} }`,
@ -177,7 +177,7 @@ var addOperatorScenarios = []expressionScenario{
{ {
description: "String concatenation", description: "String concatenation",
document: `{a: cat, b: meow}`, document: `{a: cat, b: meow}`,
expression: `.a = .a + .b`, expression: `.a += .b`,
expected: []string{ expected: []string{
"D0, P[], (doc)::{a: catmeow, b: meow}\n", "D0, P[], (doc)::{a: catmeow, b: meow}\n",
}, },