Update formatting

This commit is contained in:
Andras Schmelczer 2024-12-08 18:22:17 +00:00
parent dda356ea00
commit 49638e5aa7
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
27 changed files with 239 additions and 232 deletions

View file

@ -1,24 +1,26 @@
use super::Operation;
use crate::diffs::raw_operation::RawOperation;
use crate::operation_transformation::merge_context::MergeContext;
use crate::tokenizer::word_tokenizer::word_tokenizer;
use crate::tokenizer::Tokenizer;
use crate::utils::ordered_operation::OrderedOperation;
use crate::utils::side::Side;
use crate::utils::string_builder::StringBuilder;
use crate::{diffs::myers::diff, utils::merge_iters::MergeSorted};
use std::iter;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::Operation;
use crate::{
diffs::{myers::diff, raw_operation::RawOperation},
operation_transformation::merge_context::MergeContext,
tokenizer::{word_tokenizer::word_tokenizer, Tokenizer},
utils::{
merge_iters::MergeSorted, ordered_operation::OrderedOperation, side::Side,
string_builder::StringBuilder,
},
};
/// A sequence of operations that can be applied to a text document.
/// EditedText supports merging two sequences of operations using the
/// principle of Operational Transformation.
///
/// It's mainly created through the from_strings method, then merged with another
/// EditedText derived from the same original text and then applied to the original text
/// to get the reconciled text of concurrent edits.
/// It's mainly created through the from_strings method, then merged with
/// another EditedText derived from the same original text and then applied to
/// the original text to get the reconciled text of concurrent edits.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct EditedText<'a, T>
@ -30,10 +32,12 @@ where
}
impl<'a> EditedText<'a, String> {
/// Create an EditedText from the given original (old) and updated (new) strings.
/// The returned EditedText represents the changes from the original to the updated text.
/// When the return value is applied to the original text, it will result in the updated text.
/// The default word tokenizer is used to tokenize the text which splits the text on whitespaces.
/// Create an EditedText from the given original (old) and updated (new)
/// strings. The returned EditedText represents the changes from the
/// original to the updated text. When the return value is applied to
/// the original text, it will result in the updated text. The default
/// word tokenizer is used to tokenize the text which splits the text on
/// whitespaces.
pub fn from_strings(original: &'a str, updated: &str) -> Self {
Self::from_strings_with_tokenizer(original, updated, &word_tokenizer)
}
@ -43,10 +47,11 @@ impl<'a, T> EditedText<'a, T>
where
T: PartialEq + Clone,
{
/// Create an EditedText from the given original (old) and updated (new) strings.
/// The returned EditedText represents the changes from the original to the updated text.
/// When the return value is applied to the original text, it will result in the updated text.
/// The tokenizer function is used to tokenize the text.
/// Create an EditedText from the given original (old) and updated (new)
/// strings. The returned EditedText represents the changes from the
/// original to the updated text. When the return value is applied to
/// the original text, it will result in the updated text. The tokenizer
/// function is used to tokenize the text.
pub fn from_strings_with_tokenizer(
original: &'a str,
updated: &str,
@ -64,7 +69,8 @@ where
)
}
// Turn raw operations into ordered operations while keeping track of old & new indexes.
// Turn raw operations into ordered operations while keeping track of old & new
// indexes.
fn cook_operations<I>(raw_operations: I) -> impl Iterator<Item = OrderedOperation<T>>
where
I: IntoIterator<Item = RawOperation<T>>,
@ -162,8 +168,8 @@ where
}
/// Create a new EditedText with the given operations.
/// The operations must be in the order in which they are meant to be applied.
/// The operations must not overlap.
/// The operations must be in the order in which they are meant to be
/// applied. The operations must not overlap.
fn new(text: &'a str, operations: Vec<OrderedOperation<T>>) -> Self {
operations
.iter()
@ -227,7 +233,8 @@ where
///
/// # Errors
///
/// Returns an SyncLibError::OperationError if the operations cannot be applied to the text.
/// Returns an SyncLibError::OperationError if the operations cannot be
/// applied to the text.
pub fn apply(&self) -> String {
let mut builder: StringBuilder<'_> = StringBuilder::new(self.text);

View file

@ -39,12 +39,10 @@ impl<T> MergeContext<T>
where
T: PartialEq + Clone,
{
pub fn last_operation(&self) -> Option<&Operation<T>> {
self.last_operation.as_ref()
}
pub fn last_operation(&self) -> Option<&Operation<T>> { self.last_operation.as_ref() }
/// Replace the last delete operation (if there was one) with a new one while
/// applying it to the shift.
/// Replace the last delete operation (if there was one) with a new one
/// while applying it to the shift.
pub fn consume_and_replace_last_operation(&mut self, operation: Option<Operation<T>>) {
if let Some(Operation::Delete {
deleted_character_count,
@ -62,8 +60,8 @@ where
}
/// Remove the last operation (if there was one) in case it is behind the
/// threshold operation. This changes the shift in case the last operation was
/// a delete.
/// threshold operation. This changes the shift in case the last operation
/// was a delete.
pub fn consume_last_operation_if_it_is_too_behind(
&mut self,
threshold_operation: &Operation<T>,

View file

@ -33,11 +33,13 @@ where
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
use std::{fs, ops::Range, path::Path};
use pretty_assertions::assert_eq;
use test_case::test_matrix;
use super::*;
#[test]
fn test_merges() {
// Both replaced one token but different
@ -64,7 +66,8 @@ mod test {
"original_1 edit_1 edit_2 original_5",
);
// One deleted a large range, the other inserted and deleted a partially overlapping range
// One deleted a large range, the other inserted and deleted a partially
// overlapping range
test_merge_both_ways(
"original_1 original_2 original_3 original_4 original_5",
"original_1 original_5",
@ -102,7 +105,8 @@ mod test {
"hi, my friend!",
);
// test_merge_both_ways("hello world", "world !", "hi hello world", "hi world !");
// test_merge_both_ways("hello world", "world !", "hi hello world", "hi world
// !");
test_merge_both_ways(
"both delete the same word",

View file

@ -1,14 +1,16 @@
use crate::utils::find_common_overlap::find_common_overlap;
use crate::utils::string_builder::StringBuilder;
use crate::Token;
use std::fmt::Debug;
use std::fmt::Display;
use std::ops::Range;
use std::{
fmt::{Debug, Display},
ops::Range,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::merge_context::MergeContext;
use crate::{
utils::{find_common_overlap::find_common_overlap, string_builder::StringBuilder},
Token,
};
/// Represents a change that can be applied to a text document.
/// Operation is tied to a ropey::Rope and is mainly expected to be
@ -38,7 +40,8 @@ where
T: PartialEq + Clone,
{
/// Creates an insert operation with the given index and text.
/// If the text is empty (meaning that the operation would be a no-op), returns None.
/// If the text is empty (meaning that the operation would be a no-op),
/// returns None.
pub fn create_insert(index: usize, text: Vec<Token<T>>) -> Option<Self> {
if text.is_empty() {
return None;
@ -47,8 +50,9 @@ where
Some(Operation::Insert { index, text })
}
/// Creates a delete operation with the given index and number of to-be-deleted characters.
/// If the operation would delete 0 (meaning that the operation would be a no-op), returns None.
/// Creates a delete operation with the given index and number of
/// to-be-deleted characters. If the operation would delete 0 (meaning
/// that the operation would be a no-op), returns None.
pub fn create_delete(index: usize, deleted_character_count: usize) -> Option<Self> {
if deleted_character_count == 0 {
return None;
@ -77,16 +81,18 @@ where
})
}
/// Tries to apply the operation to the given ropey::Rope text, returning the modified text.
/// Tries to apply the operation to the given ropey::Rope text, returning
/// the modified text.
///
/// # Errors
///
/// Returns a SyncLibError::OperationApplicationError if the operation cannot be applied.
/// Returns a SyncLibError::OperationApplicationError if the operation
/// cannot be applied.
///
/// # Panics
///
/// When compiled in debug mode, panics if a delete operation is attempted on a range
/// of text that does not match the text to be deleted.
/// When compiled in debug mode, panics if a delete operation is attempted
/// on a range of text that does not match the text to be deleted.
pub fn apply<'a>(&self, mut builder: StringBuilder<'a>) -> StringBuilder<'a> {
match self {
Operation::Insert { text, .. } => builder.insert(
@ -135,11 +141,10 @@ where
}
/// Returns the range of indices of characters that the operation affects.
pub fn range(&self) -> Range<usize> {
self.start_index()..self.end_index() + 1
}
pub fn range(&self) -> Range<usize> { self.start_index()..self.end_index() + 1 }
/// Returns the number of affected characters. It is always greater than 0 because empty operations cannot be created.
/// Returns the number of affected characters. It is always greater than 0
/// because empty operations cannot be created.
pub fn len(&self) -> usize {
match self {
Operation::Insert { text, .. } => {
@ -152,7 +157,8 @@ where
}
}
/// Creates a new operation with the same type and text but with the given index.
/// Creates a new operation with the same type and text but with the given
/// index.
pub fn with_index(self, index: usize) -> Self {
match self {
Operation::Insert { text, .. } => Operation::Insert { index, text },
@ -172,8 +178,9 @@ where
}
}
/// Creates a new operation with the same type and text but with the index shifted by the given offset.
/// The offset can be negative but the resulting index must be non-negative.
/// Creates a new operation with the same type and text but with the index
/// shifted by the given offset. The offset can be negative but the
/// resulting index must be non-negative.
///
/// # Panics
///
@ -185,8 +192,9 @@ where
self.with_index(index as usize)
}
/// Merges the operation with the given context, producing a new operation and updating the context.
/// This implements a comples FSM that handles the merging of operations in a way that is consistent with the text.
/// Merges the operation with the given context, producing a new operation
/// and updating the context. This implements a comples FSM that handles
/// the merging of operations in a way that is consistent with the text.
/// The contexts are updated in-place.
pub fn merge_operations_with_context(
self,
@ -242,9 +250,10 @@ where
produced_context.shift += operation.len() as i64;
debug_assert!(
last_delete.range().contains(&operation.start_index()),
"There is a last delete ({last_delete}) but the operation ({operation}) is not contained in it"
);
last_delete.range().contains(&operation.start_index()),
"There is a last delete ({last_delete}) but the operation ({operation}) is \
not contained in it"
);
let difference = operation.start_index() as i64 - last_delete.start_index() as i64;
@ -266,9 +275,10 @@ where
Some(last_delete @ Operation::Delete { .. }),
) => {
debug_assert!(
last_delete.range().contains(&operation.start_index()),
"There is a last delete ({last_delete}) but the operation ({operation}) is not contained in it"
);
last_delete.range().contains(&operation.start_index()),
"There is a last delete ({last_delete}) but the operation ({operation}) is \
not contained in it"
);
let difference = operation.start_index() as i64 - last_delete.start_index() as i64;
@ -341,16 +351,15 @@ impl<T> Debug for Operation<T>
where
T: PartialEq + Clone,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) }
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use super::*;
#[test]
#[should_panic]
fn test_shifting_error() {