fix(parsing): keep both breaks at a folded blank by a more-indented line
In a folded block scalar, a blank run of N lines between two non-more-indented lines folds to N line breaks: the fold absorbs the first break. Next to a more-indented line nothing folds, so that break survives and the run yields N+1 breaks. The folder used N in every case, so paragraph spacing around an indented block collapsed: a blank line before a more-indented block came out as one break instead of two. Track whether the line before the blank run, or the one after it, is more-indented and add the extra break in that case. Folding between plain lines and runs with no blank lines are unchanged. Raises yaml-test-suite value-level conformance to 186/194 (95.9%) with the parse-level and should-error rejection rates unchanged. 444 tests pass.
This commit is contained in:
parent
246e6b1891
commit
37ac4d862d
@ -84,6 +84,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A blank line next to a more-indented line in a folded block scalar (`>`) now keeps both line
|
||||
breaks. Between two non-more-indented lines a blank run of N folds to N breaks (the fold is
|
||||
absorbed into the first), but next to a more-indented line nothing folds, so the absorbed break
|
||||
survives and the run yields N+1 breaks. The folder counted N breaks in every case, collapsing the
|
||||
paragraph spacing around indented blocks. Raises yaml-test-suite value-level conformance to 186/194
|
||||
(95.9%), with the parse and should-error rates unchanged.
|
||||
|
||||
- Plain scalar line folding now strips tabs, not only spaces, around continuation lines. A tab is not
|
||||
indentation, so a continuation line's leading tab (`\t3rd`) is folding white space and folds away
|
||||
rather than entering the value, and a line of only white space that holds a tab (`␣␣\t`) is an empty
|
||||
|
||||
@ -281,14 +281,18 @@ extension PureYAML.Parsing.TokenEventParser {
|
||||
var index = firstContent + 1
|
||||
while index < lines.count {
|
||||
if lines[index].isEmpty {
|
||||
// A run of N blank lines folds to N line breaks: the first break
|
||||
// is absorbed, the rest are literal.
|
||||
// Between two non-more-indented lines a blank run of N folds to N line breaks: the
|
||||
// first break is the absorbed fold, the rest are literal. Next to a more-indented line
|
||||
// nothing folds, so the absorbed break survives too and the run yields N+1 breaks.
|
||||
let beforeMoreIndented = isMoreIndentedBlockLine(lines[index - 1])
|
||||
var blankCount = 0
|
||||
while index < lines.count, lines[index].isEmpty {
|
||||
blankCount += 1
|
||||
index += 1
|
||||
}
|
||||
result += String(repeating: "\n", count: blankCount)
|
||||
let afterMoreIndented = index < lines.count && isMoreIndentedBlockLine(lines[index])
|
||||
let breaks = beforeMoreIndented || afterMoreIndented ? blankCount + 1 : blankCount
|
||||
result += String(repeating: "\n", count: breaks)
|
||||
if index < lines.count {
|
||||
result += lines[index]
|
||||
index += 1
|
||||
|
||||
35
Tests/FoldedScalarBlankLineTests.swift
Normal file
35
Tests/FoldedScalarBlankLineTests.swift
Normal file
@ -0,0 +1,35 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
/// In a folded block scalar (`>`), a single line break between two non-more-indented lines folds to a
|
||||
/// space, and a blank run of N lines between them yields N line breaks (the fold is absorbed). Next to
|
||||
/// a more-indented line nothing folds, so the absorbed break survives and the run yields N+1 breaks;
|
||||
/// more-indented lines keep their own line breaks.
|
||||
@Suite("Folded scalar blank lines")
|
||||
struct FoldedScalarBlankLineTests {
|
||||
@Test("A blank line next to a more-indented line keeps both breaks")
|
||||
func test_blankNextToMoreIndented() throws {
|
||||
let input = ">\n Sammy Sosa completed another\n fine season with great stats.\n\n"
|
||||
+ " 63 Home Runs\n 0.288 Batting Average\n\n What a year!\n"
|
||||
#expect(try PureYAML.parse(input) == .string(
|
||||
"Sammy Sosa completed another fine season with great stats.\n\n"
|
||||
+ " 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n",
|
||||
))
|
||||
}
|
||||
|
||||
@Test("A blank line between non-more-indented lines folds one break away")
|
||||
func test_blankBetweenPlainLines() throws {
|
||||
// `folded`+`line` fold to a space; the blank yields one break; the more-indented bullets keep
|
||||
// their breaks and the blank around them keeps both.
|
||||
let input = ">\n\n folded\n line\n\n next\n line\n * bullet\n\n"
|
||||
+ " * list\n * lines\n\n last\n line\n\n# Comment\n"
|
||||
#expect(try PureYAML.parse(input) == .string(
|
||||
"\nfolded line\nnext line\n * bullet\n\n * list\n * lines\n\nlast line\n",
|
||||
))
|
||||
}
|
||||
|
||||
@Test("A plain folded scalar with no blank lines is unchanged")
|
||||
func test_noBlankLines() throws {
|
||||
#expect(try PureYAML.parse(">\n folded\n text\n") == .string("folded text\n"))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user