fix(parsing): fold tabs in plain scalar continuation lines
Plain scalar line folding strips white space around continuation lines, but the scanner stripped only spaces, so tabs leaked in. A tab is not indentation, so the indentation scan left a continuation line's leading tab in place and it joined the value (`\t3rd` folded as `\t3rd`, not `3rd`), and a line of only white space holding a tab (` \t`) was treated as a non-blank continuation, folding to a space rather than the line break an empty line gives. Skip leading white space, tabs included, before a continuation line's content, and treat a whitespace-only line that holds a tab as blank when counting the folded line breaks. Raises yaml-test-suite value-level conformance to 183/194 (94.3%) with the parse-level and should-error rejection rates unchanged. 441 tests pass.
This commit is contained in:
parent
4b0dc9375e
commit
246e6b1891
@ -84,6 +84,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- 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
|
||||
line that folds to a line break, not an extra space. Raises yaml-test-suite value-level conformance
|
||||
to 183/194 (94.3%), with the parse and should-error rates unchanged.
|
||||
|
||||
- A tab in block scalar content past the block's indentation is now kept verbatim instead of being
|
||||
re-expanded to spaces. A block scalar is indented with spaces only, so a line like `␣\ttext` under a
|
||||
one-space indent strips the space and keeps the tab, yielding `\ttext`; the scanner measured the
|
||||
|
||||
@ -190,8 +190,10 @@ extension PureYAML.Parsing.Scanner {
|
||||
var count = 0
|
||||
while true {
|
||||
var blankProbe = probe
|
||||
// A line of only white space is empty for folding, including one that holds a tab: a tab
|
||||
// is not indentation, but a whitespace-only line is still blank, so consume it here.
|
||||
_ = blankProbe.consume { character in
|
||||
character == " "
|
||||
character == " " || character == "\t"
|
||||
}
|
||||
guard isLineBreak(blankProbe.peek()) else {
|
||||
break
|
||||
@ -223,6 +225,12 @@ extension PureYAML.Parsing.Scanner {
|
||||
}
|
||||
|
||||
func scanPlainScalarContinuationLine(_ state: inout State) -> String {
|
||||
// Leading white space beyond the line's indentation folds away in a plain scalar. A tab is
|
||||
// not indentation, so the indentation scan leaves it here; skip it (and any spaces after it)
|
||||
// so a continuation line like `\t3rd` joins as `3rd`, not `\t3rd`.
|
||||
while let character = state.reader.peek(), character == " " || character == "\t" {
|
||||
state.reader.advance()
|
||||
}
|
||||
var value = ""
|
||||
while let character = state.reader.peek(), !isLineBreak(character) {
|
||||
if character == " ", state.reader.peek(offset: 1) == "#" {
|
||||
|
||||
28
Tests/PlainScalarTabFoldingTests.swift
Normal file
28
Tests/PlainScalarTabFoldingTests.swift
Normal file
@ -0,0 +1,28 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
/// Plain scalar line folding strips white space, including tabs, around continuation lines: a tab is
|
||||
/// not indentation, so the leading tab of a continuation line folds away, and a line of only white
|
||||
/// space (even one holding a tab) is an empty line that folds to a line break, not a space.
|
||||
@Suite("Plain scalar tab folding")
|
||||
struct PlainScalarTabFoldingTests {
|
||||
@Test("A leading tab on a continuation line folds away")
|
||||
func test_leadingTabFoldsToSpace() throws {
|
||||
#expect(try PureYAML.parse("1st non-empty\n\n 2nd non-empty \n\t3rd non-empty\n")
|
||||
== .string("1st non-empty\n2nd non-empty 3rd non-empty"))
|
||||
}
|
||||
|
||||
@Test("A whitespace-only line holding a tab is an empty line, folding to a line break")
|
||||
func test_whitespaceOnlyTabLineIsBlank() throws {
|
||||
#expect(try PureYAML.parse("key:\n value\n with\n \t\n tabs\n") == .mapping(.init([
|
||||
.init(key: "key", value: .string("value with\ntabs")),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A tab after the indentation of a continuation line folds away")
|
||||
func test_tabAfterIndentFolds() throws {
|
||||
#expect(try PureYAML.parse("x:\n - x\n \tx\n") == .mapping(.init([
|
||||
.init(key: "x", value: .sequence([.string("x x")])),
|
||||
])))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user