jupyter-server / jupyter_ydoc

Jupyter document structures for collaborative editing using Yjs/pycrdt
https://jupyter-ydoc.readthedocs.io
BSD 3-Clause "New" or "Revised" License
29 stars 17 forks source link

Allow for document-agnostic undo/redo #238

Closed davidbrochart closed 2 months ago

davidbrochart commented 3 months ago

Problem

Currently, data that can be undone (using an undo manager) is not specified. For instance, the content of a YFile is stored in a Text root type under "source", and the cells of a YNotebook are stored in an Array root type under "cells". This prevent an undo manager to operate on a document in an agnostic way, i.e. without knowing what it is manipulating. Indeed, the undo manager needs to be scoped to one or several shared types (see here).

Proposed Solution

We could either:

Additional context

This is particularly useful in the context of a document timeline explorer.

cc @meriem-benismail

davidbrochart commented 3 months ago
  • have a special field (maybe under the "state" root Map), that would list the root types that can be undone. For a notebook, that would look like this:
{
  "state": {
    "undo": "cells"
  }
}

I'm realizing that this is not enough, as a remote peer would also need the root type itself (not only its name):

{
  "state": {
    "undo": {
      "cells": "map"
    }
  }
}
davidbrochart commented 3 months ago

Note that Yjs's author suggested using the ydoc.share Map to get all the root shared types of a document (see this discussion). It seems to only be available in Yjs though, not in Yrs.

davidbrochart commented 3 months ago

Actually the JavaScript YDocument implementation has an undo manager, and every document type (YNotebook, YFile...) adds scopes for what can be undone. We should probably do the same in the Python implementation.

dmonad commented 3 months ago

Note that Yjs's author suggested using the ydoc.share Map to get all the root shared types of a document (see this discussion). It seems to only be available in Yjs though, not in Yrs.

I advise against using ydoc.share as it encourages dynamic use of the root-level types. You should know all the root-level types beforehand.

E.g. If you want to be able to undo ydoc.getArray('cells') and ydoc.getMap('props'), you should add them individually to the undo manager.

If some third-party extension decides to add a custom field (e.g. ydoc.getMap('comment')), then it shouldn't be added to the UndoManager automatically!