Document Changes in CM6

Hello

I read CodeMirror Document Change Example and are able to insert code at the start of the document. However, I want to replace all the code that is in the editor with another code. What is the best way to do this in CM6? I looked around for a function to reset the view, but couldn’t find one.

Thanks and Best Regards

The recommended way to do this is to create a completely new state and call setState on the view.

This will cause the entire document to be redrawn and all view plugins to be reinitialized, so you should probably only use it when the new state isn’t derived from the old state

Is there a better way to do this? I don’t really want the view plugins to be reinitialized. I only want to update the string in doc.

If you’re moving to a new document that has nothing to do with the old, definitely do use setState. If you are just making changes to the existing document, dispatch transactions that make those changes.

1 Like

Had a similar use-case. I needed to update/replace the entire doc content, because it may have been updated by a third party and I didn’t know about the exact changes.

So to replace the entire content, I used this:

editor.dispatch({
    changes: {
        from: 0,
        to: editor.state.doc.length,
        insert: newContent
    }
})
1 Like

Do keep in mind that that’ll mess up the undo history and any other metadata that’s being kept about the document content (marks, mapped positions). If practical, you could compute a minimal diff and just update the changed ranges.

1 Like

Good to know! Yeah, my use case is very simple.

Each document is a new exercise. No need to keep any history around between exercises.

Thank you @Roberto for sharing that! I was stuck on what to put for the to field originally.

So, create a fresh state, then? Otherwise users can undo back to the previous exercise.

Of course, I understand committing a minimal diff would be the preferred way. However, I didn’t notice any problems with undo specifically in my case. Cmd+Z simply reverts to the previous content. Could you elaborate on this, if you find the time?

Edit: Ah, I think I got it. You mean, undo wouldn’t make any sense, if we’re talking two different documents after the replacement. Which isn’t the case in my app.

I think I tried that originally but couldn’t figure it out at first. I was trying to call setState and pass an object, but for doc it wanted Text and I couldn’t create one. I finally figured it out though.

Option 1

Use setState to create a fresh state:

let newState = EditorState.create({
  doc: exercise2.initialText,
  selection: EditorSelection.cursor(exercise2.cursorStart),
});

cmRef?.current?.view?.setState(newState);

Option 2

Dispatch a transaction that replaces the text and cursor in the current document (preserves history):

const transaction = cmRef?.current?.view?.state.update({
  changes: {
    from: 0,
    to: cmRef?.current.state?.doc.length,
    insert: exercise2.initialText,
  },
  selection: { anchor: exercise2.cursorStart },
});

if (transaction) {
  cmRef?.current?.view?.dispatch(transaction);
}

Thank you @Roberto @marijn for the help!

Note: this is using React CodeMirror - CodeMirror component for React.

I would suggest changing this to:
to: cmRef?.current.view?.state.doc.length
because to: cmRef?.current.state?.doc.length gives the length of the initial content and is not updated, which means that it will work the first time but not the second time.

1 Like

Oh! I bet that was the issue I was running into. Thanks for the suggestion!

1 Like