Setting selection to a part of a large insert in a transaction

I think I have a misunderstanding of how transactions and selection work.

I have a transaction where I insert a large amount of content at the end of a document and I want to have the selection move into that new content at a given offset. I can’t make it work in a single transaction without getting RangeErrors on the tr.setSelection call. The only way I can get it to work is in a setTimeout call after dispatching the insert transaction. What am I missing this can’t be normal.

What if you dispatch the transaction and then make a new one to set the selection?

Back to back transactions with separate dispatches fail as well.

You should definitely be able to set a new selection in the same transaction that inserts content. Dispatching multiple transactions for that isn’t necessary (and is generally a bad idea). Can you show the code that creates this transaction?

Here is a block of code that demonstrates what I am trying to do and does not work. I added a comment to explain the range error as that part was my misunderstanding

       import { DOMParser as ProseMirrorDOMParser } from "prosemirror-model";
...
        let sequence = "<p>Test line just for content to test with</p>"
        let html = sequence.repeat(200)

        const domParser = new DOMParser();
        const parsedHTML = domParser.parseFromString(html, "text/html").body;

        // Convert DOM to ProseMirror-compatible document fragment
        const docNode = ProseMirrorDOMParser.fromSchema(editor.schema).parse(parsedHTML);

        // Put the cursor at the end of first Child or 2 if for some reason this fails
        const pos = docNode.firstChild?.nodeSize ?? 2

        tr = tr.insert(0, docNode)

        // This version never does anything
        //tr = tr.setSelection(TextSelection.create(tr.doc, 0, pos - 1 ))

        // Nor does this but its essentially the same
        const resolvedPos = tr.doc.resolve(pos - 1);
        tr = tr.setSelection(TextSelection.create(tr.doc, 0, resolvedPos.pos))

        // This one throws the Range Error which I understand is correct because I misunderstood that pos has to be 
        // a position before the insert
        //const mappedPos = tr.mapping.map(pos);
        //tr = tr.setSelection(TextSelection.create(tr.doc, 0, mappedPos))

        dispatch(tr);

There’s no guarantee that the content in docNode can be inserted in the document as-is. (In fact, it’s unlikely that your document node type allows itself as a child, so it’ll definitely get mangled by the replace logic, in the process of being inserted.) As such, blindly using the side of its first (?) child as a document position isn’t safe. You could try tr.mapping.map(0, 1) to find the position after the insertion.

And you’ll want to use TextSelection.between with the resolved position, not create, when you aren’t certain that the position is a valid inline position.

Changing to this does not work either. No logged error and resolved positions are 0 and 40 so something should have been selected within that region as the content in the transaction does appear in the doc

        const startPos = tr.doc.resolve(0);
        const resolvedPos = tr.doc.resolve(pos - 1);
        tr = tr.setSelection(TextSelection.between(startPos, resolvedPos))

Sorry to be a pest but any insight to what I might be doing wrong with my latest updated? @marijn

Look, new DOMParser(), without arguments, isn’t a thing in ProseMirror’s interface, and neither does DOMParser have a parseFromString method, so I wasn’t able to do anything with that code.

Oh sorry, DOMParser is a built in wrapper from typescript

I see. Including the import statements would have helped there. I don’t really understand what you’re trying to do, but setSelection with the result of TextSelection.between seems to work fine for me.