Skip to main content

editor_core/
delta.rs

1//! Structured text change deltas.
2//!
3//! `editor-core` historically exposed state changes as a coarse event
4//! ([`crate::StateChangeType::DocumentModified`]) plus a best-effort affected region.
5//! For a full-featured editor, incremental consumers (LSP sync, incremental parsing, indexing,
6//! match highlighting, etc.) typically need **structured edits** without diffing old/new text.
7//!
8//! This module defines a small, UI-agnostic delta format expressed in **character offsets**
9//! (Unicode scalar values).
10
11/// A single text edit expressed in character offsets.
12///
13/// Semantics:
14/// - `start` is a character offset in the document **at the time this edit is applied**.
15/// - The deleted range is defined by the length (in `char`s) of `deleted_text`.
16/// - Edits inside a [`TextDelta`] must be applied **in order** to transform the "before" document
17///   into the "after" document.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct TextDeltaEdit {
20    /// Start character offset of the edit.
21    pub start: usize,
22    /// Exact deleted text (may be empty).
23    pub deleted_text: String,
24    /// Exact inserted text (may be empty).
25    pub inserted_text: String,
26}
27
28impl TextDeltaEdit {
29    /// Length of `deleted_text` in characters.
30    pub fn deleted_len(&self) -> usize {
31        self.deleted_text.chars().count()
32    }
33
34    /// Length of `inserted_text` in characters.
35    pub fn inserted_len(&self) -> usize {
36        self.inserted_text.chars().count()
37    }
38
39    /// Exclusive end character offset in the pre-edit document.
40    pub fn end(&self) -> usize {
41        self.start.saturating_add(self.deleted_len())
42    }
43}
44
45/// A structured description of a document text change.
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct TextDelta {
48    /// Character count before applying `edits`.
49    pub before_char_count: usize,
50    /// Character count after applying `edits`.
51    pub after_char_count: usize,
52    /// Ordered list of edits that transforms the "before" document into the "after" document.
53    pub edits: Vec<TextDeltaEdit>,
54    /// If known, the undo group id associated with this change.
55    pub undo_group_id: Option<usize>,
56}
57
58impl TextDelta {
59    /// Returns `true` if this delta contains no edits.
60    pub fn is_empty(&self) -> bool {
61        self.edits.is_empty()
62    }
63}