#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "js", napi_derive::napi(object))]
#[cfg_attr(feature = "py", pyo3::pyclass(get_all))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct BufferUpdate {
#[cfg_attr(feature = "serialize", serde(skip_serializing_if = "Option::is_none"))]
pub hash: Option<i64>,
pub version: Vec<i64>,
pub change: TextChange,
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "js", napi_derive::napi(object))]
#[cfg_attr(feature = "py", pyo3::pyclass(get_all))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct TextChange {
pub start_idx: u32,
pub end_idx: u32,
pub content: String,
}
impl TextChange {
pub fn span(&self) -> std::ops::Range<usize> {
self.start_idx as usize..self.end_idx as usize
}
}
#[cfg_attr(feature = "py", pyo3::pymethods)]
impl TextChange {
pub fn is_delete(&self) -> bool {
self.start_idx < self.end_idx
}
pub fn is_insert(&self) -> bool {
!self.content.is_empty()
}
pub fn is_empty(&self) -> bool {
!self.is_delete() && !self.is_insert()
}
pub fn apply(&self, txt: &str) -> String {
let pre_index = std::cmp::min(self.start_idx as usize, txt.len());
let pre = txt.get(..pre_index).unwrap_or("").to_string();
let post = txt.get(self.end_idx as usize..).unwrap_or("").to_string();
format!("{}{}{}", pre, self.content, post)
}
}
#[cfg(test)]
mod tests {
#[test]
fn textchange_apply_works_for_insertions() {
let change = super::TextChange {
start_idx: 5,
end_idx: 5,
content: " cruel".to_string(),
};
let result = change.apply("hello world!");
assert_eq!(result, "hello cruel world!");
}
#[test]
fn textchange_apply_works_for_deletions() {
let change = super::TextChange {
start_idx: 5,
end_idx: 11,
content: "".to_string(),
};
let result = change.apply("hello cruel world!");
assert_eq!(result, "hello world!");
}
#[test]
fn textchange_apply_works_for_replacements() {
let change = super::TextChange {
start_idx: 5,
end_idx: 11,
content: " not very pleasant".to_string(),
};
let result = change.apply("hello cruel world!");
assert_eq!(result, "hello not very pleasant world!");
}
#[test]
fn textchange_apply_never_panics() {
let change = super::TextChange {
start_idx: 100,
end_idx: 110,
content: "a very long string \n which totally matters".to_string(),
};
let result = change.apply("a short text");
assert_eq!(
result,
"a short texta very long string \n which totally matters"
);
}
#[test]
fn empty_textchange_doesnt_alter_buffer() {
let change = super::TextChange {
start_idx: 42,
end_idx: 42,
content: "".to_string(),
};
let result = change.apply("some important text");
assert_eq!(result, "some important text");
}
}