@@ -89,16 +89,7 @@ impl TextDocument {
8989 let mut line_index = self . line_index . clone ( ) ;
9090
9191 for change in changes {
92- if let Some ( range) = change. range {
93- let start_offset =
94- line_index. offset ( range. start , & content, encoding) . get ( ) as usize ;
95- let end_offset = line_index. offset ( range. end , & content, encoding) . get ( ) as usize ;
96-
97- content. replace_range ( start_offset..end_offset, & change. text ) ;
98- } else {
99- content = change. text ;
100- }
101-
92+ content = change. apply ( & content, & line_index, encoding) ;
10293 line_index = LineIndex :: from ( content. as_str ( ) ) ;
10394 }
10495
@@ -109,8 +100,47 @@ impl TextDocument {
109100}
110101
111102pub struct DocumentChange {
112- pub range : Option < Range > ,
113- pub text : String ,
103+ range : Option < Range > ,
104+ text : String ,
105+ }
106+
107+ impl DocumentChange {
108+ #[ must_use]
109+ pub fn new ( range : Option < Range > , text : String ) -> Self {
110+ Self { range, text }
111+ }
112+
113+ #[ must_use]
114+ pub fn range ( & self ) -> & Option < Range > {
115+ & self . range
116+ }
117+
118+ #[ must_use]
119+ pub fn text ( & self ) -> & str {
120+ & self . text
121+ }
122+
123+ /// Apply this change to content, returning the new content
124+ #[ must_use]
125+ pub fn apply (
126+ & self ,
127+ content : & str ,
128+ line_index : & LineIndex ,
129+ encoding : PositionEncoding ,
130+ ) -> String {
131+ if let Some ( range) = & self . range {
132+ let start_offset = line_index. offset ( content, range. start ( ) , encoding) . get ( ) as usize ;
133+ let end_offset = line_index. offset ( content, range. end ( ) , encoding) . get ( ) as usize ;
134+
135+ let mut result = String :: with_capacity ( content. len ( ) + self . text . len ( ) ) ;
136+ result. push_str ( & content[ ..start_offset] ) ;
137+ result. push_str ( & self . text ) ;
138+ result. push_str ( & content[ end_offset..] ) ;
139+ result
140+ } else {
141+ self . text . clone ( )
142+ }
143+ }
114144}
115145
116146#[ cfg( test) ]
@@ -165,13 +195,10 @@ mod tests {
165195 fn test_incremental_update_single_change ( ) {
166196 let mut doc = text_document ( "Hello world" , 1 , LanguageId :: Other ) ;
167197
168- let changes = vec ! [ DocumentChange {
169- range: Some ( Range {
170- start: LineCol :: new( 0 , 6 ) ,
171- end: LineCol :: new( 0 , 11 ) ,
172- } ) ,
173- text: "Rust" . to_string( ) ,
174- } ] ;
198+ let changes = vec ! [ DocumentChange :: new(
199+ Some ( Range :: new( LineCol :: new( 0 , 6 ) , LineCol :: new( 0 , 11 ) ) ) ,
200+ "Rust" . to_string( ) ,
201+ ) ] ;
175202
176203 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
177204 assert_eq ! ( doc. content( ) , "Hello Rust" ) ;
@@ -183,20 +210,14 @@ mod tests {
183210 let mut doc = text_document ( "First line\n Second line\n Third line" , 1 , LanguageId :: Other ) ;
184211
185212 let changes = vec ! [
186- DocumentChange {
187- range: Some ( Range {
188- start: LineCol :: new( 0 , 0 ) ,
189- end: LineCol :: new( 0 , 5 ) ,
190- } ) ,
191- text: "1st" . to_string( ) ,
192- } ,
193- DocumentChange {
194- range: Some ( Range {
195- start: LineCol :: new( 2 , 0 ) ,
196- end: LineCol :: new( 2 , 5 ) ,
197- } ) ,
198- text: "3rd" . to_string( ) ,
199- } ,
213+ DocumentChange :: new(
214+ Some ( Range :: new( LineCol :: new( 0 , 0 ) , LineCol :: new( 0 , 5 ) ) ) ,
215+ "1st" . to_string( ) ,
216+ ) ,
217+ DocumentChange :: new(
218+ Some ( Range :: new( LineCol :: new( 2 , 0 ) , LineCol :: new( 2 , 5 ) ) ) ,
219+ "3rd" . to_string( ) ,
220+ ) ,
200221 ] ;
201222
202223 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
@@ -207,13 +228,10 @@ mod tests {
207228 fn test_incremental_update_insertion ( ) {
208229 let mut doc = text_document ( "Hello world" , 1 , LanguageId :: Other ) ;
209230
210- let changes = vec ! [ DocumentChange {
211- range: Some ( Range {
212- start: LineCol :: new( 0 , 5 ) ,
213- end: LineCol :: new( 0 , 5 ) ,
214- } ) ,
215- text: " beautiful" . to_string( ) ,
216- } ] ;
231+ let changes = vec ! [ DocumentChange :: new(
232+ Some ( Range :: new( LineCol :: new( 0 , 5 ) , LineCol :: new( 0 , 5 ) ) ) ,
233+ " beautiful" . to_string( ) ,
234+ ) ] ;
217235
218236 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
219237 assert_eq ! ( doc. content( ) , "Hello beautiful world" ) ;
@@ -223,13 +241,10 @@ mod tests {
223241 fn test_incremental_update_deletion ( ) {
224242 let mut doc = text_document ( "Hello beautiful world" , 1 , LanguageId :: Other ) ;
225243
226- let changes = vec ! [ DocumentChange {
227- range: Some ( Range {
228- start: LineCol :: new( 0 , 6 ) ,
229- end: LineCol :: new( 0 , 16 ) ,
230- } ) ,
231- text: String :: new( ) ,
232- } ] ;
244+ let changes = vec ! [ DocumentChange :: new(
245+ Some ( Range :: new( LineCol :: new( 0 , 6 ) , LineCol :: new( 0 , 16 ) ) ) ,
246+ String :: new( ) ,
247+ ) ] ;
233248
234249 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
235250 assert_eq ! ( doc. content( ) , "Hello world" ) ;
@@ -239,10 +254,10 @@ mod tests {
239254 fn test_full_document_replacement ( ) {
240255 let mut doc = text_document ( "Old content" , 1 , LanguageId :: Other ) ;
241256
242- let changes = vec ! [ DocumentChange {
243- range : None ,
244- text : "Completely new content" . to_string( ) ,
245- } ] ;
257+ let changes = vec ! [ DocumentChange :: new (
258+ None ,
259+ "Completely new content" . to_string( ) ,
260+ ) ] ;
246261
247262 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
248263 assert_eq ! ( doc. content( ) , "Completely new content" ) ;
@@ -253,13 +268,10 @@ mod tests {
253268 fn test_incremental_update_multiline ( ) {
254269 let mut doc = text_document ( "Line 1\n Line 2\n Line 3" , 1 , LanguageId :: Other ) ;
255270
256- let changes = vec ! [ DocumentChange {
257- range: Some ( Range {
258- start: LineCol :: new( 0 , 5 ) ,
259- end: LineCol :: new( 2 , 4 ) ,
260- } ) ,
261- text: "A\n B\n C" . to_string( ) ,
262- } ] ;
271+ let changes = vec ! [ DocumentChange :: new(
272+ Some ( Range :: new( LineCol :: new( 0 , 5 ) , LineCol :: new( 2 , 4 ) ) ) ,
273+ "A\n B\n C" . to_string( ) ,
274+ ) ] ;
263275
264276 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
265277 assert_eq ! ( doc. content( ) , "Line A\n B\n C 3" ) ;
@@ -269,13 +281,10 @@ mod tests {
269281 fn test_incremental_update_with_emoji ( ) {
270282 let mut doc = text_document ( "Hello 🌍 world" , 1 , LanguageId :: Other ) ;
271283
272- let changes = vec ! [ DocumentChange {
273- range: Some ( Range {
274- start: LineCol :: new( 0 , 9 ) ,
275- end: LineCol :: new( 0 , 14 ) ,
276- } ) ,
277- text: "Rust" . to_string( ) ,
278- } ] ;
284+ let changes = vec ! [ DocumentChange :: new(
285+ Some ( Range :: new( LineCol :: new( 0 , 9 ) , LineCol :: new( 0 , 14 ) ) ) ,
286+ "Rust" . to_string( ) ,
287+ ) ] ;
279288
280289 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
281290 assert_eq ! ( doc. content( ) , "Hello 🌍 Rust" ) ;
@@ -285,13 +294,10 @@ mod tests {
285294 fn test_incremental_update_newline_at_end ( ) {
286295 let mut doc = text_document ( "Hello" , 1 , LanguageId :: Other ) ;
287296
288- let changes = vec ! [ DocumentChange {
289- range: Some ( Range {
290- start: LineCol :: new( 0 , 5 ) ,
291- end: LineCol :: new( 0 , 5 ) ,
292- } ) ,
293- text: "\n World" . to_string( ) ,
294- } ] ;
297+ let changes = vec ! [ DocumentChange :: new(
298+ Some ( Range :: new( LineCol :: new( 0 , 5 ) , LineCol :: new( 0 , 5 ) ) ) ,
299+ "\n World" . to_string( ) ,
300+ ) ] ;
295301
296302 doc. update ( changes, 2 , PositionEncoding :: Utf16 ) ;
297303 assert_eq ! ( doc. content( ) , "Hello\n World" ) ;
0 commit comments