Fixing handling of CRLF #2352

This commit is contained in:
Mike Farah
2025-12-06 19:08:37 +11:00
parent 2072808def
commit eb3d0e63e3
3 changed files with 112 additions and 48 deletions
+69 -41
View File
@@ -11,6 +11,13 @@ import (
yaml "go.yaml.in/yaml/v4"
)
var (
commentLineRe = regexp.MustCompile(`^\s*#`)
yamlDirectiveLineRe = regexp.MustCompile(`^\s*%YAML`)
separatorLineRe = regexp.MustCompile(`^\s*---\s*$`)
separatorPrefixRe = regexp.MustCompile(`^\s*---\s+`)
)
type yamlDecoder struct {
decoder yaml.Decoder
@@ -33,51 +40,72 @@ func NewYamlDecoder(prefs YamlPreferences) Decoder {
}
func (dec *yamlDecoder) processReadStream(reader *bufio.Reader) (io.Reader, string, error) {
var commentLineRegEx = regexp.MustCompile(`^\s*#`)
var yamlDirectiveLineRegEx = regexp.MustCompile(`^\s*%YA`)
var sb strings.Builder
for {
peekBytes, err := reader.Peek(4)
if errors.Is(err, io.EOF) {
// EOF are handled else where..
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
} else if string(peekBytes[0]) == "\n" {
_, err := reader.ReadString('\n')
sb.WriteString("\n")
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
}
} else if string(peekBytes) == "--- " {
_, err := reader.ReadString(' ')
sb.WriteString("$yqDocSeparator$\n")
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
}
} else if string(peekBytes) == "---\n" {
_, err := reader.ReadString('\n')
sb.WriteString("$yqDocSeparator$\n")
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
}
} else if commentLineRegEx.MatchString(string(peekBytes)) || yamlDirectiveLineRegEx.MatchString(string(peekBytes)) {
line, err := reader.ReadString('\n')
sb.WriteString(line)
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
} else if err != nil {
return reader, sb.String(), err
}
} else {
line, err := reader.ReadString('\n')
if errors.Is(err, io.EOF) && line == "" {
// no more data
return reader, sb.String(), nil
}
if err != nil && !errors.Is(err, io.EOF) {
return reader, sb.String(), err
}
// Determine newline style and strip it for inspection
newline := ""
if strings.HasSuffix(line, "\r\n") {
newline = "\r\n"
line = strings.TrimSuffix(line, "\r\n")
} else if strings.HasSuffix(line, "\n") {
newline = "\n"
line = strings.TrimSuffix(line, "\n")
}
trimmed := strings.TrimSpace(line)
// Document separator: exact line '---' or a '--- ' prefix followed by content
if separatorLineRe.MatchString(trimmed) {
sb.WriteString("$yqDocSeparator$")
sb.WriteString(newline)
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
}
continue
}
// Handle lines that start with '--- ' followed by more content (e.g. '--- cat')
if separatorPrefixRe.MatchString(line) {
match := separatorPrefixRe.FindString(line)
remainder := line[len(match):]
// normalize separator newline: if original had none, default to LF
sepNewline := newline
if sepNewline == "" {
sepNewline = "\n"
}
sb.WriteString("$yqDocSeparator$")
sb.WriteString(sepNewline)
// push the remainder back onto the reader and continue processing
reader = bufio.NewReader(io.MultiReader(strings.NewReader(remainder), reader))
if errors.Is(err, io.EOF) && remainder == "" {
return reader, sb.String(), nil
}
continue
}
// Comments, YAML directives, and blank lines are leading content
if commentLineRe.MatchString(line) || yamlDirectiveLineRe.MatchString(line) || trimmed == "" {
sb.WriteString(line)
sb.WriteString(newline)
if errors.Is(err, io.EOF) {
return reader, sb.String(), nil
}
continue
}
// First non-leading line: push it back onto a reader and return
originalLine := line + newline
return io.MultiReader(strings.NewReader(originalLine), reader), sb.String(), nil
}
}