Skip to content

Codemirror gets out of sync when not connected to a view #36

@milomg

Description

@milomg

Please save me some time and use the following template. In 90% of all issues I can't reproduce the problem because I don't know what exactly you are doing, in which environment, or which y-* version is responsible. Just use the following template even if you think the problem is obvious.

Checklist

Describe the bug

The project I'm working on has multiple codemirror states that represent different tabs in an editor, and uses a single codemirror view that can switch to render multiple codemirror states. If a doc that is in a background tab is modified by a remote peer, it becomes out of sync when it switches to the front.

const state1 = EditorState.create({
  doc: "doc1",
  extensions: [yCollabPlugin1],
})
const state2 = EditorState.create({
  doc: "doc2",
  extensions: [],
})
const view = new EditorView({
  state: state1,
  parent: document.body
})

// when switching tabs
view.setState(state2)

// when switching back
view.setState(state1)

In other words, when yCollab is installed on a codemirror state that goes offscreen (i.e. the view/editor switches to another state), the editor becomes out of sync and can't recover.

Expected behavior

I would expect the when the view plugin is destroyed changes will be queued up, and when the view is remounted, the changes are applied at once (with a warning if the resulting codemirror doc diverges from the yjs doc).

Environment Information

  • Chrome: 132.0.6834.160
  • y-codemirror.next: 0.3.5,
  • y-webrtc: 10.3.0,
  • yjs: 13.6.23

Additional context
Add any other context about the problem here.

One solution I was experimenting with is to create a simple diff and patch algorithm that was triggered when the doc was remounted, but I was wondering if there was a more official way of doing this or if this might be useful to include in this library?

import { diff } from 'fast-myers-diff'
const syncPlugin = (ytext: Y.Text) =>
  ViewPlugin.fromClass(
    class {
      constructor(public view: EditorView) {
        if (view.state.doc.toString() != ytext.toString()) {
          const ystr = ytext.toString()
          const mydiff = diff(view.state.doc.toString(), ystr)
          const changes: ChangeSpec[] = []
          for (const [sx, ex, sy, ey] of mydiff) {
            changes.push({
              from: sx,
              to: ex,
              insert: ystr.slice(sy, ey),
            })
          }
          queueMicrotask(() => {
            view.dispatch({
              changes: changes,
              annotations: [ySyncAnnotation.of(view.plugin(ySync)!.conf)]
            })
          })
        }
      }
    },
  )
  • I'm a sponsor 💖
  • This issue is a blocker for my project.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions