From 402674149ee22a8fdea9f148d3a889a42cc69eb4 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Sat, 29 Nov 2025 13:55:50 +0100 Subject: [PATCH] Do not break out quoted text in block formatting --- block.go | 34 ++++++++++++++++++++++++++-------- block_test.go | 14 ++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/block.go b/block.go index bb61186..53812ff 100644 --- a/block.go +++ b/block.go @@ -62,18 +62,28 @@ func splitsBlockLines(msg string, width int) ([]string, int) { for _, line := range strings.Split(msg, "\n") { line = strings.ReplaceAll(line, "\t", " ") lastLinePos := 0 + lastOpeningQuotePos := 0 inAnOpeningTag := false inAClosingTag := false inATagBody := false + inQuotes := false length := 0 var lastChar rune for pos, char := range line { - if char == '<' && lastChar != '\\' { - if len(line) > pos+1 && line[pos+1] == '/' { - inAClosingTag = true - inATagBody = false - } else { - inAnOpeningTag = true + if lastChar != '\\' { + switch char { + case '<': + if len(line) > pos+1 && line[pos+1] == '/' { + inAClosingTag = true + inATagBody = false + } else { + inAnOpeningTag = true + } + case '"': + inQuotes = !inQuotes + if inQuotes { + lastOpeningQuotePos = pos + } } } @@ -92,10 +102,18 @@ func splitsBlockLines(msg string, width int) ([]string, int) { } if length >= width && !inAClosingTag && !inAnOpeningTag && !inATagBody { + // if we cross the line boundary but are currently within a + // quoted text, we jump back just before the opening quote + // because we don't want to cut the text inside + if inQuotes && lastOpeningQuotePos > lastLinePos { + length = pos - lastOpeningQuotePos + pos = lastOpeningQuotePos - 1 + } else { + maxLen = width + length = 0 + } lines = append(lines, line[lastLinePos:pos+1]) - maxLen = width lastLinePos = pos + 1 - length = 0 } lastChar = char diff --git a/block_test.go b/block_test.go index fe5a332..2255e4b 100644 --- a/block_test.go +++ b/block_test.go @@ -45,6 +45,20 @@ func (ts *OutputBlockSuite) TestSplitsBlockLines(c *C) { c.Assert(maxLen, Equals, 3) } +func (ts *OutputBlockSuite) TestEnclosedWithQuotes(c *C) { + lines, maxLen := splitsBlockLines(`The "foo bar" file has moved to "a very long path". Please consider it.`, 40) + c.Assert(lines, DeepEquals, []string{`The "foo bar" file has moved to `, `"a very long path". Please consider it.`}) + c.Assert(maxLen, Equals, 38) + + lines, maxLen = splitsBlockLines(`The "foo bar" file has moved to "a very very very very very very very very very unterminated long path.`, 40) + c.Assert(lines, DeepEquals, []string{`The "foo bar" file has moved to `, `"a very very very very very very very ver`, `y very unterminated long path.`}) + c.Assert(maxLen, Equals, 40) + + lines, maxLen = splitsBlockLines(`This is an example of a message about a "/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/long/path" that needs to be moved to "/another/very/very/very/very/very/very/very/long/path"`, 107) + c.Assert(lines, DeepEquals, []string{`This is an example of a message about a `, `"/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very/long/path" that needs`, ` to be moved to "/another/very/very/very/very/very/very/very/long/path"`}) + c.Assert(maxLen, Equals, 107) +} + func (ts *OutputBlockSuite) TestSplitsBlockLinesDonotPanic(c *C) { lines, maxLen := splitsBlockLines("Foo Baz<", 4) c.Assert(lines, DeepEquals, []string{"Foo ", "Baz<"})