From b87c1503f4cb577a78d8cccd556b9098a96302e7 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Thu, 27 Oct 2022 16:16:00 +1100 Subject: [PATCH] wip --- acceptance_tests/empty.sh | 2 +- examples/empty-no-comment.yaml | 0 pkg/yqlib/decoder_yaml.go | 19 ++++++++++++++++++- pkg/yqlib/doc/usage/xml.md | 6 +++++- pkg/yqlib/encoder_xml.go | 20 ++++++++++++++++---- pkg/yqlib/operator_load_test.go | 10 +++++++++- pkg/yqlib/printer.go | 2 +- pkg/yqlib/stream_evaluator.go | 1 + pkg/yqlib/xml_test.go | 8 ++++++-- 9 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 examples/empty-no-comment.yaml diff --git a/acceptance_tests/empty.sh b/acceptance_tests/empty.sh index 7ffb813f..63d5ca80 100755 --- a/acceptance_tests/empty.sh +++ b/acceptance_tests/empty.sh @@ -9,7 +9,7 @@ EOL testEmptyEval() { X=$(./yq e test.yml) - expected=$(cat test.yml) + expected="# comment" assertEquals 0 $? assertEquals "$expected" "$X" } diff --git a/examples/empty-no-comment.yaml b/examples/empty-no-comment.yaml new file mode 100644 index 00000000..e69de29b diff --git a/pkg/yqlib/decoder_yaml.go b/pkg/yqlib/decoder_yaml.go index f18ac176..d5442d77 100644 --- a/pkg/yqlib/decoder_yaml.go +++ b/pkg/yqlib/decoder_yaml.go @@ -15,6 +15,7 @@ type yamlDecoder struct { // work around of various parsing issues by yaml.v3 with document headers prefs YamlPreferences leadingContent string + readAnything bool } func NewYamlDecoder(prefs YamlPreferences) Decoder { @@ -64,6 +65,7 @@ func (dec *yamlDecoder) Init(reader io.Reader) error { } } dec.leadingContent = leadingContent + dec.readAnything = false dec.decoder = *yaml.NewDecoder(readerToUse) return nil } @@ -72,7 +74,12 @@ func (dec *yamlDecoder) Decode() (*CandidateNode, error) { var dataBucket yaml.Node err := dec.decoder.Decode(&dataBucket) - if err != nil { + if errors.Is(err, io.EOF) && dec.leadingContent != "" && !dec.readAnything { + // force returning an empty node with a comment. + dec.readAnything = true + return dec.blankNodeWithComment(), nil + + } else if err != nil { return nil, err } @@ -90,3 +97,13 @@ func (dec *yamlDecoder) Decode() (*CandidateNode, error) { dataBucket.FootComment = "" return candidateNode, nil } + +func (dec *yamlDecoder) blankNodeWithComment() *CandidateNode { + return &CandidateNode{ + Document: 0, + Filename: "", + Node: &yaml.Node{Kind: yaml.DocumentNode, Content: []*yaml.Node{{Tag: "!!null", Kind: yaml.ScalarNode}}}, + FileIndex: 0, + LeadingContent: dec.leadingContent, + } +} diff --git a/pkg/yqlib/doc/usage/xml.md b/pkg/yqlib/doc/usage/xml.md index b5241765..a1d535b1 100644 --- a/pkg/yqlib/doc/usage/xml.md +++ b/pkg/yqlib/doc/usage/xml.md @@ -386,6 +386,7 @@ A best attempt is made to copy comments to xml. Given a sample.yml file of: ```yaml +# header comment # above_cat cat: # inline_cat # above_array @@ -402,7 +403,10 @@ yq -o=xml '.' sample.yml ``` will output ```xml - + val1 val2 diff --git a/pkg/yqlib/encoder_xml.go b/pkg/yqlib/encoder_xml.go index 5d76194d..1450f495 100644 --- a/pkg/yqlib/encoder_xml.go +++ b/pkg/yqlib/encoder_xml.go @@ -4,24 +4,28 @@ import ( "encoding/xml" "fmt" "io" + "regexp" "strings" yaml "gopkg.in/yaml.v3" ) type xmlEncoder struct { - indentString string - writer io.Writer - prefs XmlPreferences + indentString string + writer io.Writer + prefs XmlPreferences + leadingContent string } +var commentPrefix = regexp.MustCompile(`(^|\n)\s*#`) + func NewXMLEncoder(indent int, prefs XmlPreferences) Encoder { var indentString = "" for index := 0; index < indent; index++ { indentString = indentString + " " } - return &xmlEncoder{indentString, nil, prefs} + return &xmlEncoder{indentString, nil, prefs, ""} } func (e *xmlEncoder) CanHandleAliases() bool { @@ -33,6 +37,7 @@ func (e *xmlEncoder) PrintDocumentSeparator(writer io.Writer) error { } func (e *xmlEncoder) PrintLeadingContent(writer io.Writer, content string) error { + e.leadingContent = commentPrefix.ReplaceAllString(content, "\n") return nil } @@ -42,6 +47,13 @@ func (e *xmlEncoder) Encode(writer io.Writer, node *yaml.Node) error { e.writer = writer encoder.Indent("", e.indentString) + if e.leadingContent != "" { + err := e.encodeComment(encoder, e.leadingContent) + if err != nil { + return err + } + } + switch node.Kind { case yaml.MappingNode: err := e.encodeTopLevelMap(encoder, node) diff --git a/pkg/yqlib/operator_load_test.go b/pkg/yqlib/operator_load_test.go index 619b9685..67a73e15 100644 --- a/pkg/yqlib/operator_load_test.go +++ b/pkg/yqlib/operator_load_test.go @@ -7,8 +7,16 @@ import ( var loadScenarios = []expressionScenario{ { skipDoc: true, - description: "Load empty file", + description: "Load empty file with a comment", expression: `load("../../examples/empty.yaml")`, + expected: []string{ + "D0, P[], (doc)::# comment\n\n", + }, + }, + { + skipDoc: true, + description: "Load empty file with no comment", + expression: `load("../../examples/empty-no-comment.yaml")`, expected: []string{ "D0, P[], (!!null)::\n", }, diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index 78b32c50..aef082d9 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -111,7 +111,7 @@ func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error { mappedDoc := el.Value.(*CandidateNode) log.Debug("-- print sep logic: p.firstTimePrinting: %v, previousDocIndex: %v, mappedDoc.Document: %v", p.firstTimePrinting, p.previousDocIndex, mappedDoc.Document) - + log.Debug("%v", NodeToString(mappedDoc)) writer, errorWriting := p.printerWriter.GetWriter(mappedDoc) if errorWriting != nil { return errorWriting diff --git a/pkg/yqlib/stream_evaluator.go b/pkg/yqlib/stream_evaluator.go index 8229e8a7..ef78ca98 100644 --- a/pkg/yqlib/stream_evaluator.go +++ b/pkg/yqlib/stream_evaluator.go @@ -75,6 +75,7 @@ func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, p } if totalProcessDocs == 0 { + // problem is I've already slurped the leading content sadface return s.EvaluateNew(expression, printer) } diff --git a/pkg/yqlib/xml_test.go b/pkg/yqlib/xml_test.go index 28d83341..c2e804c0 100644 --- a/pkg/yqlib/xml_test.go +++ b/pkg/yqlib/xml_test.go @@ -137,7 +137,8 @@ in d before --> ` -const yamlWithComments = `# above_cat +const yamlWithComments = `# header comment +# above_cat cat: # inline_cat # above_array array: # inline_array @@ -147,7 +148,10 @@ cat: # inline_cat # below_cat ` -const expectedXMLWithComments = ` +const expectedXMLWithComments = ` val1 val2