97 lines
2.7 KiB
Python
97 lines
2.7 KiB
Python
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
# Copyright 2021- Python Language Server Contributors.
|
|
|
|
|
|
def get_well_formatted_range(lsp_range):
|
|
start = lsp_range["start"]
|
|
end = lsp_range["end"]
|
|
|
|
if start["line"] > end["line"] or (
|
|
start["line"] == end["line"] and start["character"] > end["character"]
|
|
):
|
|
return {"start": end, "end": start}
|
|
|
|
return lsp_range
|
|
|
|
|
|
def get_well_formatted_edit(text_edit):
|
|
lsp_range = get_well_formatted_range(text_edit["range"])
|
|
if lsp_range != text_edit["range"]:
|
|
return {"newText": text_edit["newText"], "range": lsp_range}
|
|
|
|
return text_edit
|
|
|
|
|
|
def compare_text_edits(a, b):
|
|
diff = a["range"]["start"]["line"] - b["range"]["start"]["line"]
|
|
if diff == 0:
|
|
return a["range"]["start"]["character"] - b["range"]["start"]["character"]
|
|
|
|
return diff
|
|
|
|
|
|
def merge_sort_text_edits(text_edits):
|
|
if len(text_edits) <= 1:
|
|
return text_edits
|
|
|
|
p = len(text_edits) // 2
|
|
left = text_edits[:p]
|
|
right = text_edits[p:]
|
|
|
|
merge_sort_text_edits(left)
|
|
merge_sort_text_edits(right)
|
|
|
|
left_idx = 0
|
|
right_idx = 0
|
|
i = 0
|
|
while left_idx < len(left) and right_idx < len(right):
|
|
ret = compare_text_edits(left[left_idx], right[right_idx])
|
|
if ret <= 0:
|
|
# smaller_equal -> take left to preserve order
|
|
text_edits[i] = left[left_idx]
|
|
i += 1
|
|
left_idx += 1
|
|
else:
|
|
# greater -> take right
|
|
text_edits[i] = right[right_idx]
|
|
i += 1
|
|
right_idx += 1
|
|
while left_idx < len(left):
|
|
text_edits[i] = left[left_idx]
|
|
i += 1
|
|
left_idx += 1
|
|
while right_idx < len(right):
|
|
text_edits[i] = right[right_idx]
|
|
i += 1
|
|
right_idx += 1
|
|
return text_edits
|
|
|
|
|
|
class OverLappingTextEditException(Exception):
|
|
"""
|
|
Text edits are expected to be sorted
|
|
and compressed instead of overlapping.
|
|
This error is raised when two edits
|
|
are overlapping.
|
|
"""
|
|
|
|
|
|
def apply_text_edits(doc, text_edits):
|
|
text = doc.source
|
|
sorted_edits = merge_sort_text_edits(list(map(get_well_formatted_edit, text_edits)))
|
|
last_modified_offset = 0
|
|
spans = []
|
|
for e in sorted_edits:
|
|
start_offset = doc.offset_at_position(e["range"]["start"])
|
|
if start_offset < last_modified_offset:
|
|
raise OverLappingTextEditException("overlapping edit")
|
|
|
|
if start_offset > last_modified_offset:
|
|
spans.append(text[last_modified_offset:start_offset])
|
|
|
|
if len(e["newText"]):
|
|
spans.append(e["newText"])
|
|
last_modified_offset = doc.offset_at_position(e["range"]["end"])
|
|
|
|
spans.append(text[last_modified_offset:])
|
|
return "".join(spans)
|