Exclude inline comments from raw value reads
rawValue and the coercion/malformed paths now stop at the quote-aware inline-comment boundary, so 'title: 2048 # note' coerces to "2048" and a read-then-write no longer compounds the comment (pinned idempotent). Strictly read-side; round-trip bytes untouched. +6 tests (124 total). Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
This commit is contained in:
@@ -459,6 +459,35 @@ struct FrontmatterAccessTests {
|
||||
#expect(document.rawValue(for: "labels") == "[a, b, c]")
|
||||
#expect(document.rawValue(for: "schema") == "1")
|
||||
}
|
||||
|
||||
/// A trailing comment belongs to the line, not to the value — reads stop at it.
|
||||
@Test func rawValueStopsAtAnInlineComment() throws {
|
||||
let document = try FrontmatterDocument.parse(Fixture.rich)
|
||||
#expect(document.rawValue(for: "project") == "lanework")
|
||||
}
|
||||
|
||||
/// …but a `#` inside a quoted scalar is value text, and a multi-line value is never scanned.
|
||||
@Test func rawValueKeepsAHashThatIsNotAComment() throws {
|
||||
let text = """
|
||||
---
|
||||
quoted: "hash # inside"
|
||||
single: 'hash # inside'
|
||||
both: "hash # inside" # and a real one
|
||||
flow: [a, "b # c"] # note
|
||||
block: |
|
||||
text # not a comment out here either
|
||||
---
|
||||
body
|
||||
|
||||
"""
|
||||
let document = try FrontmatterDocument.parse(text)
|
||||
#expect(document.rawValue(for: "quoted") == "\"hash # inside\"")
|
||||
#expect(document.rawValue(for: "single") == "'hash # inside'")
|
||||
#expect(document.rawValue(for: "both") == "\"hash # inside\"")
|
||||
#expect(document.rawValue(for: "flow") == "[a, \"b # c\"]")
|
||||
#expect(document.rawValue(for: "block") == "|\n text # not a comment out here either")
|
||||
#expect(document.serialized() == text)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Strict fields
|
||||
@@ -526,6 +555,32 @@ struct FrontmatterLenientFieldTests {
|
||||
#expect(try document("icon: 2026-07-26T16:41:38Z").icon == .valid("2026-07-26T16:41:38Z"))
|
||||
}
|
||||
|
||||
/// The comment on a value line belongs to the line, not to the value: the source text a
|
||||
/// wrong-typed scalar coerces to stops where the comment starts.
|
||||
@Test func aTrailingCommentIsNotPartOfACoercedValue() throws {
|
||||
#expect(try document("title: 2048 # note").title == .valid("2048"))
|
||||
#expect(try document("title: true # note").title == .valid("true"))
|
||||
#expect(try document("background: 42\t# tabbed").background == .valid("42"))
|
||||
#expect(try document("icon: 2026-07-26T16:41:38Z # when").icon == .valid("2026-07-26T16:41:38Z"))
|
||||
}
|
||||
|
||||
/// A `#` inside a quoted scalar is value text — the quotes are what make `#ff8800` writable
|
||||
/// at all, so a read must not treat it as a comment.
|
||||
@Test func aHashInsideAQuotedValueIsNotTrimmed() throws {
|
||||
#expect(try document("title: \"2048 # note\"").title == .valid("2048 # note"))
|
||||
#expect(try document("background: \"#ff8800\"").background == .valid("#ff8800"))
|
||||
#expect(try document("background: \"#ff8800\" # brand orange").background == .valid("#ff8800"))
|
||||
}
|
||||
|
||||
/// A sequence or mapping has no scalar reading at all; the raw text it falls back to stops at
|
||||
/// the comment like every other read.
|
||||
@Test func malformedRawStopsAtAnInlineComment() throws {
|
||||
#expect(try document("background: [red, blue] # a palette").background == .malformed(raw: "[red, blue]"))
|
||||
#expect(try document("title: {a: 1} # a mapping").title == .malformed(raw: "{a: 1}"))
|
||||
#expect(try document("width: wide # roughly").width == .malformed(raw: "wide"))
|
||||
#expect(try document("order: banana # not a number").order == .malformed(raw: "banana"))
|
||||
}
|
||||
|
||||
@Test func quotedStringLenientValuesAreUnaffectedByCoercion() throws {
|
||||
#expect(try document("title: \"2048\"").title == .valid("2048"))
|
||||
#expect(try document("title: \"true\"").title == .valid("true"))
|
||||
@@ -774,6 +829,20 @@ struct FrontmatterInlineCommentTests {
|
||||
#expect(document.serialized() == "---\norder: 8192 # keep at top\n---\nbody\n")
|
||||
}
|
||||
|
||||
/// Reading a value and writing it straight back must leave one comment, not two — the read
|
||||
/// stops at the comment, the write re-splices it.
|
||||
@Test func aReadThenWriteDoesNotCompoundTheComment() throws {
|
||||
var document = try FrontmatterDocument.parse("---\ntitle: 2048 # note\n---\nbody\n")
|
||||
#expect(document.title == .valid("2048"))
|
||||
|
||||
document.set("title", to: .string(document.title.value ?? ""))
|
||||
#expect(document.serialized() == "---\ntitle: \"2048\" # note\n---\nbody\n")
|
||||
|
||||
document.set("title", to: .string(document.title.value ?? ""))
|
||||
#expect(document.serialized() == "---\ntitle: \"2048\" # note\n---\nbody\n")
|
||||
#expect(try FrontmatterDocument.parse(document.serialized()).title == .valid("2048"))
|
||||
}
|
||||
|
||||
@Test func theCommentSurvivesAReparse() throws {
|
||||
var document = try FrontmatterDocument.parse("---\norder: 3072 # keep at top\n---\nbody\n")
|
||||
document.set("order", to: .double(4096))
|
||||
|
||||
Reference in New Issue
Block a user