@@ -130,7 +130,89 @@ impl<P: AsRef<Path> + std::fmt::Debug> ToUrl for P {
130130 }
131131}
132132
133- pub fn apply_TextEdits ( lines : & [ String ] , edits : & [ TextEdit ] ) -> Fallible < Vec < String > > {
133+ fn position_to_offset ( lines : & [ String ] , position : & Position ) -> usize {
134+ if lines. is_empty ( ) {
135+ return 0 ;
136+ }
137+
138+ let line = std:: cmp:: min ( position. line as usize , lines. len ( ) - 1 ) ;
139+ let character = std:: cmp:: min ( position. character as usize , lines[ line] . len ( ) ) ;
140+
141+ let chars_above: usize = lines[ ..line] . iter ( ) . map ( |text| text. len ( ) + 1 ) . sum ( ) ;
142+ chars_above + character
143+ }
144+
145+ #[ test]
146+ fn test_position_to_offset ( ) {
147+ assert_eq ! ( position_to_offset( & [ ] , & Position :: new( 0 , 0 ) ) , 0 ) ;
148+
149+ let lines: Vec < String > = "\n " . lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
150+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 0 , 0 ) ) , 0 ) ;
151+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 0 , 1 ) ) , 0 ) ;
152+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 0 ) ) , 0 ) ;
153+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 1 ) ) , 0 ) ;
154+
155+ let lines: Vec < String > = "a\n " . lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
156+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 0 , 0 ) ) , 0 ) ;
157+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 0 , 1 ) ) , 1 ) ;
158+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 0 , 2 ) ) , 1 ) ;
159+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 0 ) ) , 0 ) ;
160+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 1 ) ) , 1 ) ;
161+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 2 ) ) , 1 ) ;
162+
163+ let lines: Vec < String > = "a\n bc\n " . lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
164+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 0 ) ) , 2 ) ;
165+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 1 ) ) , 3 ) ;
166+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 2 ) ) , 4 ) ;
167+ assert_eq ! ( position_to_offset( & lines, & Position :: new( 1 , 3 ) ) , 4 ) ;
168+ }
169+
170+ fn offset_to_position ( lines : & [ String ] , offset : usize ) -> Position {
171+ if lines. is_empty ( ) {
172+ return Position :: new ( 0 , 0 ) ;
173+ }
174+
175+ let mut offset = offset;
176+ for ( line, text) in lines. iter ( ) . enumerate ( ) {
177+ if offset <= text. len ( ) {
178+ return Position :: new ( line as u64 , offset as u64 ) ;
179+ }
180+
181+ offset -= text. len ( ) + 1 ;
182+ }
183+
184+ let last_line = lines. len ( ) - 1 ;
185+ let last_character = lines[ last_line] . len ( ) ;
186+ Position :: new ( last_line as u64 , last_character as u64 )
187+ }
188+
189+ #[ test]
190+ fn test_offset_to_position ( ) {
191+ assert_eq ! ( offset_to_position( & [ ] , 0 ) , Position :: new( 0 , 0 ) ) ;
192+
193+ let lines: Vec < String > = "\n " . lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
194+ assert_eq ! ( offset_to_position( & lines, 0 ) , Position :: new( 0 , 0 ) ) ;
195+ assert_eq ! ( offset_to_position( & lines, 1 ) , Position :: new( 0 , 0 ) ) ;
196+
197+ let lines: Vec < String > = "a\n " . lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
198+ assert_eq ! ( offset_to_position( & lines, 0 ) , Position :: new( 0 , 0 ) ) ;
199+ assert_eq ! ( offset_to_position( & lines, 1 ) , Position :: new( 0 , 1 ) ) ;
200+ assert_eq ! ( offset_to_position( & lines, 2 ) , Position :: new( 0 , 1 ) ) ;
201+
202+ let lines: Vec < String > = "a\n bc\n " . lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
203+ assert_eq ! ( offset_to_position( & lines, 0 ) , Position :: new( 0 , 0 ) ) ;
204+ assert_eq ! ( offset_to_position( & lines, 1 ) , Position :: new( 0 , 1 ) ) ;
205+ assert_eq ! ( offset_to_position( & lines, 2 ) , Position :: new( 1 , 0 ) ) ;
206+ assert_eq ! ( offset_to_position( & lines, 3 ) , Position :: new( 1 , 1 ) ) ;
207+ assert_eq ! ( offset_to_position( & lines, 4 ) , Position :: new( 1 , 2 ) ) ;
208+ assert_eq ! ( offset_to_position( & lines, 5 ) , Position :: new( 1 , 2 ) ) ;
209+ }
210+
211+ pub fn apply_TextEdits (
212+ lines : & [ String ] ,
213+ edits : & [ TextEdit ] ,
214+ position : & Position ,
215+ ) -> Fallible < ( Vec < String > , Position ) > {
134216 // Edits are ordered from bottom to top, from right to left.
135217 let mut edits_by_index = vec ! [ ] ;
136218 for edit in edits {
@@ -153,13 +235,32 @@ pub fn apply_TextEdits(lines: &[String], edits: &[TextEdit]) -> Fallible<Vec<Str
153235 }
154236
155237 let mut text = lines. join ( "\n " ) ;
238+ let mut offset = position_to_offset ( & lines, & position) ;
156239 for ( start, end, new_text) in edits_by_index {
157240 let start = std:: cmp:: min ( start, text. len ( ) ) ;
158241 let end = std:: cmp:: min ( end, text. len ( ) ) ;
159242 text = String :: new ( ) + & text[ ..start] + new_text + & text[ end..] ;
243+
244+ // Update offset only if the edit's entire range is before it.
245+ // Edits after the offset do not affect it.
246+ // Edits covering the offset cause unpredictable effect.
247+ if end <= offset {
248+ offset += new_text. len ( ) ;
249+ offset -= new_text. matches ( "\r \n " ) . count ( ) ; // line ending is counted as one offset
250+ offset -= std:: cmp:: min ( offset, end - start) ;
251+ }
160252 }
161253
162- Ok ( text. lines ( ) . map ( ToOwned :: to_owned) . collect ( ) )
254+ offset = std:: cmp:: min ( offset, text. len ( ) ) ;
255+
256+ let new_lines: Vec < String > = text. lines ( ) . map ( ToOwned :: to_owned) . collect ( ) ;
257+ let new_position = offset_to_position ( & new_lines, offset) ;
258+ debug ! (
259+ "Position change after applying text edits: {:?} -> {:?}" ,
260+ position, new_position
261+ ) ;
262+
263+ Ok ( ( new_lines, new_position) )
163264}
164265
165266#[ test]
@@ -198,7 +299,12 @@ fn test_apply_TextEdit() {
198299 . to_owned ( ) ,
199300 } ;
200301
201- assert_eq ! ( apply_TextEdits( & lines, & [ edit] ) . unwrap( ) , expect) ;
302+ let position = Position :: new ( 0 , 0 ) ;
303+
304+ // Ignore returned position since the edit's range covers current position and the new position
305+ // is undefined in this case
306+ let ( result, _) = apply_TextEdits ( & lines, & [ edit] , & position) . unwrap ( ) ;
307+ assert_eq ! ( result, expect) ;
202308}
203309
204310#[ test]
@@ -221,7 +327,86 @@ fn test_apply_TextEdit_overlong_end() {
221327 new_text : r#"nb = 123"# . to_owned ( ) ,
222328 } ;
223329
224- assert_eq ! ( apply_TextEdits( & lines, & [ edit] ) . unwrap( ) , expect) ;
330+ let position = Position :: new ( 0 , 1 ) ;
331+
332+ let ( result, _) = apply_TextEdits ( & lines, & [ edit] , & position) . unwrap ( ) ;
333+ assert_eq ! ( result, expect) ;
334+ }
335+
336+ #[ test]
337+ fn test_apply_TextEdit_position ( ) {
338+ let lines: Vec < String > = "abc = 123" . lines ( ) . map ( |l| l. to_owned ( ) ) . collect ( ) ;
339+
340+ let expected_lines: Vec < String > = "newline\n abcde = 123"
341+ . lines ( )
342+ . map ( |l| l. to_owned ( ) )
343+ . collect ( ) ;
344+
345+ let edits = [
346+ TextEdit {
347+ range : Range {
348+ start : Position {
349+ line : 0 ,
350+ character : 1 ,
351+ } ,
352+ end : Position {
353+ line : 0 ,
354+ character : 3 ,
355+ } ,
356+ } ,
357+ new_text : "bcde" . to_owned ( ) ,
358+ } ,
359+ TextEdit {
360+ range : Range {
361+ start : Position {
362+ line : 0 ,
363+ character : 0 ,
364+ } ,
365+ end : Position {
366+ line : 0 ,
367+ character : 0 ,
368+ } ,
369+ } ,
370+ new_text : "newline\n " . to_owned ( ) ,
371+ } ,
372+ ] ;
373+
374+ let position = Position :: new ( 0 , 4 ) ;
375+ let expected_position = Position :: new ( 1 , 6 ) ;
376+
377+ assert_eq ! (
378+ apply_TextEdits( & lines, & edits, & position) . unwrap( ) ,
379+ ( expected_lines, expected_position)
380+ ) ;
381+ }
382+
383+ #[ test]
384+ fn test_apply_TextEdit_CRLF ( ) {
385+ let lines: Vec < String > = "abc = 123" . lines ( ) . map ( |l| l. to_owned ( ) ) . collect ( ) ;
386+
387+ let expected_lines: Vec < String > = "a\r \n bc = 123" . lines ( ) . map ( |l| l. to_owned ( ) ) . collect ( ) ;
388+
389+ let edit = TextEdit {
390+ range : Range {
391+ start : Position {
392+ line : 0 ,
393+ character : 0 ,
394+ } ,
395+ end : Position {
396+ line : 0 ,
397+ character : 1 ,
398+ } ,
399+ } ,
400+ new_text : "a\r \n " . to_owned ( ) ,
401+ } ;
402+
403+ let position = Position :: new ( 0 , 2 ) ;
404+ let expected_position = Position :: new ( 1 , 1 ) ;
405+
406+ assert_eq ! (
407+ apply_TextEdits( & lines, & [ edit] , & position) . unwrap( ) ,
408+ ( expected_lines, expected_position)
409+ ) ;
225410}
226411
227412pub trait Combine {
0 commit comments