targetObserver = (binding, changes) => {
  binding._mux(() => {
    binding.doc.transact(() => {
      const hasPaste = binding.yUndoManager && changes.some(change => change.origin === 'paste');

      if (hasPaste) {
        binding.yUndoManager.stopCapturing();
      }

      if (changes.length > 1) {
        // If there are several consecutive changes, we can't reliably compute the positions anymore. See y-codemirror#11
        // Instead, we will compute the diff and apply the changes
        const d = diff.simpleDiffString(binding.type.toString(), binding.cmDoc.getValue());
        binding.type.delete(d.index, d.remove);
        binding.type.insert(d.index, d.insert);
      } else {
        const change = changes[0];
        const start = binding.cmDoc.indexFromPos(change.from);
        const delLen = change.removed.map(s => s.length).reduce(math.add) + change.removed.length - 1;

        if (delLen > 0) {
          binding.type.delete(start, delLen);
        }

        if (change.text.length > 0) {
          binding.type.insert(start, change.text.join('\n'));
        }
      }

      if (hasPaste) {
        binding.yUndoManager.stopCapturing();
      }
    }, binding);
  });

  if (binding._pendingCursorEvent) {
    binding._pendingCursorEvent = false;
    binding.emit('cursorActivity', [binding]);
  }
}
@param {CodemirrorBinding} binding
@param {Array<any>} changes