Skip to content

Commit ab37f2c

Browse files
committed
Implement granular cell updates on Python side
Fix
1 parent 5c715dd commit ab37f2c

File tree

1 file changed

+52
-2
lines changed

1 file changed

+52
-2
lines changed

jupyter_ydoc/ynotebook.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
# The default minor version of the notebook format.
1818
NBFORMAT_MINOR_VERSION = 5
1919

20+
_CELL_KEY_TYPE_MAP = {"metadata": Map, "source": Text, "outputs": Array}
21+
2022

2123
class YNotebook(YBaseDoc):
2224
"""
@@ -249,7 +251,7 @@ def set(self, value: dict) -> None:
249251
"id": str(uuid4()),
250252
}
251253
]
252-
old_ycells_by_id = {ycell["id"]: ycell for ycell in self._ycells}
254+
old_ycells_by_id: dict[str, Map] = {ycell["id"]: ycell for ycell in self._ycells}
253255

254256
with self._ydoc.transaction():
255257
new_cell_list: list[dict] = []
@@ -260,7 +262,55 @@ def set(self, value: dict) -> None:
260262
cell_id = new_cell.get("id")
261263
if cell_id and (old_ycell := old_ycells_by_id.get(cell_id)):
262264
old_cell = self._cell_to_py(old_ycell)
263-
if old_cell == new_cell:
265+
updated_granularly = True
266+
if old_cell != new_cell:
267+
# attempt to update cell granularly
268+
old_keys = set(old_cell.keys())
269+
new_keys = set(new_cell.keys())
270+
271+
shared_keys = old_keys & new_keys
272+
removed_keys = old_keys - new_keys
273+
added_keys = new_keys - old_keys
274+
275+
for key in shared_keys:
276+
if old_cell[key] != new_cell[key]:
277+
if key == "output" and value:
278+
# outputs require complex handling - some have Text type nested;
279+
# for now skip creating them; clearing all outputs is fine
280+
updated_granularly = False
281+
282+
if key in _CELL_KEY_TYPE_MAP:
283+
kind = _CELL_KEY_TYPE_MAP[key]
284+
value = new_cell[key]
285+
if kind == Text:
286+
old: Text = old_ycell[key]
287+
old.clear()
288+
old.insert(0, value)
289+
elif kind == Array:
290+
old: Array = old_ycell[key]
291+
old.clear()
292+
old.extend(value)
293+
elif kind == Map:
294+
old: Map = old_ycell[key]
295+
old.clear()
296+
for k, v in value.items():
297+
old[k] = v
298+
else:
299+
old_ycell[key] = new_cell[key]
300+
301+
for key in removed_keys:
302+
del old_ycell[key]
303+
304+
for key in added_keys:
305+
if key in _CELL_KEY_TYPE_MAP:
306+
# we hard-reload cells when keys that require nested types get added
307+
# to allow the frontend to connect observers; this could be changed
308+
# in the future, once frontends learn how to observe all changes
309+
updated_granularly = False
310+
else:
311+
old_ycell[key] = new_cell[key]
312+
313+
if updated_granularly:
264314
new_cell_list.append(old_cell)
265315
retained_cells.add(cell_id)
266316
continue

0 commit comments

Comments
 (0)