Skip to content

feat(drive): structural and index-addressed Docs edits (deletion, insert-at-index, styling) #1642

Description

@newhoggy

Summary

ADR-0076 §12 defers everything in the Docs write
surface beyond replaceAllText and an append: deleteContentRange, arbitrary
--index insertion, text and paragraph styling, tables, lists, headers and
footers, and named-range manipulation.

The Docs counterpart of #1613/#1623 (Sheets structural edits and deletion).

The cut is one line, not a list

Every deferred item requires this crate to own the Docs index model, which
§5 exists to avoid. That is the whole boundary:

  • Docs indices are UTF-16 code units; Rust strings are UTF-8. Any index
    computed here is wrong for astral-plane characters in a way that corrupts
    silently rather than failing — invisible in every ASCII test.
  • Inserting at a segment's endIndex is rejected, because the last
    character is the segment terminator. That off-by-one is the most common
    Docs API bug.

Today neither can happen: insertText is addressed with
endOfSegmentLocation and the server locates the end, so no UTF-16
arithmetic exists anywhere in this crate
. The first verb that takes an
--index gives that up, and everything above comes back at once. Whoever
picks this up is paying that entry cost — it should be a deliberate decision,
not a side effect of adding a verb.

Questions to settle

  • Does deletion need its own gate operation? Almost certainly yes, and
    DriveOperation::DocsWrite's own doc comment says so where a delete verb's
    author will read it: an operator who granted docs-write for "may replace
    text" did not thereby consent to content removal. Same argument ADR-0075 §1
    records for sheets-structure, and ADR-0073 §3 before it. A grant issued
    today must not silently widen tomorrow.
  • Is deleteContentRange in v1 at all? feat(drive): structural sheet edits via spreadsheets.batchUpdate #1613 excluded deletion for Sheets
    and feat(drive): deletion of sheets, rows and columns via batchUpdate #1623 tracks it separately; the same split may be right here. Note
    --replace "" already removes text without any index arithmetic, which
    covers a real share of what deletion is wanted for.
  • How does --dry-run describe an effect that is not a text match?
    ADR-0076 §7's occurrence count does not generalise, and ADR-0075 §6 had to
    solve the same problem for dimension ranges.
  • Does one verb still equal one request? ADR-0076 §4 makes that a
    signature (batch_update takes one DocsRequest, not a Vec), which is
    what keeps the batch-ordering hazard — where an insertion shifts every later
    request's indices — structurally absent. A verb needing several requests
    reopens both that and the log shape, and should say so explicitly.

Guards that will need revisiting

no_destructive_or_unleased_request_is_reachable (src/drive/docs/api.rs)
currently fails the build if deleteContentRange appears anywhere in the
write path. That is deliberate and should be narrowed consciously rather
than deleted — the guard is what makes "omni-dev cannot delete document
content" a property of the build rather than a claim in prose.

Refs #1615, #1639, #1613, #1623, ADR-0076 §4 §5 §12, ADR-0075 §1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions