Comment ownership¶
Removing a key raises a question the parse tree cannot answer: which nearby comments belonged to it?
Get it wrong in one direction and you destroy a section heading that described ten other keys. Get it wrong in the other and you leave comments describing things that no longer exist.
The problem¶
These two documents put the comment in the same place relative to the key below it:
server:
host: localhost
# ==== TLS SECTION ==== ← describes everything below
tls_cert: /etc/cert.pem
What distinguishes them is layout: the blank lines. No parser can tell from the
words which comment describes what, and yamldoc does not try. It reads the
layout, applies a written rule, and gives every comment exactly one owner:
the stream, a document, a collection, a mapping entry or a sequence item.
The rules¶
The rules are decisions in spec 0008 (D39 to D41, D48 to D56 and D113 to D125), with an exact fixture for each. The everyday ones:
| Comment | Belongs to | On removal of the entry below |
|---|---|---|
| A block of comment lines directly above an entry, no blank line between them | the entry | removed with it |
| A block of comment lines with a blank line between it and the entry below | the containing mapping, as a section comment | survives, bytes untouched |
| On the entry's own line | the entry | removed with it |
| After the last entry of a block mapping, at the entries' indentation | the mapping | survives, and stays with the mapping even when it empties |
| Inside a flow collection after a separator | the entry or item before it | removed with that entry |
On a --- or ... line |
the document | survives root replacement |
| Before the first document, or between documents | the stream | survives everything |
The dividing line is the blank line between comment and entry. A blank line above the comment does not detach it from the entry below; the fixture in D39 is exactly that shape. A heading you want to keep needs a blank line under it as well.
Sequence items follow the same rules with the dash line as the entry line. An entry whose value is a collection also owns a comment on the line that opens or closes that collection, so replacing the value with a scalar keeps the comment on the line.
The trailing case¶
A tree-based editor attaches that comment to port, because port is the
last entry, and removing port takes it. Here it belongs to the mapping.
Removing port leaves it in place, and removing every entry leaves the
mapping's {} marker on the former child line so the comment still has a
mapping to belong to.
How ownership is enforced¶
Every transaction records the owner of every comment before it starts, and
the re-parsed result is checked against that record before it commits: each
retained comment must still be there, once, with the same owner. An edit that
would move a comment to a different owner as a side effect fails with
ErrPreservation rather than committing, and the file is left as it was.
That is why a scalar with an inline comment becomes a flow collection on the
same line rather than a block one below it: the block form would hand the
comment to the first new entry.
Where an edit's own footprint genuinely cannot keep every comment in place, the library refuses rather than guesses. Orphaning a comment is untidy and immediately visible; destroying one loses work the author did, silently, and they may not notice for months. That asymmetry decides the tie, and it is why the answer to an unrepresentable placement is an error and not a best effort.
When the owner a layout implies is not the one you want, say so: Move or copy a comment gives a comment a new owner as a command of its own, checked the same way.