Skip to content

Remove a key safely

err := doc.Remove("server.debug")

Removal is the operation where a naive editor destroys work, because it has to decide which nearby comments belonged to the key. yamldoc decides by layout, and the rules are written down: see Comment ownership for the reasoning.

What goes, and what stays

server:
  host: localhost

  # ==== TLS ====          ← a blank line between it and the key: a section comment, survives
  tls_cert: /etc/cert.pem
  # the port we bind to    ← directly above a key: belongs to it, goes with it
  port: 8080               # inline: goes with it
  # end of the block       ← after the last entry: belongs to the mapping, survives

Removing server.port from that document takes the two comments that describe port and leaves the other two exactly where they can still be read, bytes untouched.

The dividing line is the blank line. A comment block with no blank line between it and the entry below belongs to that entry, even when a blank line sits above the comment. That is the rule that makes # the port we bind to go with port, and it means a heading you want to survive needs a blank line under it as well as above.

Emptiness is a value

Removing the last entry of a mapping leaves an empty mapping. The parent is never removed on your behalf:

feature:
  beta: true
other: 1
doc.Remove("feature.beta")
feature:
  {}
other: 1

The {} sits on the former child's line rather than on the parent's, so a comment that followed the last entry keeps its place and its owner.

Removing an item from a sequence

err := doc.Remove("servers[1]")

Inside Edit, the same reach is an IndexStep:

err := f.Edit(func(tx *yamldoc.Transaction) error {
    s, _ := tx.Snapshot()
    d, _ := s.Document(0)

    servers, err := d.Get("servers")
    if err != nil {
        return err
    }

    return tx.Remove(servers, yamldoc.IndexStep(1))
})

The removed item takes its own comments; the surviving items keep theirs, and comments that belong to the sequence itself stay.

Absent is an error

err := doc.Remove("server.nope")   // ErrNotFound

Silence there would hide a typo. A caller that means "make sure this is gone" tests for the sentinel:

if err := doc.Remove(path); err != nil && !errors.Is(err, yamldoc.ErrNotFound) {
    return err
}

Inside Edit, tx.RemoveIfPresent says the same thing in one call.

Refused removals

Removing an anchored value that an alias elsewhere still refers to is refused with ErrReference, and the document is left as it was. Remove or rewrite the alias first.