11package yqutil
22
33import (
4- "bufio"
54 "bytes"
65 "fmt"
76 "os"
87 "strings"
98
9+ "github.com/google/yamlfmt"
1010 "github.com/google/yamlfmt/formatters/basic"
1111 "github.com/mikefarah/yq/v4/pkg/yqlib"
1212 "github.com/sirupsen/logrus"
@@ -16,7 +16,16 @@ import (
1616// EvaluateExpression evaluates the yq expression, and returns the modified yaml.
1717func EvaluateExpression (expression string , content []byte ) ([]byte , error ) {
1818 logrus .Debugf ("Evaluating yq expression: %q" , expression )
19- contentModified , err := replaceLineBreaksWithMagicString (content )
19+ formatter , err := yamlfmtBasicFormatter ()
20+ if err != nil {
21+ return nil , err
22+ }
23+ // `ApplyFeatures()` is being called directly before passing content to `yqlib`.
24+ // This results in `ApplyFeatures()` being called twice with `FeatureApplyBefore`:
25+ // once here and once inside `formatter.Format`.
26+ // Currently, calling `ApplyFeatures()` with `FeatureApplyBefore` twice is not an issue,
27+ // but future changes to `yamlfmt` might cause problems if it is called twice.
28+ contentModified , err := formatter .Features .ApplyFeatures (content , yamlfmt .FeatureApplyBefore )
2029 if err != nil {
2130 return nil , err
2231 }
@@ -75,7 +84,7 @@ func EvaluateExpression(expression string, content []byte) ([]byte, error) {
7584 return nil , err
7685 }
7786
78- return yamlfmt (out .Bytes ())
87+ return formatter . Format (out .Bytes ())
7988}
8089
8190func Join (yqExprs []string ) string {
@@ -85,55 +94,23 @@ func Join(yqExprs []string) string {
8594 return strings .Join (yqExprs , " | " )
8695}
8796
88- func yamlfmt ( content [] byte ) ([] byte , error ) {
97+ func yamlfmtBasicFormatter ( ) (* basic. BasicFormatter , error ) {
8998 factory := basic.BasicFormatterFactory {}
9099 config := map [string ]interface {}{
91- "indentless_arrays" : true ,
92- "line_ending" : "lf" , // prefer LF even on Windows
93- "pad_line_comments" : 2 ,
94- "retain_line_breaks" : true , // does not affect to the output because yq removes empty lines before formatting
100+ "indentless_arrays" : true ,
101+ "line_ending" : "lf" , // prefer LF even on Windows
102+ "pad_line_comments" : 2 ,
103+ "retain_line_breaks" : true ,
104+ "retain_line_breaks_single" : false ,
95105 }
106+
96107 formatter , err := factory .NewFormatter (config )
97108 if err != nil {
98109 return nil , err
99110 }
100- return formatter .Format (content )
101- }
102-
103- const yamlfmtLineBreakPlaceholder = "#magic___^_^___line"
104-
105- type paddinger struct {
106- strings.Builder
107- }
108-
109- func (p * paddinger ) adjust (txt string ) {
110- var indentSize int
111- for i := 0 ; i < len (txt ) && txt [i ] == ' ' ; i ++ { // yaml only allows space to indent.
112- indentSize ++
113- }
114- // Grows if the given size is larger than us and always return the max padding.
115- for diff := indentSize - p .Len (); diff > 0 ; diff -- {
116- p .WriteByte (' ' )
117- }
118- }
119-
120- func replaceLineBreaksWithMagicString (content []byte ) ([]byte , error ) {
121- // hotfix: yq does not support line breaks in the middle of a string.
122- var buf bytes.Buffer
123- reader := bytes .NewReader (content )
124- scanner := bufio .NewScanner (reader )
125- var padding paddinger
126- for scanner .Scan () {
127- txt := scanner .Text ()
128- padding .adjust (txt )
129- if strings .TrimSpace (txt ) == "" { // line break or empty space line.
130- buf .WriteString (padding .String ()) // prepend some padding incase literal multiline strings.
131- buf .WriteString (yamlfmtLineBreakPlaceholder )
132- buf .WriteString ("\n " )
133- } else {
134- buf .WriteString (txt )
135- buf .WriteString ("\n " )
136- }
111+ basicFormatter , ok := formatter .(* basic.BasicFormatter )
112+ if ! ok {
113+ return nil , fmt .Errorf ("unexpected formatter type: %T" , formatter )
137114 }
138- return buf . Bytes (), scanner . Err ()
115+ return basicFormatter , nil
139116}
0 commit comments