@@ -63,11 +63,33 @@ impl Document {
63
63
// very much not guaranteed to result in correct ranges
64
64
self . diagnostics . clear ( ) ;
65
65
66
- let changes = change
67
- . changes
68
- . iter ( )
69
- . flat_map ( |c| self . apply_change ( c) )
70
- . collect ( ) ;
66
+ // when we recieive more than one change, we need to push back the changes based on the
67
+ // total range of the previous ones. This is because the ranges are always related to the original state.
68
+ let mut changes = Vec :: new ( ) ;
69
+
70
+ let mut offset: i64 = 0 ;
71
+
72
+ for change in & change. changes {
73
+ let adjusted_change = if offset != 0 && change. range . is_some ( ) {
74
+ & ChangeParams {
75
+ text : change. text . clone ( ) ,
76
+ range : change. range . map ( |range| {
77
+ let start = u32:: from ( range. start ( ) ) ;
78
+ let end = u32:: from ( range. end ( ) ) ;
79
+ TextRange :: new (
80
+ TextSize :: from ( ( start as i64 + offset) . try_into ( ) . unwrap_or ( 0 ) ) ,
81
+ TextSize :: from ( ( end as i64 + offset) . try_into ( ) . unwrap_or ( 0 ) ) ,
82
+ )
83
+ } ) ,
84
+ }
85
+ } else {
86
+ change
87
+ } ;
88
+
89
+ changes. extend ( self . apply_change ( adjusted_change) ) ;
90
+
91
+ offset += change. change_size ( ) ;
92
+ }
71
93
72
94
self . version = change. version ;
73
95
@@ -356,6 +378,18 @@ impl Document {
356
378
}
357
379
358
380
impl ChangeParams {
381
+ /// For lack of a better name, this returns the change in size of the text compared to the range
382
+ pub fn change_size ( & self ) -> i64 {
383
+ match self . range {
384
+ Some ( range) => {
385
+ let range_length: usize = range. len ( ) . into ( ) ;
386
+ let text_length = self . text . chars ( ) . count ( ) ;
387
+ text_length as i64 - range_length as i64
388
+ }
389
+ None => i64:: try_from ( self . text . chars ( ) . count ( ) ) . unwrap ( ) ,
390
+ }
391
+ }
392
+
359
393
pub fn diff_size ( & self ) -> TextSize {
360
394
match self . range {
361
395
Some ( range) => {
@@ -1522,6 +1556,70 @@ mod tests {
1522
1556
assert_document_integrity ( & doc) ;
1523
1557
}
1524
1558
1559
+ #[ test]
1560
+ fn multiple_deletions_at_once ( ) {
1561
+ let path = PgTPath :: new ( "test.sql" ) ;
1562
+
1563
+ let mut doc = Document :: new ( "\n \n \n \n ALTER TABLE ONLY \" public\" .\" sendout\" \n ADD CONSTRAINT \" sendout_organisation_id_fkey\" FOREIGN
1564
+ KEY (\" organisation_id\" ) REFERENCES \" public\" .\" organisation\" (\" id\" ) ON UPDATE RESTRICT ON DELETE CASCADE;\n " . to_string ( ) , 0 ) ;
1565
+
1566
+ let change = ChangeFileParams {
1567
+ path : path. clone ( ) ,
1568
+ version : 1 ,
1569
+ changes : vec ! [
1570
+ ChangeParams {
1571
+ range: Some ( TextRange :: new( 31 . into( ) , 38 . into( ) ) ) ,
1572
+ text: "te" . to_string( ) ,
1573
+ } ,
1574
+ ChangeParams {
1575
+ range: Some ( TextRange :: new( 60 . into( ) , 67 . into( ) ) ) ,
1576
+ text: "te" . to_string( ) ,
1577
+ } ,
1578
+ ] ,
1579
+ } ;
1580
+
1581
+ let changed = doc. apply_file_change ( & change) ;
1582
+
1583
+ assert_eq ! ( doc. content, "\n \n \n \n ALTER TABLE ONLY \" public\" .\" te\" \n ADD CONSTRAINT \" te_organisation_id_fkey\" FOREIGN
1584
+ KEY (\" organisation_id\" ) REFERENCES \" public\" .\" organisation\" (\" id\" ) ON UPDATE RESTRICT ON DELETE CASCADE;\n " ) ;
1585
+
1586
+ assert_eq ! ( changed. len( ) , 2 ) ;
1587
+
1588
+ assert_document_integrity ( & doc) ;
1589
+ }
1590
+
1591
+ #[ test]
1592
+ fn multiple_additions_at_once ( ) {
1593
+ let path = PgTPath :: new ( "test.sql" ) ;
1594
+
1595
+ let mut doc = Document :: new ( "\n \n \n \n ALTER TABLE ONLY \" public\" .\" sendout\" \n ADD CONSTRAINT \" sendout_organisation_id_fkey\" FOREIGN
1596
+ KEY (\" organisation_id\" ) REFERENCES \" public\" .\" organisation\" (\" id\" ) ON UPDATE RESTRICT ON DELETE CASCADE;\n " . to_string ( ) , 0 ) ;
1597
+
1598
+ let change = ChangeFileParams {
1599
+ path : path. clone ( ) ,
1600
+ version : 1 ,
1601
+ changes : vec ! [
1602
+ ChangeParams {
1603
+ range: Some ( TextRange :: new( 31 . into( ) , 38 . into( ) ) ) ,
1604
+ text: "omni_channel_message" . to_string( ) ,
1605
+ } ,
1606
+ ChangeParams {
1607
+ range: Some ( TextRange :: new( 60 . into( ) , 67 . into( ) ) ) ,
1608
+ text: "omni_channel_message" . to_string( ) ,
1609
+ } ,
1610
+ ] ,
1611
+ } ;
1612
+
1613
+ let changed = doc. apply_file_change ( & change) ;
1614
+
1615
+ assert_eq ! ( doc. content, "\n \n \n \n ALTER TABLE ONLY \" public\" .\" omni_channel_message\" \n ADD CONSTRAINT \" omni_channel_message_organisation_id_fkey\" FOREIGN
1616
+ KEY (\" organisation_id\" ) REFERENCES \" public\" .\" organisation\" (\" id\" ) ON UPDATE RESTRICT ON DELETE CASCADE;\n " ) ;
1617
+
1618
+ assert_eq ! ( changed. len( ) , 2 ) ;
1619
+
1620
+ assert_document_integrity ( & doc) ;
1621
+ }
1622
+
1525
1623
#[ test]
1526
1624
fn remove_inbetween_whitespace ( ) {
1527
1625
let path = PgTPath :: new ( "test.sql" ) ;
0 commit comments