Skip to content

Commit f6455a8

Browse files
committed
Add undo_manager to Y documents
1 parent f94d120 commit f6455a8

File tree

6 files changed

+54
-5
lines changed

6 files changed

+54
-5
lines changed

jupyter_ydoc/ybasedoc.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
# Distributed under the terms of the Modified BSD License.
33

44
from abc import ABC, abstractmethod
5-
from typing import Any, Callable, Dict, Optional
5+
from typing import Any, Callable, Optional
66

7-
from pycrdt import Doc, Map, Subscription
7+
from pycrdt import Doc, Map, Subscription, UndoManager
88

99

1010
class YBaseDoc(ABC):
@@ -15,6 +15,11 @@ class YBaseDoc(ABC):
1515
subscribe to changes in the document.
1616
"""
1717

18+
_ydoc: Doc
19+
_ystate: Map
20+
_subscriptions: dict[Any, Subscription]
21+
_undo_manager: UndoManager
22+
1823
def __init__(self, ydoc: Optional[Doc] = None):
1924
"""
2025
Constructs a YBaseDoc.
@@ -27,7 +32,8 @@ def __init__(self, ydoc: Optional[Doc] = None):
2732
else:
2833
self._ydoc = ydoc
2934
self._ystate = self._ydoc.get("state", type=Map)
30-
self._subscriptions: Dict[Any, Subscription] = {}
35+
self._subscriptions = {}
36+
self._undo_manager = UndoManager(self._ystate, 0)
3137

3238
@property
3339
@abstractmethod
@@ -40,6 +46,15 @@ def version(self) -> str:
4046
"""
4147

4248
@property
49+
def undo_manager(self) -> UndoManager:
50+
"""
51+
A :class:`pycrdt.UndoManager` for the document.
52+
53+
:return: The document's undo manager.
54+
:rtype: :class:`pycrdt.UndoManager`
55+
"""
56+
return self._undo_manager
57+
4358
def ystate(self) -> Map:
4459
"""
4560
A :class:`pycrdt.Map` containing the state of the document.

jupyter_ydoc/yblob.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self, ydoc: Optional[Doc] = None):
3333
"""
3434
super().__init__(ydoc)
3535
self._ysource = self._ydoc.get("source", type=Map)
36+
self.undo_manager.expand_scope(self._ysource)
3637

3738
@property
3839
def version(self) -> str:

jupyter_ydoc/ynotebook.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def __init__(self, ydoc: Optional[Doc] = None):
5656
super().__init__(ydoc)
5757
self._ymeta = self._ydoc.get("meta", type=Map)
5858
self._ycells = self._ydoc.get("cells", type=Array)
59+
self.undo_manager.expand_scope(self._ycells)
5960

6061
@property
6162
def version(self) -> str:

jupyter_ydoc/yunicode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(self, ydoc: Optional[Doc] = None):
3232
"""
3333
super().__init__(ydoc)
3434
self._ysource = self._ydoc.get("source", type=Text)
35+
self.undo_manager.expand_scope(self._ysource)
3536

3637
@property
3738
def version(self) -> str:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ requires-python = ">=3.8"
1313
keywords = ["jupyter", "pycrdt", "yjs"]
1414
dependencies = [
1515
"importlib_metadata >=3.6; python_version<'3.10'",
16-
"pycrdt >=0.8.16,<0.9.0",
16+
"pycrdt >=0.8.31,<0.9.0",
1717
]
1818

1919
[[project.authors]]

tests/test_ydocs.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
from jupyter_ydoc import YBlob
4+
from jupyter_ydoc import YBlob, YNotebook
55

66

77
def test_yblob():
@@ -22,3 +22,34 @@ def callback(topic, event):
2222
assert topic == "source"
2323
assert event.keys["bytes"]["oldValue"] == b"012"
2424
assert event.keys["bytes"]["newValue"] == b"345"
25+
26+
27+
def test_ynotebook_undo_manager():
28+
ynotebook = YNotebook()
29+
cell0 = {
30+
"cell_type": "code",
31+
"source": "Hello",
32+
}
33+
ynotebook.append_cell(cell0)
34+
source = ynotebook.ycells[0]["source"]
35+
source += ", World!\n"
36+
cell1 = {
37+
"cell_type": "code",
38+
"source": "print(1 + 1)\n",
39+
}
40+
ynotebook.append_cell(cell1)
41+
assert len(ynotebook.ycells) == 2
42+
assert str(ynotebook.ycells[0]["source"]) == "Hello, World!\n"
43+
assert str(ynotebook.ycells[1]["source"]) == "print(1 + 1)\n"
44+
assert ynotebook.undo_manager.can_undo()
45+
ynotebook.undo_manager.undo()
46+
assert len(ynotebook.ycells) == 1
47+
assert str(ynotebook.ycells[0]["source"]) == "Hello, World!\n"
48+
assert ynotebook.undo_manager.can_undo()
49+
ynotebook.undo_manager.undo()
50+
assert len(ynotebook.ycells) == 1
51+
assert str(ynotebook.ycells[0]["source"]) == "Hello"
52+
assert ynotebook.undo_manager.can_undo()
53+
ynotebook.undo_manager.undo()
54+
assert len(ynotebook.ycells) == 0
55+
assert not ynotebook.undo_manager.can_undo()

0 commit comments

Comments
 (0)