Skip to content

Commit 7e297f3

Browse files
committed
Add apply_text_edits method to workspace document
1 parent f19b3b9 commit 7e297f3

File tree

2 files changed

+413
-0
lines changed

2 files changed

+413
-0
lines changed

pylsp/workspace.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,65 @@ def _create_document(self, doc_uri, source=None, version=None):
129129
)
130130

131131

132+
def get_well_formatted_range(range):
133+
start = range['start']
134+
end = range['end']
135+
136+
if start['line'] > end['line'] or (start['line'] == end['line'] and start['character'] > end['character']):
137+
return { 'start': end, 'end': start }
138+
139+
return range
140+
141+
def get_well_formatted_edit(text_edit):
142+
range = get_well_formatted_range(text_edit['range'])
143+
if range != text_edit['range']:
144+
return { 'newText': text_edit['newText'], 'range': range }
145+
146+
return text_edit
147+
148+
def compare_text_edits(a, b):
149+
diff = a['range']['start']['line'] - b['range']['start']['line']
150+
if diff == 0:
151+
return a['range']['start']['character'] - b['range']['start']['character']
152+
153+
return diff
154+
155+
def merge_sort_text_edits(text_edits):
156+
if len(text_edits) <= 1:
157+
return text_edits
158+
159+
p = len(text_edits) // 2
160+
left = text_edits[:p]
161+
right = text_edits[p:]
162+
163+
merge_sort_text_edits(left)
164+
merge_sort_text_edits(right)
165+
166+
left_idx = 0
167+
right_idx = 0
168+
i = 0
169+
while left_idx < len(left) and right_idx < len(right):
170+
ret = compare_text_edits(left[left_idx], right[right_idx])
171+
if ret <= 0:
172+
# smaller_equal -> take left to preserve order
173+
text_edits[i] = left[left_idx]
174+
i += 1
175+
left_idx += 1
176+
else:
177+
# greater -> take right
178+
text_edits[i] = right[right_idx]
179+
i+=1
180+
right_idx +=1
181+
while left_idx < len(left):
182+
text_edits[i] = left[left_idx]
183+
i += 1
184+
left_idx += 1
185+
while right_idx < len(right):
186+
text_edits[i] = right[right_idx]
187+
i += 1
188+
right_idx += 1
189+
return text_edits
190+
132191
class Document:
133192

134193
def __init__(self, uri, workspace, source=None, version=None, local=True, extra_sys_path=None,
@@ -216,6 +275,26 @@ def apply_change(self, change):
216275

217276
self._source = new.getvalue()
218277

278+
@lock
279+
def apply_text_edits(self, text_edits):
280+
text = self._source
281+
sorted_edits = merge_sort_text_edits(list(map(get_well_formatted_edit,text_edits)))
282+
last_modified_offset = 0
283+
spans = []
284+
for e in sorted_edits:
285+
start_offset = self.offset_at_position(e['range']['start'])
286+
if start_offset < last_modified_offset:
287+
raise Exception('overlapping edit')
288+
elif start_offset > last_modified_offset:
289+
spans.append(text[last_modified_offset:start_offset])
290+
291+
if len(e['newText']):
292+
spans.append(e['newText'])
293+
last_modified_offset = self.offset_at_position(e['range']['end'])
294+
295+
spans.append(text[last_modified_offset:])
296+
return ''.join(spans)
297+
219298
def offset_at_position(self, position):
220299
"""Return the byte-offset pointed at by the given position."""
221300
return position['character'] + len(''.join(self.lines[:position['line']]))

0 commit comments

Comments
 (0)