mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-25 10:33:21 +03:00
check size when applying operations
This commit is contained in:
parent
887b676acb
commit
f78d5b104f
@ -1,3 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use difference::{Changeset, Difference};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -11,13 +12,40 @@ pub enum Operation {
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn apply(&self, text: &mut Vec<char>) {
|
||||
pub fn apply(&self, text: &mut Vec<char>) -> Result<()> {
|
||||
match self {
|
||||
Operation::Insert((index, chunk)) => {
|
||||
text.splice(*index as usize..*index as usize, chunk.chars());
|
||||
if *index as usize > text.len() {
|
||||
Err(anyhow::anyhow!(
|
||||
"Index out of bounds, {} > {}",
|
||||
index,
|
||||
text.len()
|
||||
))
|
||||
} else if *index as usize == text.len() {
|
||||
text.extend(chunk.chars());
|
||||
Ok(())
|
||||
} else {
|
||||
text.splice(*index as usize..*index as usize, chunk.chars());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Operation::Delete((index, len)) => {
|
||||
text.splice(*index as usize..(*index + *len) as usize, "".chars());
|
||||
if *index as usize > text.len() {
|
||||
Err(anyhow::anyhow!(
|
||||
"Index out of bounds, {} > {}",
|
||||
index,
|
||||
text.len()
|
||||
))
|
||||
} else if *index as usize + *len as usize > text.len() {
|
||||
Err(anyhow::anyhow!(
|
||||
"Index + length out of bounds, {} > {}",
|
||||
index + len,
|
||||
text.len()
|
||||
))
|
||||
} else {
|
||||
text.splice(*index as usize..(*index + *len) as usize, "".chars());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use super::{deltas, operations};
|
||||
use anyhow::Result;
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TextDocument {
|
||||
@ -8,20 +8,21 @@ pub struct TextDocument {
|
||||
deltas: Vec<deltas::Delta>,
|
||||
}
|
||||
|
||||
fn apply_deltas(doc: &mut Vec<char>, deltas: &Vec<deltas::Delta>) {
|
||||
fn apply_deltas(doc: &mut Vec<char>, deltas: &Vec<deltas::Delta>) -> Result<()> {
|
||||
for delta in deltas {
|
||||
for operation in &delta.operations {
|
||||
operation.apply(doc);
|
||||
operation.apply(doc)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl TextDocument {
|
||||
// creates a new text document from a deltas.
|
||||
pub fn from_deltas(deltas: Vec<deltas::Delta>) -> TextDocument {
|
||||
pub fn from_deltas(deltas: Vec<deltas::Delta>) -> Result<TextDocument> {
|
||||
let mut doc = vec![];
|
||||
apply_deltas(&mut doc, &deltas);
|
||||
TextDocument { doc, deltas }
|
||||
apply_deltas(&mut doc, &deltas)?;
|
||||
Ok(TextDocument { doc, deltas })
|
||||
}
|
||||
|
||||
pub fn get_deltas(&self) -> Vec<deltas::Delta> {
|
||||
@ -29,18 +30,18 @@ impl TextDocument {
|
||||
}
|
||||
|
||||
// returns a text document where internal state is seeded with value, and deltas are applied.
|
||||
pub fn new(value: &str, deltas: Vec<deltas::Delta>) -> TextDocument {
|
||||
pub fn new(value: &str, deltas: Vec<deltas::Delta>) -> Result<TextDocument> {
|
||||
let mut all_deltas = vec![deltas::Delta {
|
||||
operations: operations::get_delta_operations("", value),
|
||||
timestamp_ms: 0,
|
||||
}];
|
||||
all_deltas.append(&mut deltas.clone());
|
||||
let mut doc = vec![];
|
||||
apply_deltas(&mut doc, &all_deltas);
|
||||
TextDocument { doc, deltas }
|
||||
apply_deltas(&mut doc, &all_deltas)?;
|
||||
Ok(TextDocument { doc, deltas })
|
||||
}
|
||||
|
||||
pub fn update(&mut self, value: &str) -> bool {
|
||||
pub fn update(&mut self, value: &str) -> Result<bool> {
|
||||
let diffs = operations::get_delta_operations(&self.to_string(), value);
|
||||
let event = deltas::Delta {
|
||||
operations: diffs,
|
||||
@ -50,11 +51,11 @@ impl TextDocument {
|
||||
.as_millis(),
|
||||
};
|
||||
if event.operations.len() == 0 {
|
||||
return false;
|
||||
return Ok(false);
|
||||
}
|
||||
apply_deltas(&mut self.doc, &vec![event.clone()]);
|
||||
apply_deltas(&mut self.doc, &vec![event.clone()])?;
|
||||
self.deltas.push(event);
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
|
@ -3,14 +3,18 @@ use crate::deltas::{operations::Operation, text_document::TextDocument, Delta};
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let document = TextDocument::new("hello world", vec![]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let document = document.unwrap();
|
||||
assert_eq!(document.to_string(), "hello world");
|
||||
assert_eq!(document.get_deltas().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update() {
|
||||
let mut document = TextDocument::new("hello world", vec![]);
|
||||
document.update("hello world!");
|
||||
let document = TextDocument::new("hello world", vec![]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let mut document = document.unwrap();
|
||||
document.update("hello world!").unwrap();
|
||||
assert_eq!(document.to_string(), "hello world!");
|
||||
assert_eq!(document.get_deltas().len(), 1);
|
||||
assert_eq!(document.get_deltas()[0].operations.len(), 1);
|
||||
@ -22,8 +26,10 @@ fn test_update() {
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
let mut document = TextDocument::from_deltas(vec![]);
|
||||
document.update("hello world!");
|
||||
let document = TextDocument::from_deltas(vec![]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let mut document = document.unwrap();
|
||||
document.update("hello world!").unwrap();
|
||||
assert_eq!(document.to_string(), "hello world!");
|
||||
assert_eq!(document.get_deltas().len(), 1);
|
||||
assert_eq!(document.get_deltas()[0].operations.len(), 1);
|
||||
@ -52,14 +58,18 @@ fn test_from_deltas() {
|
||||
],
|
||||
},
|
||||
]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let document = document.unwrap();
|
||||
assert_eq!(document.to_string(), "held!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex_line() {
|
||||
let mut document = TextDocument::from_deltas(vec![]);
|
||||
let document = TextDocument::from_deltas(vec![]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let mut document = document.unwrap();
|
||||
|
||||
document.update("hello");
|
||||
document.update("hello").unwrap();
|
||||
assert_eq!(document.to_string(), "hello");
|
||||
assert_eq!(document.get_deltas().len(), 1);
|
||||
assert_eq!(document.get_deltas()[0].operations.len(), 1);
|
||||
@ -68,7 +78,7 @@ fn test_complex_line() {
|
||||
Operation::Insert((0, "hello".to_string()))
|
||||
);
|
||||
|
||||
document.update("hello world");
|
||||
document.update("hello world").unwrap();
|
||||
assert_eq!(document.to_string(), "hello world");
|
||||
assert_eq!(document.get_deltas().len(), 2);
|
||||
assert_eq!(document.get_deltas()[1].operations.len(), 1);
|
||||
@ -77,7 +87,7 @@ fn test_complex_line() {
|
||||
Operation::Insert((5, " world".to_string()))
|
||||
);
|
||||
|
||||
document.update("held!");
|
||||
document.update("held!").unwrap();
|
||||
assert_eq!(document.to_string(), "held!");
|
||||
assert_eq!(document.get_deltas().len(), 3);
|
||||
assert_eq!(document.get_deltas()[2].operations.len(), 2);
|
||||
@ -93,9 +103,11 @@ fn test_complex_line() {
|
||||
|
||||
#[test]
|
||||
fn test_multiline_add() {
|
||||
let mut document = TextDocument::from_deltas(vec![]);
|
||||
let document = TextDocument::from_deltas(vec![]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let mut document = document.unwrap();
|
||||
|
||||
document.update("first");
|
||||
document.update("first").unwrap();
|
||||
assert_eq!(document.to_string(), "first");
|
||||
assert_eq!(document.get_deltas().len(), 1);
|
||||
assert_eq!(document.get_deltas()[0].operations.len(), 1);
|
||||
@ -104,7 +116,7 @@ fn test_multiline_add() {
|
||||
Operation::Insert((0, "first".to_string()))
|
||||
);
|
||||
|
||||
document.update("first\ntwo");
|
||||
document.update("first\ntwo").unwrap();
|
||||
assert_eq!(document.to_string(), "first\ntwo");
|
||||
assert_eq!(document.get_deltas().len(), 2);
|
||||
assert_eq!(document.get_deltas()[1].operations.len(), 1);
|
||||
@ -113,7 +125,7 @@ fn test_multiline_add() {
|
||||
Operation::Insert((5, "\ntwo".to_string()))
|
||||
);
|
||||
|
||||
document.update("first line\nline two");
|
||||
document.update("first line\nline two").unwrap();
|
||||
assert_eq!(document.to_string(), "first line\nline two");
|
||||
assert_eq!(document.get_deltas().len(), 3);
|
||||
assert_eq!(document.get_deltas()[2].operations.len(), 2);
|
||||
@ -129,9 +141,11 @@ fn test_multiline_add() {
|
||||
|
||||
#[test]
|
||||
fn test_multiline_remove() {
|
||||
let mut document = TextDocument::from_deltas(vec![]);
|
||||
let document = TextDocument::from_deltas(vec![]);
|
||||
assert_eq!(document.is_ok(), true);
|
||||
let mut document = document.unwrap();
|
||||
|
||||
document.update("first line\nline two");
|
||||
document.update("first line\nline two").unwrap();
|
||||
assert_eq!(document.to_string(), "first line\nline two");
|
||||
assert_eq!(document.get_deltas().len(), 1);
|
||||
assert_eq!(document.get_deltas()[0].operations.len(), 1);
|
||||
@ -140,7 +154,7 @@ fn test_multiline_remove() {
|
||||
Operation::Insert((0, "first line\nline two".to_string()))
|
||||
);
|
||||
|
||||
document.update("first\ntwo");
|
||||
document.update("first\ntwo").unwrap();
|
||||
assert_eq!(document.to_string(), "first\ntwo");
|
||||
assert_eq!(document.get_deltas().len(), 2);
|
||||
assert_eq!(document.get_deltas()[1].operations.len(), 2);
|
||||
@ -153,7 +167,7 @@ fn test_multiline_remove() {
|
||||
Operation::Delete((6, 5))
|
||||
);
|
||||
|
||||
document.update("first");
|
||||
document.update("first").unwrap();
|
||||
assert_eq!(document.to_string(), "first");
|
||||
assert_eq!(document.get_deltas().len(), 3);
|
||||
assert_eq!(document.get_deltas()[2].operations.len(), 1);
|
||||
@ -162,7 +176,7 @@ fn test_multiline_remove() {
|
||||
Operation::Delete((5, 4))
|
||||
);
|
||||
|
||||
document.update("");
|
||||
document.update("").unwrap();
|
||||
assert_eq!(document.to_string(), "");
|
||||
assert_eq!(document.get_deltas().len(), 4);
|
||||
assert_eq!(document.get_deltas()[3].operations.len(), 1);
|
||||
|
Loading…
Reference in New Issue
Block a user