Skip to content

Commit ba21026

Browse files
committed
Use stdlib sequence matcher to perform granular text updates
1 parent a4b662d commit ba21026

File tree

1 file changed

+30
-6
lines changed

1 file changed

+30
-6
lines changed

jupyter_ydoc/yunicode.py

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

44
from collections.abc import Callable
5+
from difflib import SequenceMatcher
56
from functools import partial
67
from typing import Any
78

@@ -64,17 +65,40 @@ def set(self, value: str) -> None:
6465
:param value: The content of the document.
6566
:type value: str
6667
"""
67-
if self.get() == value:
68+
old_value = self.get()
69+
if old_value == value:
6870
# no-op if the values are already the same,
6971
# to avoid side-effects such as cursor jumping to the top
7072
return
7173

7274
with self._ydoc.transaction():
73-
# clear document
74-
self._ysource.clear()
75-
# initialize document
76-
if value:
77-
self._ysource += value
75+
matcher = SequenceMatcher(a=old_value, b=value)
76+
77+
# for very different strings, just replace the whole content;
78+
# this avoids generating a huge number of operations
79+
if matcher.ratio() < 0.6:
80+
# clear document
81+
self._ysource.clear()
82+
# initialize document
83+
if value:
84+
self._ysource += value
85+
else:
86+
operations = matcher.get_opcodes()
87+
offset = 0
88+
for tag, i1, i2, j1, j2 in operations:
89+
if tag == "replace":
90+
self._ysource[i1 + offset : i2 + offset] = value[j1:j2]
91+
offset += (j2 - j1) - (i2 - i1)
92+
elif tag == "delete":
93+
del self._ysource[i1 + offset : i2 + offset]
94+
offset -= i2 - i1
95+
elif tag == "insert":
96+
self._ysource[i1 + offset : i2 + offset] = value[j1:j2]
97+
offset += j2 - j1
98+
elif tag == "equal":
99+
pass
100+
else:
101+
raise ValueError(f"Unknown tag '{tag}' in sequence matcher")
78102

79103
def observe(self, callback: Callable[[str, Any], None]) -> None:
80104
"""

0 commit comments

Comments
 (0)