90 lines
2.3 KiB
Python
90 lines
2.3 KiB
Python
import attr
|
|
|
|
class EditOutOfRange(Exception):
|
|
pass
|
|
|
|
def apply_delete(current_spans, edit):
|
|
assert edit
|
|
if not current_spans:
|
|
raise ValueError('edit is out of bounds')
|
|
|
|
spans = []
|
|
pos = 0
|
|
edit_end = edit['start'] + len(edit['old'])
|
|
|
|
cur_span = current_spans.pop(0)
|
|
while pos + cur_span.length < edit['start']:
|
|
spans.append(cur_span)
|
|
pos += cur_span.length
|
|
cur_span = current_spans.pop(0)
|
|
|
|
if edit['start'] > pos:
|
|
new_span = attr.evolve(cur_span, length=edit['start'] - pos)
|
|
spans.append(new_span)
|
|
|
|
while pos + cur_span.length < edit_end:
|
|
pos += cur_span.length
|
|
cur_span = current_spans.pop(0)
|
|
|
|
if pos + cur_span.length != edit_end:
|
|
offset = cur_span.start - pos
|
|
new_start = offset + (edit_end - pos)
|
|
diff = new_start - cur_span.start
|
|
new_span = attr.evolve(cur_span,
|
|
length=cur_span.length - diff,
|
|
start=new_start)
|
|
spans.append(new_span)
|
|
|
|
spans += current_spans
|
|
return spans
|
|
|
|
def apply_insert(current_spans, edit):
|
|
if not current_spans and edit['0'] == 0:
|
|
return edit['span']
|
|
|
|
pos = 0
|
|
spans = []
|
|
cur_span = current_spans.pop(0)
|
|
while pos + cur_span.length < edit['start']:
|
|
spans.append(cur_span)
|
|
pos += cur_span.length
|
|
cur_span = current_spans.pop(0)
|
|
|
|
if edit['start'] >= pos:
|
|
length_a = edit['start'] - pos
|
|
length_b = cur_span.length - length_a
|
|
|
|
if length_a:
|
|
span_a = attr.evolve(cur_span, length=length_a)
|
|
pos += length_a
|
|
spans.append(span_a)
|
|
|
|
spans.append(edit['span'])
|
|
pos += edit['span'].length
|
|
|
|
if length_b:
|
|
span_b = attr.evolve(cur_span,
|
|
start=cur_span.start + length_a,
|
|
length=length_b)
|
|
spans.append(span_b)
|
|
|
|
pos += length_b
|
|
else:
|
|
spans.append(edit['span'])
|
|
|
|
spans += current_spans
|
|
return spans
|
|
|
|
def apply_edits(spans, edits):
|
|
for edit in edits:
|
|
if edit['op'] == 'delete':
|
|
spans = apply_delete(spans, edit)
|
|
continue
|
|
if edit['op'] == 'insert':
|
|
spans = apply_insert(spans, edit)
|
|
continue
|
|
|
|
return spans
|
|
|
|
|