Skip to content

Send hosts ranges, not needles: one text-matching implementation across editors #589

Description

@kcarnold

EditorAPI currently asks hosts to find text: selectPhrase(phrase), and applyEdit({type: 'str_replace', oldStr, …}). Every host therefore implements its own search, with its own leniency:

Host How it searches Leniency
Lexical indexOf per TextNode, via the fold ladder tiered (#587)
Word body.search / Range.search ignorePunct, ignoreSpace, matchCase — all at once, untiered
Google Docs Apps Script findText (regex) exact, then retry with a word trimmed off each end

Three policies for one question. They disagree in ways that produce real failures: Word's selectPhrase is lenient while its applyEdit is not, so the reveal highlights a phrase the apply then fails to find. Google Docs' trimming can't repair an interior mismatch (a curly apostrophe mid-phrase) and, when it does succeed, selects less text than was asked for. And the shared ladder in @/utilities/textMatching can't reach Apps Script at all: Code.gs is deployed separately from the bundle and drifts from it.

Proposal

Invert the seam. The browser already holds the document text (getDocText / getParagraphs), which is what the ladder needs. Match there, and give hosts two primitives that require no searching:

  • selectRange(start, end)
  • replaceRange(start, end, text) (or keep applySplice and lower it to range replaces)

Then matching has one implementation, in the module that has the tests, and hosts shrink to what they're uniquely able to do.

Falls out of it:

Cost

Needs an Apps Script redeploy for the new bridge functions. The version-drift fallback pattern already exists in googleDocsEditorAPI.ts (documentPropertyBridge treats the bridge methods as optional and falls back when the installed deployment predates them), so this can ship without assuming a synchronized deploy.

Depends on #588: matching by offset requires the document text the browser holds to correspond to the document Apps Script will edit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions