@@ -129,6 +129,65 @@ def _create_document(self, doc_uri, source=None, version=None):
129
129
)
130
130
131
131
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
+
132
191
class Document :
133
192
134
193
def __init__ (self , uri , workspace , source = None , version = None , local = True , extra_sys_path = None ,
@@ -216,6 +275,26 @@ def apply_change(self, change):
216
275
217
276
self ._source = new .getvalue ()
218
277
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
+
219
298
def offset_at_position (self , position ):
220
299
"""Return the byte-offset pointed at by the given position."""
221
300
return position ['character' ] + len ('' .join (self .lines [:position ['line' ]]))
0 commit comments