Move or copy a comment¶
A comment follows its owner through every edit (see
Comment ownership). When the owner is
going away, or the note describes something else after a reorganisation,
ChangeComments gives it a new owner on purpose, and ReplaceWithComments
does the same while it replaces a node.
err := f.Edit(func(tx *yamldoc.Transaction) error {
// notes: comments read from the current working snapshot
// owner: the CommentOwner they should belong to
return tx.ChangeComments(yamldoc.CommentChanges{
Moves: []yamldoc.CommentMove{{Sources: notes, To: yamldoc.AtOwner(owner, yamldoc.Leading())}},
})
})
Moving a note before its owner goes¶
Replacing server with a scalar removes retries and its note with it. Move
the note to server first, in the same transaction, and it survives:
The complete program is
the ChangeComments example.
Both commands commit together or not at all.
Selecting comments and owners¶
Read the comments from the snapshot you are editing: Entry.Comments,
Node.ItemComments, Node.Comments, Document.Comments and
Snapshot.Comments. Each returns the comments that owner holds, in source order.
Two comments with the same text are two occurrences, and the handle you
pass is the one that moves.
The destination is a CommentOwner: Entry.CommentOwner,
Node.ItemCommentOwner(i), or Node.CommentOwner for a mapping or sequence
itself. Like a Node, every handle belongs to one
working revision, so take tx.Snapshot() again before each command. A
handle from an earlier revision is ErrStale.
Where a comment can go¶
A comment can go to a mapping entry or sequence item, to the tail of a block mapping or sequence, or beside a document marker (below), in the same document or another document of the same file. A source can be any comment, including one at the end of a line, a file header, or a note between documents.
| Placement | Owner | Lands |
|---|---|---|
Leading() |
entry, item | After the owner's existing leading comments, directly above its key or dash |
BeforeComment(c) |
entry, item | Directly above c, a standalone comment the same owner holds |
AfterComment(c) |
entry, item | Directly below c |
Inline() |
entry, item | At the end of the owner's line: after a scalar value, after a block scalar's header, or after the key or dash of a block collection |
Tail() |
block mapping or sequence | After its last member and any comments already at its tail |
server: # Inline() on the server entry
port: 8080 # Inline() on the port entry
# Tail() on the server mapping
Comments take the owner's indentation and the file's line endings. A comment moved to the end of a line whose own comment moves away in the same command keeps that comment's spacing.
Documents and the stream¶
Five destinations sit beside document markers rather than at an owner, and
each needs its marker to be in the file already: a comment command never
adds a --- or ....
| Destination | Owner | Lands |
|---|---|---|
AtStartMarker(doc) |
the document | At the end of its --- line; one comment |
AtEndMarker(doc) |
the document | At the end of its ... line; one comment |
InDocumentGap(before, after) |
the stream | Between before's ... and after's directives or ---, after any notes already there |
InStreamHeader(snapshot) |
the stream | Before the first document's directives or ---, after the header already there |
InStreamFooter(snapshot) |
the stream | At the end of the file, after the last document's ... and the footer already there |
# InStreamHeader
--- # AtStartMarker(first)
retries: 3
... # AtEndMarker(first)
# InDocumentGap(first, second)
---
log_level: info
...
# InStreamFooter
``` A group
lands in the order it had in the file, whatever order you pass the handles
in. A blank line that separated the comments does not travel with them.
## Moving and copying
A `CommentMove` takes the selected comments away from where they are. A
`CommentCopy` leaves them and writes new ones at every destination it names.
One command can do both:
```go
yamldoc.CommentChanges{
Moves: []yamldoc.CommentMove{{Sources: notes, To: yamldoc.AtOwner(relocated, yamldoc.Leading())}},
Copies: []yamldoc.CommentCopy{{Sources: notes, To: []yamldoc.Destination{yamldoc.AtOwner(replica, yamldoc.Leading())}}},
}
Passing the same handle twice selects it once, and the same destination twice in one copy writes it once. Two copy commands write two copies. An empty selection succeeds and changes nothing, so a filter that matched no comments needs no special case.
What is refused¶
Nothing changes when a command is refused, and the transaction cannot commit afterwards.
| Request | Error |
|---|---|
| A copy with no destination, a move with none, or one comment moved to two places | ErrCommand |
| Two groups for the same place in one command | ErrPlacement: their order would be a guess, so send two commands in the order you want |
| A move anchored on a comment that the same command moves | ErrPlacement |
| An anchor another owner holds, or one at the end of a line | ErrPlacement |
| An entry that shares its line with something else, such as the first key after a sequence dash or a key inside a one-line flow mapping | ErrPlacement: a comment on that line would belong to another owner |
Inline() for more than one comment, or on a line that already ends in a comment that is staying |
ErrPlacement: a line has one end |
Tail() on a flow collection, or on an entry or item |
ErrPlacement |
| A container's tail and the next entry's documentation in one command | ErrPlacement: they are the same place in the file, so send two commands |
AtOwner with a document or the stream as the owner |
ErrPlacement: use one of the destinations beside a marker |
A marker, gap, header or footer whose --- or ... is not in the file |
ErrPlacement |
AtStartMarker or AtEndMarker for more than one comment, or on a marker line that already holds a comment that is staying, or a value |
ErrPlacement |
InDocumentGap for documents that are not neighbours in that order |
ErrPlacement |
Replacing a node and keeping its comments¶
A replacement drops the comments its old value owned, and a scalar with a
comment on its line keeps that comment by turning into a flow collection.
ReplaceWithComments replaces a node and moves or copies comments in one
command, and InReplacement names an owner inside the value being written:
to := yamldoc.InReplacement([]yamldoc.Step{yamldoc.StringStep("retries")}, yamldoc.CommentOnMappingEntry, yamldoc.Inline())
err := tx.ReplaceWithComments(root, map[string]any{"retries": 5}, yamldoc.CommentChanges{
Moves: []yamldoc.CommentMove{{Sources: notes, To: to}},
})
becomes
The path runs from the value being written. With CommentOnMappingEntry or
CommentOnSequenceItem its last step names the entry or item; with
CommentOnContainer the path names the mapping or sequence itself, and an
empty path is the value. Any placement but BeforeComment and
AfterComment works, since the new value holds no comments to anchor on.
Destinations outside the replacement work as in ChangeComments.
Moved comments leave before the value is written, so they no longer hold it
to the flow form, and every step is checked before anything commits. A path
the value does not have, a role it does not fit, or an owner the replacement
removes is ErrPlacement; InReplacement anywhere but ReplaceWithComments
is ErrCommand. The complete program is
the ReplaceWithComments example.