Replace selection with new text, without losing selected state

I want the user to be able to select some text, and click on a button/something to have the selected text be replaced with new text (e.g. think of upper-casing the selected text). It would be nice if the new (replaced) text would continue to be selected, and the first two things I tried didn’t work (all examples can be run in the browser console at https://prosemirror.net/):

  • tr.replaceSelectionWith:

    view.dispatch(view.state.tr.replaceSelectionWith(
        view.state.schema.text('hello')))
    

    — this loses the selection, i.e. after this, nothing is selected.

  • tr.insertText:

    view.dispatch(view.state.tr.insertText('hello'))
    

    — this is really convenient, but also loses the selected state.

By scrolling down further in the Reference manual, I was able to find something that works:

  • tr.replaceWith:

    view.dispatch(view.state.tr.replaceWith(
        view.state.selection.from, 
        view.state.selection.to, 
        view.state.schema.text('hello')))
    

This (or replaceRangeWith) replaces the text and the new text is selected, which is what I wanted.

But just out of curiosity, I’m trying to understand why the first two don’t work the same way. The documentation says the editor selection will be mapped through the steps in the transaction – does that not apply here?

replaceSelection/replaceSelectionWith/insertText move the selection after the inserted content. If you want full control over the selection after a replacement, follow up your replacement with a call to setSelection on the transaction.

Oh I see, thank you. Looks like this is intended behaviour then (the selection is intentionally changed to be just the point after the inserted content). Makes sense, just wasn’t clear to me from reading the guide and reference until just now.