Improve docs and compare with alternatives

This commit is contained in:
Andras Schmelczer 2026-03-10 20:29:35 +00:00
commit 3d382ad741
14 changed files with 106 additions and 69 deletions

View file

@ -18,18 +18,16 @@ use crate::{
utils::string_builder::StringBuilder,
};
/// A text document and a sequence of operations that can be applied to the text
/// document. `EditedText` supports merging two sequences of operations using
/// the principles of Operational Transformation.
/// A text document with a sequence of operations derived from diffing it
/// against an updated version. Supports merging two `EditedText` instances
/// (from the same original) via 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.
/// Created via `from_strings`, `from_strings_with_tokenizer`, or `from_diff`,
/// then merged with another `EditedText` and applied to get the reconciled
/// text.
///
/// In addition to text and operations, it also keeps track of cursor positions
/// in the original text. The cursor positions are updated when the operations
/// are applied, so that the cursor positions can be used to restore the
/// cursor positions in the updated text.
/// Also tracks cursor positions from the updated text, repositioning them
/// when operations are applied.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct EditedText<'a, T>
@ -43,12 +41,8 @@ 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 and updated strings.
/// Uses the default word tokenizer (splits on word boundaries).
#[must_use]
pub fn from_strings(original: &'a str, updated: &TextWithCursors) -> Self {
Self::from_strings_with_tokenizer(original, updated, &*BuiltinTokenizer::Word)
@ -59,11 +53,8 @@ impl<'a, T> EditedText<'a, T>
where
T: PartialEq + Clone + Debug,
{
/// 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 and updated strings
/// using the provided tokenizer.
pub fn from_strings_with_tokenizer(
original: &'a str,
updated: &TextWithCursors,
@ -110,7 +101,7 @@ where
///
/// # Panics
///
/// Panics if there's an integer overflow (in i64) when calculating new
/// Panics if there's an integer overflow (in isize) when calculating new
/// cursor positions.
#[must_use]
#[allow(clippy::too_many_lines)]
@ -280,7 +271,7 @@ where
/// Apply the operations to the text and return the resulting text in chunks
/// together with the provenance describing where each chunk came from.
///
/// The result includes deleted spans as well.
/// Returns all spans including deletions (not present in the merged text).
///
/// ```
/// use reconcile_text::{History, SpanWithHistory, BuiltinTokenizer, reconcile};
@ -422,7 +413,7 @@ where
result
}
/// Deserialize an `EditedText` from a change list and the original text.
/// Reconstruct an `EditedText` from a diff and the original text.
///
/// # Errors
///

View file

@ -46,9 +46,8 @@ impl<T> Operation<T>
where
T: PartialEq + Clone + Debug,
{
/// Creates an equal operation with the given index.
/// This operation is used to indicate that the text at the given index
/// is unchanged.
/// Creates an equal (retain) operation starting at the given character
/// offset in the original text.
pub fn create_equal(order: usize, length: usize) -> Self {
Operation::Equal {
order,
@ -69,13 +68,14 @@ where
}
}
/// Creates an insert operation with the given index and text.
/// Creates an insert operation at the given character offset with the
/// given tokens.
pub fn create_insert(order: usize, text: Vec<Token<T>>) -> Self {
Operation::Insert { order, text }
}
/// Creates a delete operation with the given index and number of
/// to-be-deleted characters.
/// Creates a delete operation at the given character offset for the
/// specified number of characters.
pub fn create_delete(order: usize, deleted_character_count: usize) -> Self {
Operation::Delete {
order,
@ -179,8 +179,8 @@ where
builder
}
/// Returns the number of affected characters. It is always greater than 0
/// because empty operations cannot be created.
/// Returns the number of affected characters. May be 0 after
/// `merge_operations`.
pub fn len(&self) -> usize {
match self {
Operation::Equal { length, .. } => *length,
@ -192,10 +192,9 @@ where
}
}
/// 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.
/// Adjusts this operation based on `previous_operation` from the other side
/// to avoid duplicating or conflicting changes. Updates
/// `previous_operation` in-place.
#[allow(clippy::too_many_lines)]
pub fn merge_operations(self, previous_operation: &mut Option<Self>) -> Operation<T> {
let operation = self;