fix(parsing): end an inline item value at a sibling block entry
A block entry with an inline node recorded only the node's indent (one past
the dash) as a valid implicit level, not the dash's own indent. So a plain
scalar that is an inline sequence item value could not tell a sibling entry
that returns to the sequence indent from its own content, and a compact nested
sequence as an explicit key's value folded into one scalar: `? a` / `: - b` /
` - - c` / ` - d` became `{a: [b - - c - d]}` instead of `{a: [b, [c, d]]}`.
Record the entry's own indent as a sibling level too, so the continuation
stops there. Skip it under a pending explicit key, whose own sequence key
drives the implicit indentation differently (an explicit key whose key is a
sequence, `? - x`, must keep parsing). A block entry more indented than its
container is still plain content, so a wrapped value beginning a line with
`- ` folds in. The block-entry recording moved to a helper to keep the file
within its length limit.
Brings yaml-test-suite value-level conformance to 197/197 (100.0%) with the
parse-level and should-error rejection rates unchanged. 485 tests pass.
This commit is contained in:
parent
3b504a9c3a
commit
4b06f6e5fc
10
CHANGELOG.md
10
CHANGELOG.md
@ -84,6 +84,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A plain scalar that is an inline block sequence item value (`: - b`) now stops folding at a later
|
||||
block entry that returns to the sequence's own indent, so the dashes form the rest of the sequence
|
||||
rather than folding into the first item. A block entry only recorded its inline node's indent as a
|
||||
valid implicit level, not the dash's own indent, so a continuation could not tell a sibling entry
|
||||
from content, and `? a` / `: - b` / ` - - c` / ` - d` folded into one scalar. The entry's indent
|
||||
is now recorded as a sibling level too (except under an explicit key, whose sequence key drives
|
||||
implicit indentation itself), while a block entry more indented than its container stays plain
|
||||
content. Brings yaml-test-suite value-level conformance to 197/197 (100.0%), with the parse and
|
||||
should-error rates unchanged.
|
||||
|
||||
- A leading blank line of a block scalar now keeps any whitespace past the content indentation as
|
||||
content, as a blank line within the scalar already did. The leading blank lines (those before the
|
||||
first content line) were only counted, so an otherwise-blank `␣\t` line under a one-space content
|
||||
|
||||
@ -349,6 +349,21 @@ extension PureYAML.Parsing.Scanner {
|
||||
/// further key would form a forbidden inline nested mapping; in flow style several pairs per
|
||||
/// line are allowed, so that restriction does not apply. In flow a scalar that is the content of
|
||||
/// an open explicit key (`?`) is the key itself, not a new implicit key.
|
||||
/// Records the implicit indentation levels a block entry with an inline node introduces: the
|
||||
/// node's own indent (one past the dash), and the dash's indent as a sibling level so an inline
|
||||
/// item's plain value stops folding at a later entry that returns to it (`: -␣b` then ` - - c`).
|
||||
/// The sibling level is skipped under a pending explicit key (`? - x`), whose sequence key drives
|
||||
/// implicit indentation itself.
|
||||
func recordBlockEntryImplicitIndentation(start: PureYAML.Parsing.Mark, state: inout State) {
|
||||
guard hasNodeOnSameLine(afterCurrentPositionIn: state.reader) else {
|
||||
return
|
||||
}
|
||||
state.validImplicitIndentation.insert(start.column + 1)
|
||||
if !state.pendingExplicitKey {
|
||||
state.validImplicitIndentation.insert(start.column - 1)
|
||||
}
|
||||
}
|
||||
|
||||
func canSynthesizeImplicitBlockKey(_ state: State) -> Bool {
|
||||
if state.flowDepth > 0 {
|
||||
return state.flowContexts.last?.hasExplicitKey != true
|
||||
|
||||
@ -369,8 +369,8 @@ extension PureYAML.Parsing.Scanner {
|
||||
let start = state.reader.mark
|
||||
state.reader.advance()
|
||||
state.append(kind, mark: start, endMark: state.reader.mark)
|
||||
if case .blockEntry = kind, hasNodeOnSameLine(afterCurrentPositionIn: state.reader) {
|
||||
state.validImplicitIndentation.insert(start.column + 1)
|
||||
if case .blockEntry = kind {
|
||||
recordBlockEntryImplicitIndentation(start: start, state: &state)
|
||||
}
|
||||
if case .mappingValue = kind, state.flowContexts.last?.kind == .mapping {
|
||||
state.flowContexts[state.flowContexts.count - 1].isExpectingKey = false
|
||||
|
||||
34
Tests/InlineSequenceItemContinuationTests.swift
Normal file
34
Tests/InlineSequenceItemContinuationTests.swift
Normal file
@ -0,0 +1,34 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
/// A plain scalar that is an inline block sequence item value (`: - b`) stops folding when a later
|
||||
/// block entry returns to the sequence's own indent: those dashes form the rest of the sequence, not
|
||||
/// content of the first item. A block entry that is more indented than the sequence is still plain
|
||||
/// content of the scalar, so a wrapped value that happens to begin a line with `- ` folds in.
|
||||
@Suite("Inline sequence item continuation")
|
||||
struct InlineSequenceItemContinuationTests {
|
||||
@Test("An inline item scalar ends at a sibling block entry")
|
||||
func test_siblingEntryEndsItem() throws {
|
||||
// `: - b` then ` - c` is the two-item sequence `[b, c]`, not `[b - c]`.
|
||||
#expect(try PureYAML.parse(": - b\n - c\n") == .mapping(.init([
|
||||
.init(keyNode: .string(""), value: .sequence([.string("b"), .string("c")])),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A nested compact sequence as an explicit-key value parses with tabs")
|
||||
func test_explicitKeyNestedSequenceWithTabs() throws {
|
||||
#expect(try PureYAML.parse("? a\n: -\tb\n - -\tc\n - d\n") == .mapping(.init([
|
||||
.init(key: "a", value: .sequence([
|
||||
.string("b"),
|
||||
.sequence([.string("c"), .string("d")]),
|
||||
])),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A more-indented dash inside a wrapped value stays content")
|
||||
func test_moreIndentedDashIsContent() throws {
|
||||
#expect(try PureYAML.parse("- description: a query\n and 3\n - Other.\n") == .sequence([
|
||||
.mapping(.init([.init(key: "description", value: .string("a query and 3 - Other."))])),
|
||||
]))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user