more working

This commit is contained in:
Andras Schmelczer 2024-11-14 22:30:16 +00:00
parent 3dfc3c9680
commit 2c4737999f
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
2 changed files with 48 additions and 53 deletions

View file

@ -1,6 +1,5 @@
use ropey::Rope; use ropey::Rope;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use similar::{Change, ChangeTag};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::Display; use std::fmt::Display;
@ -36,7 +35,7 @@ impl Display for Operation {
} }
impl Operation { impl Operation {
pub fn create(tag: ChangeTag, index: i64, text: &str) -> Result<Self, SyncLibError> { pub fn create_insert(index: i64, text: &str) -> Result<Option<Self>, SyncLibError> {
if index < 0 { if index < 0 {
return Err(SyncLibError::NegativeOperationIndexError(format!( return Err(SyncLibError::NegativeOperationIndexError(format!(
"Index {} is negative", "Index {} is negative",
@ -44,29 +43,17 @@ impl Operation {
))); )));
} }
Ok(match tag { if text.is_empty() {
ChangeTag::Insert => Operation::Insert { return Ok(None);
index, }
text: text.to_string(),
}, Ok(Some(Operation::Insert {
ChangeTag::Delete => Operation::Delete { index,
index, text: text.to_string(),
deleted_character_count: text.chars().count() as i64, }))
},
_ => {
return Err(SyncLibError::OperationConversionError(format!(
"Cannot convert editing operation because {:?}",
tag
)))
}
})
} }
pub fn create_insert(index: i64, text: &str) -> Result<Self, SyncLibError> { pub fn create_delete(index: i64, length: i64) -> Result<Option<Self>, SyncLibError> {
Self::create(ChangeTag::Insert, index, text)
}
pub fn create_delete(index: i64, length: i64) -> Result<Self, SyncLibError> {
if index < 0 { if index < 0 {
return Err(SyncLibError::NegativeOperationIndexError(format!( return Err(SyncLibError::NegativeOperationIndexError(format!(
"Index {} is negative", "Index {} is negative",
@ -81,16 +68,20 @@ impl Operation {
))); )));
} }
Ok(Operation::Delete { if length == 0 {
return Ok(None);
}
Ok(Some(Operation::Delete {
index, index,
deleted_character_count: length, deleted_character_count: length,
}) }))
} }
pub fn apply<'a>(&self, rope_text: &'a mut Rope) -> Result<&'a mut Rope, SyncLibError> { pub fn apply<'a>(&self, rope_text: &'a mut Rope) -> Result<&'a mut Rope, SyncLibError> {
let index: usize = self.start_index() as usize; let index: usize = self.start_index() as usize;
match self { match self {
Operation::Insert { text, .. } => rope_text.try_insert(index, &text).map_err(|err| { Operation::Insert { text, .. } => rope_text.try_insert(index, text).map_err(|err| {
SyncLibError::OperationApplicationError(format!("Failed to insert text: {}", err)) SyncLibError::OperationApplicationError(format!("Failed to insert text: {}", err))
}), }),
Operation::Delete { Operation::Delete {
@ -133,6 +124,10 @@ impl Operation {
} }
} }
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the range of indices of characters that the operation affects, inclusive. /// Returns the range of indices of characters that the operation affects, inclusive.
pub fn range(&self) -> std::ops::RangeInclusive<i64> { pub fn range(&self) -> std::ops::RangeInclusive<i64> {
self.start_index()..=self.end_index() self.start_index()..=self.end_index()
@ -144,6 +139,7 @@ impl Operation {
index, index,
text: text.clone(), text: text.clone(),
}, },
Operation::Delete { Operation::Delete {
deleted_character_count, deleted_character_count,
.. ..
@ -177,7 +173,7 @@ impl Ord for Operation {
impl PartialOrd for Operation { impl PartialOrd for Operation {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other)) Some(self.cmp(other))
} }
} }
@ -187,8 +183,6 @@ mod tests {
#[test] #[test]
fn test_creation_errors() { fn test_creation_errors() {
insta::assert_debug_snapshot!(Operation::create(ChangeTag::Insert, -1, "hi"));
insta::assert_debug_snapshot!(Operation::create(ChangeTag::Equal, 0, "hi"));
insta::assert_debug_snapshot!(Operation::create_insert(-1, "hi")); insta::assert_debug_snapshot!(Operation::create_insert(-1, "hi"));
insta::assert_debug_snapshot!(Operation::create_delete(0, -1)); insta::assert_debug_snapshot!(Operation::create_delete(0, -1));
insta::assert_debug_snapshot!(Operation::create_delete(-1, -1)); insta::assert_debug_snapshot!(Operation::create_delete(-1, -1));
@ -212,7 +206,7 @@ mod tests {
#[test] #[test]
fn test_apply_delete_with_create() -> Result<(), SyncLibError> { fn test_apply_delete_with_create() -> Result<(), SyncLibError> {
let mut rope = Rope::from_str("hello world"); let mut rope = Rope::from_str("hello world");
let operation = Operation::create(ChangeTag::Delete, 6, "world")?; let operation = Operation::create_delete(5, 6)?.unwrap();
operation.apply(&mut rope)?; operation.apply(&mut rope)?;
@ -239,7 +233,7 @@ mod tests {
#[test] #[test]
fn test_apply_insert_with_create() -> Result<(), SyncLibError> { fn test_apply_insert_with_create() -> Result<(), SyncLibError> {
let mut rope = Rope::from_str("hello"); let mut rope = Rope::from_str("hello");
let operation = Operation::create(ChangeTag::Insert, 5, " my friend")?; let operation = Operation::create_insert(5, " my friend")?.unwrap();
operation.apply(&mut rope)?; operation.apply(&mut rope)?;

View file

@ -1,11 +1,9 @@
use super::{operation, Operation}; use super::Operation;
use crate::errors::SyncLibError; use crate::errors::SyncLibError;
use log::info;
use ropey::Rope; use ropey::Rope;
use serde::{de, Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use similar::utils::diff_graphemes; use similar::Algorithm;
use similar::{utils::TextDiffRemapper, ChangeTag, TextDiff}; use similar::{utils::TextDiffRemapper, ChangeTag, TextDiff};
use similar::{Algorithm, DiffableStrRef};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
struct MergeContext { struct MergeContext {
@ -43,7 +41,7 @@ impl OperationSequence {
if diff_ratio > diff_ratio_threshold { if diff_ratio > diff_ratio_threshold {
return Err(SyncLibError::DiffTooLarge { return Err(SyncLibError::DiffTooLarge {
diff_ratio, diff_ratio,
diff_ratio_limit: diff_ratio_threshold as f32, diff_ratio_limit: diff_ratio_threshold,
}); });
} }
@ -56,16 +54,18 @@ impl OperationSequence {
.map(|(tag, text)| match tag { .map(|(tag, text)| match tag {
ChangeTag::Equal => { ChangeTag::Equal => {
index += text.chars().count(); index += text.chars().count();
None Ok(None)
} }
ChangeTag::Insert => { ChangeTag::Insert => {
let result = Some(Operation::create(tag, index as i64, text)); let result = Operation::create_insert(index as i64, text);
index += text.chars().count(); index += text.chars().count();
result result
} }
ChangeTag::Delete => Some(Operation::create(tag, index as i64, text)), ChangeTag::Delete => {
Operation::create_delete(index as i64, text.chars().count() as i64)
}
}) })
.flat_map(Option::into_iter) .flat_map(|result| result.transpose().into_iter())
.collect::<Result<Vec<_>, SyncLibError>>() .collect::<Result<Vec<_>, SyncLibError>>()
.map(Self::new) .map(Self::new)
} }
@ -93,7 +93,7 @@ impl OperationSequence {
loop { loop {
let left_op = self.operations.get(left_index); let left_op = self.operations.get(left_index);
let right_op = other.operations.get(right_index); let right_op = other.operations.get(right_index);
println!(""); println!();
println!("{:#?} <> {:#?}", left_op.cloned(), right_op.cloned()); println!("{:#?} <> {:#?}", left_op.cloned(), right_op.cloned());
println!("{:?} <> {:?}", left_delete_context, right_delete_context); println!("{:?} <> {:?}", left_delete_context, right_delete_context);
@ -161,12 +161,12 @@ impl OperationSequence {
if last_delete if last_delete
.range() .range()
.contains(&(&operation.start_index() + affecting_context.shift)) .contains(&(operation.start_index() + affecting_context.shift))
{ {
affecting_context.last_delete = Some(Operation::create_delete( affecting_context.last_delete = Operation::create_delete(
last_delete.start_index() + operation.len(), last_delete.start_index() + operation.len(),
0.max(last_delete.len() - operation.len()), 0.max(last_delete.len() - operation.len()),
)?); )?;
Some(operation.with_index(last_delete.start_index())) Some(operation.with_index(last_delete.start_index()))
} else { } else {
@ -188,15 +188,15 @@ impl OperationSequence {
{ {
affecting_context.shift -= affecting_context.shift -=
shifted_operation.start_index() - last_delete.start_index(); shifted_operation.start_index() - last_delete.start_index();
affecting_context.last_delete = Some(Operation::create_delete( affecting_context.last_delete = Operation::create_delete(
shifted_operation.end_index() + 1, shifted_operation.start_index(),
last_delete.end_index() - shifted_operation.end_index(), last_delete.len() - shifted_operation.len(),
)?); )?;
None None
} else if last_delete } else if last_delete
.range() .range()
.contains(&shifted_operation.start_index()) .contains(&(operation.start_index() + affecting_context.shift))
{ {
let overlap = last_delete.end_index() let overlap = last_delete.end_index()
- (operation.start_index() + affecting_context.shift) - (operation.start_index() + affecting_context.shift)
@ -204,10 +204,10 @@ impl OperationSequence {
affecting_context.last_delete = None; affecting_context.last_delete = None;
affecting_context.shift -= last_delete.len() - overlap; affecting_context.shift -= last_delete.len() - overlap;
let operation = Some(Operation::create_delete( let operation = Operation::create_delete(
last_delete.start_index(), last_delete.start_index(),
operation.len() - overlap, operation.len() - overlap,
)?); )?;
Self::replace_delete_in_produced_context(produced_context, operation.clone()); Self::replace_delete_in_produced_context(produced_context, operation.clone());
operation operation
} else { } else {
@ -246,7 +246,6 @@ impl OperationSequence {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::operations::test;
use super::*; use super::*;
@ -296,6 +295,8 @@ mod tests {
#[test] #[test]
fn test_merges() { fn test_merges() {
test_merge_both_ways("a b c d e", "a e", "a c e", "a e");
test_merge_both_ways( test_merge_both_ways(
"hello world", "hello world",
"hi, world", "hi, world",