client-py/venv/lib/python3.12/site-packages/pylsp/text_edit.py
2026-05-02 13:34:53 +05:00

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)