Skip to content

Commit f84ed29

Browse files
committed
fix: also handle deletion case
1 parent 0c8f379 commit f84ed29

File tree

2 files changed

+68
-18
lines changed

2 files changed

+68
-18
lines changed

crates/pgt_workspace/src/workspace.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub struct ChangeFileParams {
4646
pub changes: Vec<ChangeParams>,
4747
}
4848

49-
#[derive(Debug, serde::Serialize, serde::Deserialize)]
49+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
5050
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
5151
pub struct ChangeParams {
5252
/// The range of the file that changed. If `None`, the whole file changed.
@@ -58,15 +58,6 @@ impl ChangeParams {
5858
pub fn overwrite(text: String) -> Self {
5959
Self { range: None, text }
6060
}
61-
62-
pub fn push_back(&self, by: TextSize) -> Self {
63-
Self {
64-
range: self
65-
.range
66-
.map(|r| TextRange::new(r.start() + by, r.end() + by)),
67-
text: self.text.clone(),
68-
}
69-
}
7061
}
7162

7263
#[derive(Debug, serde::Serialize, serde::Deserialize)]

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

+67-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
use biome_deserialize::Text;
12
use pgt_text_size::{TextLen, TextRange, TextSize};
2-
use std::ops::{Add, Sub};
3+
use std::{
4+
i64,
5+
ops::{Add, Sub},
6+
};
37

48
use crate::workspace::{ChangeFileParams, ChangeParams};
59

@@ -58,26 +62,37 @@ struct Affected {
5862
impl Document {
5963
/// Applies a file change to the document and returns the affected statements
6064
pub fn apply_file_change(&mut self, change: &ChangeFileParams) -> Vec<StatementChange> {
61-
tracing::debug!("apply_file_change: {:?}", change);
6265
// cleanup all diagnostics with every change because we cannot guarantee that they are still valid
6366
// this is because we know their ranges only by finding slices within the content which is
6467
// very much not guaranteed to result in correct ranges
6568
self.diagnostics.clear();
6669

70+
// when we recieive more than one change, we need to push back the changes based on the
71+
// total range of the previous ones. This is because the ranges are always related to the original state.
6772
let mut changes = Vec::new();
6873

69-
let mut push_back: TextSize = 0.into();
74+
let mut offset: i64 = 0;
7075

7176
for change in &change.changes {
72-
let change = if push_back > 0.into() {
73-
&change.push_back(push_back)
77+
let adjusted_change = if offset != 0 && change.range.is_some() {
78+
&ChangeParams {
79+
text: change.text.clone(),
80+
range: change.range.map(|range| {
81+
let start = u32::from(range.start());
82+
let end = u32::from(range.end());
83+
TextRange::new(
84+
TextSize::from((start as i64 + offset).try_into().unwrap_or(0)),
85+
TextSize::from((end as i64 + offset).try_into().unwrap_or(0)),
86+
)
87+
}),
88+
}
7489
} else {
7590
change
7691
};
7792

78-
changes.extend(self.apply_change(change));
93+
changes.extend(self.apply_change(adjusted_change));
7994

80-
push_back += change.diff_size();
95+
offset += change.change_size();
8196
}
8297

8398
self.version = change.version;
@@ -367,6 +382,18 @@ impl Document {
367382
}
368383

369384
impl ChangeParams {
385+
/// For lack of a better name, this returns the change in size of the text compared to the range
386+
pub fn change_size(&self) -> i64 {
387+
match self.range {
388+
Some(range) => {
389+
let range_length: usize = range.len().into();
390+
let text_length = self.text.chars().count();
391+
text_length as i64 - range_length as i64
392+
}
393+
None => i64::try_from(self.text.chars().count()).unwrap(),
394+
}
395+
}
396+
370397
pub fn diff_size(&self) -> TextSize {
371398
match self.range {
372399
Some(range) => {
@@ -1534,7 +1561,39 @@ mod tests {
15341561
}
15351562

15361563
#[test]
1537-
fn multiple_changes_at_once() {
1564+
fn multiple_deletions_at_once() {
1565+
let path = PgTPath::new("test.sql");
1566+
1567+
let mut doc = Document::new("\n\n\n\nALTER TABLE ONLY \"public\".\"sendout\"\n ADD CONSTRAINT \"sendout_organisation_id_fkey\" FOREIGN
1568+
KEY (\"organisation_id\") REFERENCES \"public\".\"organisation\"(\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;\n".to_string(), 0);
1569+
1570+
let change = ChangeFileParams {
1571+
path: path.clone(),
1572+
version: 1,
1573+
changes: vec![
1574+
ChangeParams {
1575+
range: Some(TextRange::new(31.into(), 38.into())),
1576+
text: "te".to_string(),
1577+
},
1578+
ChangeParams {
1579+
range: Some(TextRange::new(60.into(), 67.into())),
1580+
text: "te".to_string(),
1581+
},
1582+
],
1583+
};
1584+
1585+
let changed = doc.apply_file_change(&change);
1586+
1587+
assert_eq!(doc.content, "\n\n\n\nALTER TABLE ONLY \"public\".\"te\"\n ADD CONSTRAINT \"te_organisation_id_fkey\" FOREIGN
1588+
KEY (\"organisation_id\") REFERENCES \"public\".\"organisation\"(\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;\n");
1589+
1590+
assert_eq!(changed.len(), 2);
1591+
1592+
assert_document_integrity(&doc);
1593+
}
1594+
1595+
#[test]
1596+
fn multiple_additions_at_once() {
15381597
let path = PgTPath::new("test.sql");
15391598

15401599
let mut doc = Document::new("\n\n\n\nALTER TABLE ONLY \"public\".\"sendout\"\n ADD CONSTRAINT \"sendout_organisation_id_fkey\" FOREIGN

0 commit comments

Comments
 (0)