1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# Copied and adapted from on python-lsp-server
#
# Copyright 2017-2020 Palantir Technologies, Inc.
# Copyright 2021- Python Language Server Contributors.
# License: Expat (MIT/X11)
#
from typing import List
from lsprotocol.types import Range, TextEdit, Position
def get_well_formatted_range(lsp_range: Range) -> 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 Range(end, start)
return lsp_range
def get_well_formatted_edit(text_edit: TextEdit) -> TextEdit:
lsp_range = get_well_formatted_range(text_edit.range)
if lsp_range != text_edit.range:
return TextEdit(new_text=text_edit.new_text, range=lsp_range)
return text_edit
def compare_text_edits(a: TextEdit, b: TextEdit) -> int:
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: List[TextEdit]) -> List[TextEdit]:
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 offset_at_position(lines: List[str], server_position: Position) -> int:
row, col = server_position.line, server_position.character
return col + sum(len(line) for line in lines[:row])
def apply_text_edits(text: str, lines: List[str], text_edits: List[TextEdit]) -> str:
sorted_edits = merge_sort_text_edits(
[get_well_formatted_edit(e) for e in text_edits]
)
last_modified_offset = 0
spans = []
for e in sorted_edits:
start_offset = offset_at_position(lines, 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 e.new_text != "":
spans.append(e.new_text)
last_modified_offset = offset_at_position(lines, e.range.end)
spans.append(text[last_modified_offset:])
return "".join(spans)
|