Skip to content

Commit 78231c1

Browse files
Add undo_manager to Y documents (#248) (#251)
This is a cherry-pick of #248.
1 parent 947c46e commit 78231c1

File tree

7 files changed

+65
-12
lines changed

7 files changed

+65
-12
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Tests
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [2.x]
66
pull_request:
7-
branches: [main]
7+
branches: [2.x]
88

99
jobs:
1010
pre-commit:

jupyter_ydoc/ybasedoc.py

Lines changed: 19 additions & 4 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
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.
@@ -26,8 +31,9 @@ def __init__(self, ydoc: Optional[Doc] = None):
2631
self._ydoc = Doc()
2732
else:
2833
self._ydoc = ydoc
29-
self._ydoc["state"] = self._ystate = Map()
30-
self._subscriptions: Dict[Any, str] = {}
34+
self._ystate = self._ydoc.get("state", type=Map)
35+
self._subscriptions = {}
36+
self._undo_manager = UndoManager(doc=self._ydoc, capture_timeout_millis=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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def __init__(self, ydoc: Optional[Doc] = None):
3636
:type ydoc: :class:`pycrdt.Doc`, optional.
3737
"""
3838
super().__init__(ydoc)
39-
self._ydoc["source"] = self._ysource = Map()
39+
self._ysource = self._ydoc.get("source", type=Map)
40+
self.undo_manager.expand_scope(self._ysource)
4041

4142
@property
4243
def version(self) -> str:

jupyter_ydoc/ynotebook.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ def __init__(self, ydoc: Optional[Doc] = None):
5454
:type ydoc: :class:`pycrdt.Doc`, optional.
5555
"""
5656
super().__init__(ydoc)
57-
self._ydoc["meta"] = self._ymeta = Map()
58-
self._ydoc["cells"] = self._ycells = Array()
57+
self._ymeta = self._ydoc.get("meta", type=Map)
58+
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def __init__(self, ydoc: Optional[Doc] = None):
3131
:type ydoc: :class:`pycrdt.Doc`, optional.
3232
"""
3333
super().__init__(ydoc)
34-
self._ydoc["source"] = self._ysource = Text()
34+
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ requires-python = ">=3.7"
1313
keywords = ["jupyter", "ypy"]
1414
dependencies = [
1515
"importlib_metadata >=3.6; python_version<'3.10'",
16-
"pycrdt >=0.8.1,<0.9.0",
16+
"pycrdt >=0.9.0,<0.10.0",
1717
]
1818

1919
[[project.authors]]
@@ -31,7 +31,7 @@ test = [
3131
"pytest",
3232
"pytest-asyncio",
3333
"websockets >=10.0",
34-
"pycrdt-websocket >=0.12.6,<0.13.0",
34+
"pycrdt-websocket >=0.14.1,<0.15.0",
3535
]
3636
docs = [
3737
"sphinx",

tests/test_ydocs.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
from jupyter_ydoc import YNotebook
5+
6+
7+
def test_ynotebook_undo_manager():
8+
ynotebook = YNotebook()
9+
cell0 = {
10+
"cell_type": "code",
11+
"source": "Hello",
12+
}
13+
ynotebook.append_cell(cell0)
14+
source = ynotebook.ycells[0]["source"]
15+
source += ", World!\n"
16+
cell1 = {
17+
"cell_type": "code",
18+
"source": "print(1 + 1)\n",
19+
}
20+
ynotebook.append_cell(cell1)
21+
assert len(ynotebook.ycells) == 2
22+
assert str(ynotebook.ycells[0]["source"]) == "Hello, World!\n"
23+
assert str(ynotebook.ycells[1]["source"]) == "print(1 + 1)\n"
24+
assert ynotebook.undo_manager.can_undo()
25+
ynotebook.undo_manager.undo()
26+
assert len(ynotebook.ycells) == 1
27+
assert str(ynotebook.ycells[0]["source"]) == "Hello, World!\n"
28+
assert ynotebook.undo_manager.can_undo()
29+
ynotebook.undo_manager.undo()
30+
assert len(ynotebook.ycells) == 1
31+
assert str(ynotebook.ycells[0]["source"]) == "Hello"
32+
assert ynotebook.undo_manager.can_undo()
33+
ynotebook.undo_manager.undo()
34+
assert len(ynotebook.ycells) == 0
35+
assert not ynotebook.undo_manager.can_undo()

0 commit comments

Comments
 (0)