From c0273f1f049aa66e4426023d8e43d68fbdbccbb6 Mon Sep 17 00:00:00 2001 From: bbaa <bbaa@bbaasite.cn> Date: Mon, 22 Jan 2024 10:24:49 +0800 Subject: [PATCH] caddyfile: Add heredoc support to `fmt` command (#6056) --- caddyconfig/caddyfile/formatter.go | 74 +++++++++++++++++++++++++ caddyconfig/caddyfile/formatter_test.go | 70 +++++++++++++++++++++++ caddyconfig/caddyfile/lexer.go | 2 +- 3 files changed, 145 insertions(+), 1 deletion(-) diff --git a/caddyconfig/caddyfile/formatter.go b/caddyconfig/caddyfile/formatter.go index 82581a3d3..18753c65e 100644 --- a/caddyconfig/caddyfile/formatter.go +++ b/caddyconfig/caddyfile/formatter.go @@ -31,6 +31,14 @@ func Format(input []byte) []byte { out := new(bytes.Buffer) rdr := bytes.NewReader(input) + type heredocState int + + const ( + heredocClosed heredocState = 0 + heredocOpening heredocState = 1 + heredocOpened heredocState = 2 + ) + var ( last rune // the last character that was written to the result @@ -47,6 +55,11 @@ func Format(input []byte) []byte { quoted bool // whether we're in a quoted segment escaped bool // whether current char is escaped + heredoc heredocState // whether we're in a heredoc + heredocEscaped bool // whether heredoc is escaped + heredocMarker []rune + heredocClosingMarker []rune + nesting int // indentation level ) @@ -75,6 +88,58 @@ func Format(input []byte) []byte { panic(err) } + // detect whether we have the start of a heredoc + if !quoted && !(heredoc != heredocClosed || heredocEscaped) && + space && last == '<' && ch == '<' { + write(ch) + heredoc = heredocOpening + space = false + continue + } + + if heredoc == heredocOpening { + if ch == '\n' { + if len(heredocMarker) > 0 && heredocMarkerRegexp.MatchString(string(heredocMarker)) { + heredoc = heredocOpened + } else { + heredocMarker = nil + heredoc = heredocClosed + nextLine() + continue + } + write(ch) + continue + } + if unicode.IsSpace(ch) { + // a space means it's just a regular token and not a heredoc + heredocMarker = nil + heredoc = heredocClosed + } else { + heredocMarker = append(heredocMarker, ch) + write(ch) + continue + } + } + // if we're in a heredoc, all characters are read&write as-is + if heredoc == heredocOpened { + write(ch) + heredocClosingMarker = append(heredocClosingMarker, ch) + if len(heredocClosingMarker) > len(heredocMarker) { + heredocClosingMarker = heredocClosingMarker[1:] + } + // check if we're done + if string(heredocClosingMarker) == string(heredocMarker) { + heredocMarker = nil + heredocClosingMarker = nil + heredoc = heredocClosed + } + continue + } + + if last == '<' && space { + space = false + } + if comment { if ch == '\n' { comment = false @@ -98,6 +163,9 @@ func Format(input []byte) []byte { } if escaped { + if ch == '<' { + heredocEscaped = true + } write(ch) escaped = false continue @@ -117,6 +185,7 @@ func Format(input []byte) []byte { if unicode.IsSpace(ch) { space = true + heredocEscaped = false if ch == '\n' { newLines++ } @@ -205,6 +274,11 @@ func Format(input []byte) []byte { write('{') openBraceWritten = true } + + if spacePrior && ch == '<' { + space = true + } + write(ch) beginningOfLine = false diff --git a/caddyconfig/caddyfile/formatter_test.go b/caddyconfig/caddyfile/formatter_test.go index 8e5b36860..6eec822fe 100644 --- a/caddyconfig/caddyfile/formatter_test.go +++ b/caddyconfig/caddyfile/formatter_test.go @@ -362,6 +362,76 @@ block { block { } +`, + }, + { + description: "keep heredoc as-is", + input: `block { + heredoc <<HEREDOC + Here's more than one space Here's more than one space + HEREDOC +} +`, + expect: `block { + heredoc <<HEREDOC + Here's more than one space Here's more than one space + HEREDOC +} +`, + }, + { + description: "Mixing heredoc with regular part", + input: `block { + heredoc <<HEREDOC + Here's more than one space Here's more than one space + HEREDOC + respond "More than one space will be eaten" 200 +} + +block2 { + heredoc <<HEREDOC + Here's more than one space Here's more than one space + HEREDOC + respond "More than one space will be eaten" 200 +} +`, + expect: `block { + heredoc <<HEREDOC + Here's more than one space Here's more than one space + HEREDOC + respond "More than one space will be eaten" 200 +} + +block2 { + heredoc <<HEREDOC + Here's more than one space Here's more than one space + HEREDOC + respond "More than one space will be eaten" 200 +} +`, + }, + { + description: "Heredoc as regular token", + input: `block { + heredoc <<HEREDOC "More than one space will be eaten" +} +`, + expect: `block { + heredoc <<HEREDOC "More than one space will be eaten" +} +`, + }, + { + description: "Escape heredoc", + input: `block { + heredoc \<<HEREDOC + respond "More than one space will be eaten" 200 +} +`, + expect: `block { + heredoc \<<HEREDOC + respond "More than one space will be eaten" 200 +} `, }, } { diff --git a/caddyconfig/caddyfile/lexer.go b/caddyconfig/caddyfile/lexer.go index e5026738b..763afb6c2 100644 --- a/caddyconfig/caddyfile/lexer.go +++ b/caddyconfig/caddyfile/lexer.go @@ -313,7 +313,7 @@ func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) { // iterate over each line and strip the whitespace from the front var out string for lineNum, lineText := range lines[:len(lines)-1] { - if lineText == "" { + if lineText == "" || lineText == "\r" { out += "\n" continue }