Skip to content

Commit 6469ce3

Browse files
authored
fix: properly handle multiple changes at once (#389)
* fix: multiple changes at once * fix: also handle deletion case * cleanup * cleanup * cleanup
1 parent 5945c1b commit 6469ce3

File tree

1 file changed

+103
-5
lines changed
  • crates/pgt_workspace/src/workspace/server

1 file changed

+103
-5
lines changed

crates/pgt_workspace/src/workspace/server/change.rs

+103-5
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,33 @@ impl Document {
6363
// very much not guaranteed to result in correct ranges
6464
self.diagnostics.clear();
6565

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+
}
7193

7294
self.version = change.version;
7395

@@ -356,6 +378,18 @@ impl Document {
356378
}
357379

358380
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+
359393
pub fn diff_size(&self) -> TextSize {
360394
match self.range {
361395
Some(range) => {
@@ -1522,6 +1556,70 @@ mod tests {
15221556
assert_document_integrity(&doc);
15231557
}
15241558

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\nALTER 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\nALTER 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\nALTER 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\nALTER 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+
15251623
#[test]
15261624
fn remove_inbetween_whitespace() {
15271625
let path = PgTPath::new("test.sql");

0 commit comments

Comments
 (0)