Remove the concept of a local clock; use lamport clocks for all per-replica versioning (#2924)

Max Brunsfeld created

### Background

Currently, our CRDT uses three different types of timestamps:

| clock type | representation | purpose |
|-----|----------------|----------|
| `Local` | replica id + u32 | uniquely identifies operations |
| `Lamport` | replica id + u32 | provides a consistent total ordering
for all operations |
| `Global` | N local clocks | fully defines the partial ordering between
all concurrent operations |

All text operations include *each* type of timestamp. And every
`Fragment` in a buffer's fragment tree contains both a local and a
lamport timestamp.

### Change

An operation can be uniquely identified by its lamport timestamp, so we
don't really need a concept of a local timestamp. In this PR, I've
removed the concept of a local timestamp. Version vectors
(`clock::Global`) now store vectors of *lamport* timestamps.

Eliminating local timestamps reduces the memory footprint of a buffer by
four bytes per fragment, reduces the size of our `UpdateBuffer` RPC
messages, and reduces the amount of data we need to store in our
database for channel buffers. It also makes our CRDT a bit easier to
understand, IMO, because there is now only one scalar value that we
increment per replica.

It's possible I'm missing something here though. @as-cii, @nathansobo
it'd be good to get your 👀

Change summary

crates/clock/src/clock.rs               |  81 ++--------
crates/collab/src/db/queries/buffers.rs |  62 +++-----
crates/collab/src/db/queries/users.rs   |   2 
crates/language/src/buffer.rs           |  10 
crates/language/src/proto.rs            | 118 +++++++--------
crates/rpc/proto/zed.proto              |  26 +-
crates/rpc/src/rpc.rs                   |   2 
crates/text/Cargo.toml                  |   1 
crates/text/src/anchor.rs               |   6 
crates/text/src/text.rs                 | 196 +++++++++-----------------
crates/text/src/undo_map.rs             |  12 
11 files changed, 194 insertions(+), 322 deletions(-)

Detailed changes

crates/clock/src/clock.rs 🔗

@@ -2,70 +2,17 @@ use smallvec::SmallVec;
 use std::{
     cmp::{self, Ordering},
     fmt, iter,
-    ops::{Add, AddAssign},
 };
 
 pub type ReplicaId = u16;
 pub type Seq = u32;
 
-#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, Ord, PartialOrd)]
-pub struct Local {
-    pub replica_id: ReplicaId,
-    pub value: Seq,
-}
-
 #[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
 pub struct Lamport {
     pub replica_id: ReplicaId,
     pub value: Seq,
 }
 
-impl Local {
-    pub const MIN: Self = Self {
-        replica_id: ReplicaId::MIN,
-        value: Seq::MIN,
-    };
-    pub const MAX: Self = Self {
-        replica_id: ReplicaId::MAX,
-        value: Seq::MAX,
-    };
-
-    pub fn new(replica_id: ReplicaId) -> Self {
-        Self {
-            replica_id,
-            value: 1,
-        }
-    }
-
-    pub fn tick(&mut self) -> Self {
-        let timestamp = *self;
-        self.value += 1;
-        timestamp
-    }
-
-    pub fn observe(&mut self, timestamp: Self) {
-        if timestamp.replica_id == self.replica_id {
-            self.value = cmp::max(self.value, timestamp.value + 1);
-        }
-    }
-}
-
-impl<'a> Add<&'a Self> for Local {
-    type Output = Local;
-
-    fn add(self, other: &'a Self) -> Self::Output {
-        *cmp::max(&self, other)
-    }
-}
-
-impl<'a> AddAssign<&'a Local> for Local {
-    fn add_assign(&mut self, other: &Self) {
-        if *self < *other {
-            *self = *other;
-        }
-    }
-}
-
 /// A vector clock
 #[derive(Clone, Default, Hash, Eq, PartialEq)]
 pub struct Global(SmallVec<[u32; 8]>);
@@ -79,7 +26,7 @@ impl Global {
         self.0.get(replica_id as usize).copied().unwrap_or(0) as Seq
     }
 
-    pub fn observe(&mut self, timestamp: Local) {
+    pub fn observe(&mut self, timestamp: Lamport) {
         if timestamp.value > 0 {
             let new_len = timestamp.replica_id as usize + 1;
             if new_len > self.0.len() {
@@ -126,7 +73,7 @@ impl Global {
         self.0.resize(new_len, 0);
     }
 
-    pub fn observed(&self, timestamp: Local) -> bool {
+    pub fn observed(&self, timestamp: Lamport) -> bool {
         self.get(timestamp.replica_id) >= timestamp.value
     }
 
@@ -178,16 +125,16 @@ impl Global {
         false
     }
 
-    pub fn iter(&self) -> impl Iterator<Item = Local> + '_ {
-        self.0.iter().enumerate().map(|(replica_id, seq)| Local {
+    pub fn iter(&self) -> impl Iterator<Item = Lamport> + '_ {
+        self.0.iter().enumerate().map(|(replica_id, seq)| Lamport {
             replica_id: replica_id as ReplicaId,
             value: *seq,
         })
     }
 }
 
-impl FromIterator<Local> for Global {
-    fn from_iter<T: IntoIterator<Item = Local>>(locals: T) -> Self {
+impl FromIterator<Lamport> for Global {
+    fn from_iter<T: IntoIterator<Item = Lamport>>(locals: T) -> Self {
         let mut result = Self::new();
         for local in locals {
             result.observe(local);
@@ -212,6 +159,16 @@ impl PartialOrd for Lamport {
 }
 
 impl Lamport {
+    pub const MIN: Self = Self {
+        replica_id: ReplicaId::MIN,
+        value: Seq::MIN,
+    };
+
+    pub const MAX: Self = Self {
+        replica_id: ReplicaId::MAX,
+        value: Seq::MAX,
+    };
+
     pub fn new(replica_id: ReplicaId) -> Self {
         Self {
             value: 1,
@@ -230,12 +187,6 @@ impl Lamport {
     }
 }
 
-impl fmt::Debug for Local {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "Local {{{}: {}}}", self.replica_id, self.value)
-    }
-}
-
 impl fmt::Debug for Lamport {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "Lamport {{{}: {}}}", self.replica_id, self.value)

crates/collab/src/db/queries/buffers.rs 🔗

@@ -1,6 +1,6 @@
 use super::*;
 use prost::Message;
-use text::{EditOperation, InsertionTimestamp, UndoOperation};
+use text::{EditOperation, UndoOperation};
 
 impl Database {
     pub async fn join_channel_buffer(
@@ -182,7 +182,6 @@ impl Database {
         .await
     }
 
-    #[cfg(debug_assertions)]
     pub async fn get_channel_buffer_collaborators(
         &self,
         channel_id: ChannelId,
@@ -370,7 +369,6 @@ fn operation_to_storage(
             operation.replica_id,
             operation.lamport_timestamp,
             storage::Operation {
-                local_timestamp: operation.local_timestamp,
                 version: version_to_storage(&operation.version),
                 is_undo: false,
                 edit_ranges: operation
@@ -389,7 +387,6 @@ fn operation_to_storage(
             operation.replica_id,
             operation.lamport_timestamp,
             storage::Operation {
-                local_timestamp: operation.local_timestamp,
                 version: version_to_storage(&operation.version),
                 is_undo: true,
                 edit_ranges: Vec::new(),
@@ -399,7 +396,7 @@ fn operation_to_storage(
                     .iter()
                     .map(|entry| storage::UndoCount {
                         replica_id: entry.replica_id,
-                        local_timestamp: entry.local_timestamp,
+                        lamport_timestamp: entry.lamport_timestamp,
                         count: entry.count,
                     })
                     .collect(),
@@ -427,7 +424,6 @@ fn operation_from_storage(
     Ok(if operation.is_undo {
         proto::operation::Variant::Undo(proto::operation::Undo {
             replica_id: row.replica_id as u32,
-            local_timestamp: operation.local_timestamp as u32,
             lamport_timestamp: row.lamport_timestamp as u32,
             version,
             counts: operation
@@ -435,7 +431,7 @@ fn operation_from_storage(
                 .iter()
                 .map(|entry| proto::UndoCount {
                     replica_id: entry.replica_id,
-                    local_timestamp: entry.local_timestamp,
+                    lamport_timestamp: entry.lamport_timestamp,
                     count: entry.count,
                 })
                 .collect(),
@@ -443,7 +439,6 @@ fn operation_from_storage(
     } else {
         proto::operation::Variant::Edit(proto::operation::Edit {
             replica_id: row.replica_id as u32,
-            local_timestamp: operation.local_timestamp as u32,
             lamport_timestamp: row.lamport_timestamp as u32,
             version,
             ranges: operation
@@ -483,10 +478,9 @@ fn version_from_storage(version: &Vec<storage::VectorClockEntry>) -> Vec<proto::
 pub fn operation_from_wire(operation: proto::Operation) -> Option<text::Operation> {
     match operation.variant? {
         proto::operation::Variant::Edit(edit) => Some(text::Operation::Edit(EditOperation {
-            timestamp: InsertionTimestamp {
+            timestamp: clock::Lamport {
                 replica_id: edit.replica_id as text::ReplicaId,
-                local: edit.local_timestamp,
-                lamport: edit.lamport_timestamp,
+                value: edit.lamport_timestamp,
             },
             version: version_from_wire(&edit.version),
             ranges: edit
@@ -498,32 +492,26 @@ pub fn operation_from_wire(operation: proto::Operation) -> Option<text::Operatio
                 .collect(),
             new_text: edit.new_text.into_iter().map(Arc::from).collect(),
         })),
-        proto::operation::Variant::Undo(undo) => Some(text::Operation::Undo {
-            lamport_timestamp: clock::Lamport {
+        proto::operation::Variant::Undo(undo) => Some(text::Operation::Undo(UndoOperation {
+            timestamp: clock::Lamport {
                 replica_id: undo.replica_id as text::ReplicaId,
                 value: undo.lamport_timestamp,
             },
-            undo: UndoOperation {
-                id: clock::Local {
-                    replica_id: undo.replica_id as text::ReplicaId,
-                    value: undo.local_timestamp,
-                },
-                version: version_from_wire(&undo.version),
-                counts: undo
-                    .counts
-                    .into_iter()
-                    .map(|c| {
-                        (
-                            clock::Local {
-                                replica_id: c.replica_id as text::ReplicaId,
-                                value: c.local_timestamp,
-                            },
-                            c.count,
-                        )
-                    })
-                    .collect(),
-            },
-        }),
+            version: version_from_wire(&undo.version),
+            counts: undo
+                .counts
+                .into_iter()
+                .map(|c| {
+                    (
+                        clock::Lamport {
+                            replica_id: c.replica_id as text::ReplicaId,
+                            value: c.lamport_timestamp,
+                        },
+                        c.count,
+                    )
+                })
+                .collect(),
+        })),
         _ => None,
     }
 }
@@ -531,7 +519,7 @@ pub fn operation_from_wire(operation: proto::Operation) -> Option<text::Operatio
 fn version_from_wire(message: &[proto::VectorClockEntry]) -> clock::Global {
     let mut version = clock::Global::new();
     for entry in message {
-        version.observe(clock::Local {
+        version.observe(clock::Lamport {
             replica_id: entry.replica_id as text::ReplicaId,
             value: entry.timestamp,
         });
@@ -546,8 +534,6 @@ mod storage {
 
     #[derive(Message)]
     pub struct Operation {
-        #[prost(uint32, tag = "1")]
-        pub local_timestamp: u32,
         #[prost(message, repeated, tag = "2")]
         pub version: Vec<VectorClockEntry>,
         #[prost(bool, tag = "3")]
@@ -581,7 +567,7 @@ mod storage {
         #[prost(uint32, tag = "1")]
         pub replica_id: u32,
         #[prost(uint32, tag = "2")]
-        pub local_timestamp: u32,
+        pub lamport_timestamp: u32,
         #[prost(uint32, tag = "3")]
         pub count: u32,
     }

crates/collab/src/db/queries/users.rs 🔗

@@ -241,7 +241,6 @@ impl Database {
         result
     }
 
-    #[cfg(debug_assertions)]
     pub async fn create_user_flag(&self, flag: &str) -> Result<FlagId> {
         self.transaction(|tx| async move {
             let flag = feature_flag::Entity::insert(feature_flag::ActiveModel {
@@ -257,7 +256,6 @@ impl Database {
         .await
     }
 
-    #[cfg(debug_assertions)]
     pub async fn add_user_flag(&self, user: UserId, flag: FlagId) -> Result<()> {
         self.transaction(|tx| async move {
             user_feature::Entity::insert(user_feature::ActiveModel {

crates/language/src/buffer.rs 🔗

@@ -439,7 +439,7 @@ impl Buffer {
             operations.extend(
                 text_operations
                     .iter()
-                    .filter(|(_, op)| !since.observed(op.local_timestamp()))
+                    .filter(|(_, op)| !since.observed(op.timestamp()))
                     .map(|(_, op)| proto::serialize_operation(&Operation::Buffer(op.clone()))),
             );
             operations.sort_unstable_by_key(proto::lamport_timestamp_for_operation);
@@ -1304,7 +1304,7 @@ impl Buffer {
 
     pub fn wait_for_edits(
         &mut self,
-        edit_ids: impl IntoIterator<Item = clock::Local>,
+        edit_ids: impl IntoIterator<Item = clock::Lamport>,
     ) -> impl Future<Output = Result<()>> {
         self.text.wait_for_edits(edit_ids)
     }
@@ -1362,7 +1362,7 @@ impl Buffer {
         }
     }
 
-    pub fn set_text<T>(&mut self, text: T, cx: &mut ModelContext<Self>) -> Option<clock::Local>
+    pub fn set_text<T>(&mut self, text: T, cx: &mut ModelContext<Self>) -> Option<clock::Lamport>
     where
         T: Into<Arc<str>>,
     {
@@ -1375,7 +1375,7 @@ impl Buffer {
         edits_iter: I,
         autoindent_mode: Option<AutoindentMode>,
         cx: &mut ModelContext<Self>,
-    ) -> Option<clock::Local>
+    ) -> Option<clock::Lamport>
     where
         I: IntoIterator<Item = (Range<S>, T)>,
         S: ToOffset,
@@ -1412,7 +1412,7 @@ impl Buffer {
             .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));
 
         let edit_operation = self.text.edit(edits.iter().cloned());
-        let edit_id = edit_operation.local_timestamp();
+        let edit_id = edit_operation.timestamp();
 
         if let Some((before_edit, mode)) = autoindent_request {
             let mut delta = 0isize;

crates/language/src/proto.rs 🔗

@@ -41,24 +41,22 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
                 proto::operation::Variant::Edit(serialize_edit_operation(edit))
             }
 
-            crate::Operation::Buffer(text::Operation::Undo {
-                undo,
-                lamport_timestamp,
-            }) => proto::operation::Variant::Undo(proto::operation::Undo {
-                replica_id: undo.id.replica_id as u32,
-                local_timestamp: undo.id.value,
-                lamport_timestamp: lamport_timestamp.value,
-                version: serialize_version(&undo.version),
-                counts: undo
-                    .counts
-                    .iter()
-                    .map(|(edit_id, count)| proto::UndoCount {
-                        replica_id: edit_id.replica_id as u32,
-                        local_timestamp: edit_id.value,
-                        count: *count,
-                    })
-                    .collect(),
-            }),
+            crate::Operation::Buffer(text::Operation::Undo(undo)) => {
+                proto::operation::Variant::Undo(proto::operation::Undo {
+                    replica_id: undo.timestamp.replica_id as u32,
+                    lamport_timestamp: undo.timestamp.value,
+                    version: serialize_version(&undo.version),
+                    counts: undo
+                        .counts
+                        .iter()
+                        .map(|(edit_id, count)| proto::UndoCount {
+                            replica_id: edit_id.replica_id as u32,
+                            lamport_timestamp: edit_id.value,
+                            count: *count,
+                        })
+                        .collect(),
+                })
+            }
 
             crate::Operation::UpdateSelections {
                 selections,
@@ -101,8 +99,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
 pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::Edit {
     proto::operation::Edit {
         replica_id: operation.timestamp.replica_id as u32,
-        local_timestamp: operation.timestamp.local,
-        lamport_timestamp: operation.timestamp.lamport,
+        lamport_timestamp: operation.timestamp.value,
         version: serialize_version(&operation.version),
         ranges: operation.ranges.iter().map(serialize_range).collect(),
         new_text: operation
@@ -114,7 +111,7 @@ pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::
 }
 
 pub fn serialize_undo_map_entry(
-    (edit_id, counts): (&clock::Local, &[(clock::Local, u32)]),
+    (edit_id, counts): (&clock::Lamport, &[(clock::Lamport, u32)]),
 ) -> proto::UndoMapEntry {
     proto::UndoMapEntry {
         replica_id: edit_id.replica_id as u32,
@@ -123,7 +120,7 @@ pub fn serialize_undo_map_entry(
             .iter()
             .map(|(undo_id, count)| proto::UndoCount {
                 replica_id: undo_id.replica_id as u32,
-                local_timestamp: undo_id.value,
+                lamport_timestamp: undo_id.value,
                 count: *count,
             })
             .collect(),
@@ -197,7 +194,7 @@ pub fn serialize_diagnostics<'a>(
 pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
     proto::Anchor {
         replica_id: anchor.timestamp.replica_id as u32,
-        local_timestamp: anchor.timestamp.value,
+        timestamp: anchor.timestamp.value,
         offset: anchor.offset as u64,
         bias: match anchor.bias {
             Bias::Left => proto::Bias::Left as i32,
@@ -218,32 +215,26 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
                 crate::Operation::Buffer(text::Operation::Edit(deserialize_edit_operation(edit)))
             }
             proto::operation::Variant::Undo(undo) => {
-                crate::Operation::Buffer(text::Operation::Undo {
-                    lamport_timestamp: clock::Lamport {
+                crate::Operation::Buffer(text::Operation::Undo(UndoOperation {
+                    timestamp: clock::Lamport {
                         replica_id: undo.replica_id as ReplicaId,
                         value: undo.lamport_timestamp,
                     },
-                    undo: UndoOperation {
-                        id: clock::Local {
-                            replica_id: undo.replica_id as ReplicaId,
-                            value: undo.local_timestamp,
-                        },
-                        version: deserialize_version(&undo.version),
-                        counts: undo
-                            .counts
-                            .into_iter()
-                            .map(|c| {
-                                (
-                                    clock::Local {
-                                        replica_id: c.replica_id as ReplicaId,
-                                        value: c.local_timestamp,
-                                    },
-                                    c.count,
-                                )
-                            })
-                            .collect(),
-                    },
-                })
+                    version: deserialize_version(&undo.version),
+                    counts: undo
+                        .counts
+                        .into_iter()
+                        .map(|c| {
+                            (
+                                clock::Lamport {
+                                    replica_id: c.replica_id as ReplicaId,
+                                    value: c.lamport_timestamp,
+                                },
+                                c.count,
+                            )
+                        })
+                        .collect(),
+                }))
             }
             proto::operation::Variant::UpdateSelections(message) => {
                 let selections = message
@@ -298,10 +289,9 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
 
 pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation {
     EditOperation {
-        timestamp: InsertionTimestamp {
+        timestamp: clock::Lamport {
             replica_id: edit.replica_id as ReplicaId,
-            local: edit.local_timestamp,
-            lamport: edit.lamport_timestamp,
+            value: edit.lamport_timestamp,
         },
         version: deserialize_version(&edit.version),
         ranges: edit.ranges.into_iter().map(deserialize_range).collect(),
@@ -311,9 +301,9 @@ pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation
 
 pub fn deserialize_undo_map_entry(
     entry: proto::UndoMapEntry,
-) -> (clock::Local, Vec<(clock::Local, u32)>) {
+) -> (clock::Lamport, Vec<(clock::Lamport, u32)>) {
     (
-        clock::Local {
+        clock::Lamport {
             replica_id: entry.replica_id as u16,
             value: entry.local_timestamp,
         },
@@ -322,9 +312,9 @@ pub fn deserialize_undo_map_entry(
             .into_iter()
             .map(|undo_count| {
                 (
-                    clock::Local {
+                    clock::Lamport {
                         replica_id: undo_count.replica_id as u16,
-                        value: undo_count.local_timestamp,
+                        value: undo_count.lamport_timestamp,
                     },
                     undo_count.count,
                 )
@@ -384,9 +374,9 @@ pub fn deserialize_diagnostics(
 
 pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
     Some(Anchor {
-        timestamp: clock::Local {
+        timestamp: clock::Lamport {
             replica_id: anchor.replica_id as ReplicaId,
-            value: anchor.local_timestamp,
+            value: anchor.timestamp,
         },
         offset: anchor.offset as usize,
         bias: match proto::Bias::from_i32(anchor.bias)? {
@@ -500,12 +490,12 @@ pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction>
 
 pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
     proto::Transaction {
-        id: Some(serialize_local_timestamp(transaction.id)),
+        id: Some(serialize_timestamp(transaction.id)),
         edit_ids: transaction
             .edit_ids
             .iter()
             .copied()
-            .map(serialize_local_timestamp)
+            .map(serialize_timestamp)
             .collect(),
         start: serialize_version(&transaction.start),
     }
@@ -513,7 +503,7 @@ pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
 
 pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transaction> {
     Ok(Transaction {
-        id: deserialize_local_timestamp(
+        id: deserialize_timestamp(
             transaction
                 .id
                 .ok_or_else(|| anyhow!("missing transaction id"))?,
@@ -521,21 +511,21 @@ pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transa
         edit_ids: transaction
             .edit_ids
             .into_iter()
-            .map(deserialize_local_timestamp)
+            .map(deserialize_timestamp)
             .collect(),
         start: deserialize_version(&transaction.start),
     })
 }
 
-pub fn serialize_local_timestamp(timestamp: clock::Local) -> proto::LocalTimestamp {
-    proto::LocalTimestamp {
+pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp {
+    proto::LamportTimestamp {
         replica_id: timestamp.replica_id as u32,
         value: timestamp.value,
     }
 }
 
-pub fn deserialize_local_timestamp(timestamp: proto::LocalTimestamp) -> clock::Local {
-    clock::Local {
+pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lamport {
+    clock::Lamport {
         replica_id: timestamp.replica_id as ReplicaId,
         value: timestamp.value,
     }
@@ -555,7 +545,7 @@ pub fn deserialize_range(range: proto::Range) -> Range<FullOffset> {
 pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global {
     let mut version = clock::Global::new();
     for entry in message {
-        version.observe(clock::Local {
+        version.observe(clock::Lamport {
             replica_id: entry.replica_id as ReplicaId,
             value: entry.timestamp,
         });

crates/rpc/proto/zed.proto 🔗

@@ -861,12 +861,12 @@ message ProjectTransaction {
 }
 
 message Transaction {
-    LocalTimestamp id = 1;
-    repeated LocalTimestamp edit_ids = 2;
+    LamportTimestamp id = 1;
+    repeated LamportTimestamp edit_ids = 2;
     repeated VectorClockEntry start = 3;
 }
 
-message LocalTimestamp {
+message LamportTimestamp {
     uint32 replica_id = 1;
     uint32 value = 2;
 }
@@ -1280,7 +1280,7 @@ message Excerpt {
 
 message Anchor {
     uint32 replica_id = 1;
-    uint32 local_timestamp = 2;
+    uint32 timestamp = 2;
     uint64 offset = 3;
     Bias bias = 4;
     optional uint64 buffer_id = 5;
@@ -1324,19 +1324,17 @@ message Operation {
 
     message Edit {
         uint32 replica_id = 1;
-        uint32 local_timestamp = 2;
-        uint32 lamport_timestamp = 3;
-        repeated VectorClockEntry version = 4;
-        repeated Range ranges = 5;
-        repeated string new_text = 6;
+        uint32 lamport_timestamp = 2;
+        repeated VectorClockEntry version = 3;
+        repeated Range ranges = 4;
+        repeated string new_text = 5;
     }
 
     message Undo {
         uint32 replica_id = 1;
-        uint32 local_timestamp = 2;
-        uint32 lamport_timestamp = 3;
-        repeated VectorClockEntry version = 4;
-        repeated UndoCount counts = 5;
+        uint32 lamport_timestamp = 2;
+        repeated VectorClockEntry version = 3;
+        repeated UndoCount counts = 4;
     }
 
     message UpdateSelections {
@@ -1362,7 +1360,7 @@ message UndoMapEntry {
 
 message UndoCount {
     uint32 replica_id = 1;
-    uint32 local_timestamp = 2;
+    uint32 lamport_timestamp = 2;
     uint32 count = 3;
 }
 

crates/rpc/src/rpc.rs 🔗

@@ -6,4 +6,4 @@ pub use conn::Connection;
 pub use peer::*;
 mod macros;
 
-pub const PROTOCOL_VERSION: u32 = 61;
+pub const PROTOCOL_VERSION: u32 = 62;

crates/text/Cargo.toml 🔗

@@ -31,6 +31,7 @@ regex.workspace = true
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
 gpui = { path = "../gpui", features = ["test-support"] }
+util = { path = "../util", features = ["test-support"] }
 ctor.workspace = true
 env_logger.workspace = true
 rand.workspace = true

crates/text/src/anchor.rs 🔗

@@ -8,7 +8,7 @@ use sum_tree::Bias;
 
 #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)]
 pub struct Anchor {
-    pub timestamp: clock::Local,
+    pub timestamp: clock::Lamport,
     pub offset: usize,
     pub bias: Bias,
     pub buffer_id: Option<u64>,
@@ -16,14 +16,14 @@ pub struct Anchor {
 
 impl Anchor {
     pub const MIN: Self = Self {
-        timestamp: clock::Local::MIN,
+        timestamp: clock::Lamport::MIN,
         offset: usize::MIN,
         bias: Bias::Left,
         buffer_id: None,
     };
 
     pub const MAX: Self = Self {
-        timestamp: clock::Local::MAX,
+        timestamp: clock::Lamport::MAX,
         offset: usize::MAX,
         bias: Bias::Right,
         buffer_id: None,

crates/text/src/text.rs 🔗

@@ -46,18 +46,16 @@ lazy_static! {
     static ref LINE_SEPARATORS_REGEX: Regex = Regex::new("\r\n|\r|\u{2028}|\u{2029}").unwrap();
 }
 
-pub type TransactionId = clock::Local;
+pub type TransactionId = clock::Lamport;
 
 pub struct Buffer {
     snapshot: BufferSnapshot,
     history: History,
     deferred_ops: OperationQueue<Operation>,
     deferred_replicas: HashSet<ReplicaId>,
-    replica_id: ReplicaId,
-    local_clock: clock::Local,
     pub lamport_clock: clock::Lamport,
     subscriptions: Topic,
-    edit_id_resolvers: HashMap<clock::Local, Vec<oneshot::Sender<()>>>,
+    edit_id_resolvers: HashMap<clock::Lamport, Vec<oneshot::Sender<()>>>,
     wait_for_version_txs: Vec<(clock::Global, oneshot::Sender<()>)>,
 }
 
@@ -85,7 +83,7 @@ pub struct HistoryEntry {
 #[derive(Clone, Debug)]
 pub struct Transaction {
     pub id: TransactionId,
-    pub edit_ids: Vec<clock::Local>,
+    pub edit_ids: Vec<clock::Lamport>,
     pub start: clock::Global,
 }
 
@@ -97,8 +95,8 @@ impl HistoryEntry {
 
 struct History {
     base_text: Rope,
-    operations: TreeMap<clock::Local, Operation>,
-    insertion_slices: HashMap<clock::Local, Vec<InsertionSlice>>,
+    operations: TreeMap<clock::Lamport, Operation>,
+    insertion_slices: HashMap<clock::Lamport, Vec<InsertionSlice>>,
     undo_stack: Vec<HistoryEntry>,
     redo_stack: Vec<HistoryEntry>,
     transaction_depth: usize,
@@ -107,7 +105,7 @@ struct History {
 
 #[derive(Clone, Debug)]
 struct InsertionSlice {
-    insertion_id: clock::Local,
+    insertion_id: clock::Lamport,
     range: Range<usize>,
 }
 
@@ -129,18 +127,18 @@ impl History {
     }
 
     fn push(&mut self, op: Operation) {
-        self.operations.insert(op.local_timestamp(), op);
+        self.operations.insert(op.timestamp(), op);
     }
 
     fn start_transaction(
         &mut self,
         start: clock::Global,
         now: Instant,
-        local_clock: &mut clock::Local,
+        clock: &mut clock::Lamport,
     ) -> Option<TransactionId> {
         self.transaction_depth += 1;
         if self.transaction_depth == 1 {
-            let id = local_clock.tick();
+            let id = clock.tick();
             self.undo_stack.push(HistoryEntry {
                 transaction: Transaction {
                     id,
@@ -251,7 +249,7 @@ impl History {
         self.redo_stack.clear();
     }
 
-    fn push_undo(&mut self, op_id: clock::Local) {
+    fn push_undo(&mut self, op_id: clock::Lamport) {
         assert_ne!(self.transaction_depth, 0);
         if let Some(Operation::Edit(_)) = self.operations.get(&op_id) {
             let last_transaction = self.undo_stack.last_mut().unwrap();
@@ -412,37 +410,14 @@ impl<D1, D2> Edit<(D1, D2)> {
     }
 }
 
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
-pub struct InsertionTimestamp {
-    pub replica_id: ReplicaId,
-    pub local: clock::Seq,
-    pub lamport: clock::Seq,
-}
-
-impl InsertionTimestamp {
-    pub fn local(&self) -> clock::Local {
-        clock::Local {
-            replica_id: self.replica_id,
-            value: self.local,
-        }
-    }
-
-    pub fn lamport(&self) -> clock::Lamport {
-        clock::Lamport {
-            replica_id: self.replica_id,
-            value: self.lamport,
-        }
-    }
-}
-
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub struct Fragment {
     pub id: Locator,
-    pub insertion_timestamp: InsertionTimestamp,
+    pub timestamp: clock::Lamport,
     pub insertion_offset: usize,
     pub len: usize,
     pub visible: bool,
-    pub deletions: HashSet<clock::Local>,
+    pub deletions: HashSet<clock::Lamport>,
     pub max_undos: clock::Global,
 }
 
@@ -470,29 +445,26 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
 
 #[derive(Eq, PartialEq, Clone, Debug)]
 struct InsertionFragment {
-    timestamp: clock::Local,
+    timestamp: clock::Lamport,
     split_offset: usize,
     fragment_id: Locator,
 }
 
 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
 struct InsertionFragmentKey {
-    timestamp: clock::Local,
+    timestamp: clock::Lamport,
     split_offset: usize,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Operation {
     Edit(EditOperation),
-    Undo {
-        undo: UndoOperation,
-        lamport_timestamp: clock::Lamport,
-    },
+    Undo(UndoOperation),
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct EditOperation {
-    pub timestamp: InsertionTimestamp,
+    pub timestamp: clock::Lamport,
     pub version: clock::Global,
     pub ranges: Vec<Range<FullOffset>>,
     pub new_text: Vec<Arc<str>>,
@@ -500,9 +472,9 @@ pub struct EditOperation {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UndoOperation {
-    pub id: clock::Local,
-    pub counts: HashMap<clock::Local, u32>,
+    pub timestamp: clock::Lamport,
     pub version: clock::Global,
+    pub counts: HashMap<clock::Lamport, u32>,
 }
 
 impl Buffer {
@@ -514,24 +486,21 @@ impl Buffer {
         let mut fragments = SumTree::new();
         let mut insertions = SumTree::new();
 
-        let mut local_clock = clock::Local::new(replica_id);
         let mut lamport_clock = clock::Lamport::new(replica_id);
         let mut version = clock::Global::new();
 
         let visible_text = history.base_text.clone();
         if !visible_text.is_empty() {
-            let insertion_timestamp = InsertionTimestamp {
+            let insertion_timestamp = clock::Lamport {
                 replica_id: 0,
-                local: 1,
-                lamport: 1,
+                value: 1,
             };
-            local_clock.observe(insertion_timestamp.local());
-            lamport_clock.observe(insertion_timestamp.lamport());
-            version.observe(insertion_timestamp.local());
+            lamport_clock.observe(insertion_timestamp);
+            version.observe(insertion_timestamp);
             let fragment_id = Locator::between(&Locator::min(), &Locator::max());
             let fragment = Fragment {
                 id: fragment_id,
-                insertion_timestamp,
+                timestamp: insertion_timestamp,
                 insertion_offset: 0,
                 len: visible_text.len(),
                 visible: true,
@@ -557,8 +526,6 @@ impl Buffer {
             history,
             deferred_ops: OperationQueue::new(),
             deferred_replicas: HashSet::default(),
-            replica_id,
-            local_clock,
             lamport_clock,
             subscriptions: Default::default(),
             edit_id_resolvers: Default::default(),
@@ -575,7 +542,7 @@ impl Buffer {
     }
 
     pub fn replica_id(&self) -> ReplicaId {
-        self.local_clock.replica_id
+        self.lamport_clock.replica_id
     }
 
     pub fn remote_id(&self) -> u64 {
@@ -602,16 +569,12 @@ impl Buffer {
             .map(|(range, new_text)| (range, new_text.into()));
 
         self.start_transaction();
-        let timestamp = InsertionTimestamp {
-            replica_id: self.replica_id,
-            local: self.local_clock.tick().value,
-            lamport: self.lamport_clock.tick().value,
-        };
+        let timestamp = self.lamport_clock.tick();
         let operation = Operation::Edit(self.apply_local_edit(edits, timestamp));
 
         self.history.push(operation.clone());
-        self.history.push_undo(operation.local_timestamp());
-        self.snapshot.version.observe(operation.local_timestamp());
+        self.history.push_undo(operation.timestamp());
+        self.snapshot.version.observe(operation.timestamp());
         self.end_transaction();
         operation
     }
@@ -619,7 +582,7 @@ impl Buffer {
     fn apply_local_edit<S: ToOffset, T: Into<Arc<str>>>(
         &mut self,
         edits: impl ExactSizeIterator<Item = (Range<S>, T)>,
-        timestamp: InsertionTimestamp,
+        timestamp: clock::Lamport,
     ) -> EditOperation {
         let mut edits_patch = Patch::default();
         let mut edit_op = EditOperation {
@@ -696,7 +659,7 @@ impl Buffer {
                             .item()
                             .map_or(&Locator::max(), |old_fragment| &old_fragment.id),
                     ),
-                    insertion_timestamp: timestamp,
+                    timestamp,
                     insertion_offset,
                     len: new_text.len(),
                     deletions: Default::default(),
@@ -726,7 +689,7 @@ impl Buffer {
                     intersection.insertion_offset += fragment_start - old_fragments.start().visible;
                     intersection.id =
                         Locator::between(&new_fragments.summary().max_id, &intersection.id);
-                    intersection.deletions.insert(timestamp.local());
+                    intersection.deletions.insert(timestamp);
                     intersection.visible = false;
                 }
                 if intersection.len > 0 {
@@ -781,7 +744,7 @@ impl Buffer {
         self.subscriptions.publish_mut(&edits_patch);
         self.history
             .insertion_slices
-            .insert(timestamp.local(), insertion_slices);
+            .insert(timestamp, insertion_slices);
         edit_op
     }
 
@@ -808,28 +771,23 @@ impl Buffer {
     fn apply_op(&mut self, op: Operation) -> Result<()> {
         match op {
             Operation::Edit(edit) => {
-                if !self.version.observed(edit.timestamp.local()) {
+                if !self.version.observed(edit.timestamp) {
                     self.apply_remote_edit(
                         &edit.version,
                         &edit.ranges,
                         &edit.new_text,
                         edit.timestamp,
                     );
-                    self.snapshot.version.observe(edit.timestamp.local());
-                    self.local_clock.observe(edit.timestamp.local());
-                    self.lamport_clock.observe(edit.timestamp.lamport());
-                    self.resolve_edit(edit.timestamp.local());
+                    self.snapshot.version.observe(edit.timestamp);
+                    self.lamport_clock.observe(edit.timestamp);
+                    self.resolve_edit(edit.timestamp);
                 }
             }
-            Operation::Undo {
-                undo,
-                lamport_timestamp,
-            } => {
-                if !self.version.observed(undo.id) {
+            Operation::Undo(undo) => {
+                if !self.version.observed(undo.timestamp) {
                     self.apply_undo(&undo)?;
-                    self.snapshot.version.observe(undo.id);
-                    self.local_clock.observe(undo.id);
-                    self.lamport_clock.observe(lamport_timestamp);
+                    self.snapshot.version.observe(undo.timestamp);
+                    self.lamport_clock.observe(undo.timestamp);
                 }
             }
         }
@@ -849,7 +807,7 @@ impl Buffer {
         version: &clock::Global,
         ranges: &[Range<FullOffset>],
         new_text: &[Arc<str>],
-        timestamp: InsertionTimestamp,
+        timestamp: clock::Lamport,
     ) {
         if ranges.is_empty() {
             return;
@@ -916,9 +874,7 @@ impl Buffer {
             // Skip over insertions that are concurrent to this edit, but have a lower lamport
             // timestamp.
             while let Some(fragment) = old_fragments.item() {
-                if fragment_start == range.start
-                    && fragment.insertion_timestamp.lamport() > timestamp.lamport()
-                {
+                if fragment_start == range.start && fragment.timestamp > timestamp {
                     new_ropes.push_fragment(fragment, fragment.visible);
                     new_fragments.push(fragment.clone(), &None);
                     old_fragments.next(&cx);
@@ -955,7 +911,7 @@ impl Buffer {
                             .item()
                             .map_or(&Locator::max(), |old_fragment| &old_fragment.id),
                     ),
-                    insertion_timestamp: timestamp,
+                    timestamp,
                     insertion_offset,
                     len: new_text.len(),
                     deletions: Default::default(),
@@ -986,7 +942,7 @@ impl Buffer {
                         fragment_start - old_fragments.start().0.full_offset();
                     intersection.id =
                         Locator::between(&new_fragments.summary().max_id, &intersection.id);
-                    intersection.deletions.insert(timestamp.local());
+                    intersection.deletions.insert(timestamp);
                     intersection.visible = false;
                     insertion_slices.push(intersection.insertion_slice());
                 }
@@ -1038,13 +994,13 @@ impl Buffer {
         self.snapshot.insertions.edit(new_insertions, &());
         self.history
             .insertion_slices
-            .insert(timestamp.local(), insertion_slices);
+            .insert(timestamp, insertion_slices);
         self.subscriptions.publish_mut(&edits_patch)
     }
 
     fn fragment_ids_for_edits<'a>(
         &'a self,
-        edit_ids: impl Iterator<Item = &'a clock::Local>,
+        edit_ids: impl Iterator<Item = &'a clock::Lamport>,
     ) -> Vec<&'a Locator> {
         // Get all of the insertion slices changed by the given edits.
         let mut insertion_slices = Vec::new();
@@ -1105,7 +1061,7 @@ impl Buffer {
                 let fragment_was_visible = fragment.visible;
 
                 fragment.visible = fragment.is_visible(&self.undo_map);
-                fragment.max_undos.observe(undo.id);
+                fragment.max_undos.observe(undo.timestamp);
 
                 let old_start = old_fragments.start().1;
                 let new_start = new_fragments.summary().text.visible;
@@ -1159,10 +1115,10 @@ impl Buffer {
         if self.deferred_replicas.contains(&op.replica_id()) {
             false
         } else {
-            match op {
-                Operation::Edit(edit) => self.version.observed_all(&edit.version),
-                Operation::Undo { undo, .. } => self.version.observed_all(&undo.version),
-            }
+            self.version.observed_all(match op {
+                Operation::Edit(edit) => &edit.version,
+                Operation::Undo(undo) => &undo.version,
+            })
         }
     }
 
@@ -1180,7 +1136,7 @@ impl Buffer {
 
     pub fn start_transaction_at(&mut self, now: Instant) -> Option<TransactionId> {
         self.history
-            .start_transaction(self.version.clone(), now, &mut self.local_clock)
+            .start_transaction(self.version.clone(), now, &mut self.lamport_clock)
     }
 
     pub fn end_transaction(&mut self) -> Option<(TransactionId, clock::Global)> {
@@ -1209,7 +1165,7 @@ impl Buffer {
         &self.history.base_text
     }
 
-    pub fn operations(&self) -> &TreeMap<clock::Local, Operation> {
+    pub fn operations(&self) -> &TreeMap<clock::Lamport, Operation> {
         &self.history.operations
     }
 
@@ -1289,16 +1245,13 @@ impl Buffer {
         }
 
         let undo = UndoOperation {
-            id: self.local_clock.tick(),
+            timestamp: self.lamport_clock.tick(),
             version: self.version(),
             counts,
         };
         self.apply_undo(&undo)?;
-        let operation = Operation::Undo {
-            undo,
-            lamport_timestamp: self.lamport_clock.tick(),
-        };
-        self.snapshot.version.observe(operation.local_timestamp());
+        self.snapshot.version.observe(undo.timestamp);
+        let operation = Operation::Undo(undo);
         self.history.push(operation.clone());
         Ok(operation)
     }
@@ -1363,7 +1316,7 @@ impl Buffer {
 
     pub fn wait_for_edits(
         &mut self,
-        edit_ids: impl IntoIterator<Item = clock::Local>,
+        edit_ids: impl IntoIterator<Item = clock::Lamport>,
     ) -> impl 'static + Future<Output = Result<()>> {
         let mut futures = Vec::new();
         for edit_id in edit_ids {
@@ -1435,7 +1388,7 @@ impl Buffer {
         self.wait_for_version_txs.clear();
     }
 
-    fn resolve_edit(&mut self, edit_id: clock::Local) {
+    fn resolve_edit(&mut self, edit_id: clock::Lamport) {
         for mut tx in self
             .edit_id_resolvers
             .remove(&edit_id)
@@ -1513,7 +1466,7 @@ impl Buffer {
                 .insertions
                 .get(
                     &InsertionFragmentKey {
-                        timestamp: fragment.insertion_timestamp.local(),
+                        timestamp: fragment.timestamp,
                         split_offset: fragment.insertion_offset,
                     },
                     &(),
@@ -1996,7 +1949,7 @@ impl BufferSnapshot {
             let fragment = fragment_cursor.item().unwrap();
             let overshoot = offset - *fragment_cursor.start();
             Anchor {
-                timestamp: fragment.insertion_timestamp.local(),
+                timestamp: fragment.timestamp,
                 offset: fragment.insertion_offset + overshoot,
                 bias,
                 buffer_id: Some(self.remote_id),
@@ -2188,15 +2141,14 @@ impl<'a, D: TextDimension + Ord, F: FnMut(&FragmentSummary) -> bool> Iterator fo
                 break;
             }
 
-            let timestamp = fragment.insertion_timestamp.local();
             let start_anchor = Anchor {
-                timestamp,
+                timestamp: fragment.timestamp,
                 offset: fragment.insertion_offset,
                 bias: Bias::Right,
                 buffer_id: Some(self.buffer_id),
             };
             let end_anchor = Anchor {
-                timestamp,
+                timestamp: fragment.timestamp,
                 offset: fragment.insertion_offset + fragment.len,
                 bias: Bias::Left,
                 buffer_id: Some(self.buffer_id),
@@ -2269,19 +2221,17 @@ impl<'a, D: TextDimension + Ord, F: FnMut(&FragmentSummary) -> bool> Iterator fo
 impl Fragment {
     fn insertion_slice(&self) -> InsertionSlice {
         InsertionSlice {
-            insertion_id: self.insertion_timestamp.local(),
+            insertion_id: self.timestamp,
             range: self.insertion_offset..self.insertion_offset + self.len,
         }
     }
 
     fn is_visible(&self, undos: &UndoMap) -> bool {
-        !undos.is_undone(self.insertion_timestamp.local())
-            && self.deletions.iter().all(|d| undos.is_undone(*d))
+        !undos.is_undone(self.timestamp) && self.deletions.iter().all(|d| undos.is_undone(*d))
     }
 
     fn was_visible(&self, version: &clock::Global, undos: &UndoMap) -> bool {
-        (version.observed(self.insertion_timestamp.local())
-            && !undos.was_undone(self.insertion_timestamp.local(), version))
+        (version.observed(self.timestamp) && !undos.was_undone(self.timestamp, version))
             && self
                 .deletions
                 .iter()
@@ -2294,14 +2244,14 @@ impl sum_tree::Item for Fragment {
 
     fn summary(&self) -> Self::Summary {
         let mut max_version = clock::Global::new();
-        max_version.observe(self.insertion_timestamp.local());
+        max_version.observe(self.timestamp);
         for deletion in &self.deletions {
             max_version.observe(*deletion);
         }
         max_version.join(&self.max_undos);
 
         let mut min_insertion_version = clock::Global::new();
-        min_insertion_version.observe(self.insertion_timestamp.local());
+        min_insertion_version.observe(self.timestamp);
         let max_insertion_version = min_insertion_version.clone();
         if self.visible {
             FragmentSummary {
@@ -2378,7 +2328,7 @@ impl sum_tree::KeyedItem for InsertionFragment {
 impl InsertionFragment {
     fn new(fragment: &Fragment) -> Self {
         Self {
-            timestamp: fragment.insertion_timestamp.local(),
+            timestamp: fragment.timestamp,
             split_offset: fragment.insertion_offset,
             fragment_id: fragment.id.clone(),
         }
@@ -2501,10 +2451,10 @@ impl Operation {
         operation_queue::Operation::lamport_timestamp(self).replica_id
     }
 
-    pub fn local_timestamp(&self) -> clock::Local {
+    pub fn timestamp(&self) -> clock::Lamport {
         match self {
-            Operation::Edit(edit) => edit.timestamp.local(),
-            Operation::Undo { undo, .. } => undo.id,
+            Operation::Edit(edit) => edit.timestamp,
+            Operation::Undo(undo) => undo.timestamp,
         }
     }
 
@@ -2523,10 +2473,8 @@ impl Operation {
 impl operation_queue::Operation for Operation {
     fn lamport_timestamp(&self) -> clock::Lamport {
         match self {
-            Operation::Edit(edit) => edit.timestamp.lamport(),
-            Operation::Undo {
-                lamport_timestamp, ..
-            } => *lamport_timestamp,
+            Operation::Edit(edit) => edit.timestamp,
+            Operation::Undo(undo) => undo.timestamp,
         }
     }
 }

crates/text/src/undo_map.rs 🔗

@@ -26,8 +26,8 @@ impl sum_tree::KeyedItem for UndoMapEntry {
 
 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
 struct UndoMapKey {
-    edit_id: clock::Local,
-    undo_id: clock::Local,
+    edit_id: clock::Lamport,
+    undo_id: clock::Lamport,
 }
 
 impl sum_tree::Summary for UndoMapKey {
@@ -50,7 +50,7 @@ impl UndoMap {
                 sum_tree::Edit::Insert(UndoMapEntry {
                     key: UndoMapKey {
                         edit_id: *edit_id,
-                        undo_id: undo.id,
+                        undo_id: undo.timestamp,
                     },
                     undo_count: *count,
                 })
@@ -59,11 +59,11 @@ impl UndoMap {
         self.0.edit(edits, &());
     }
 
-    pub fn is_undone(&self, edit_id: clock::Local) -> bool {
+    pub fn is_undone(&self, edit_id: clock::Lamport) -> bool {
         self.undo_count(edit_id) % 2 == 1
     }
 
-    pub fn was_undone(&self, edit_id: clock::Local, version: &clock::Global) -> bool {
+    pub fn was_undone(&self, edit_id: clock::Lamport, version: &clock::Global) -> bool {
         let mut cursor = self.0.cursor::<UndoMapKey>();
         cursor.seek(
             &UndoMapKey {
@@ -88,7 +88,7 @@ impl UndoMap {
         undo_count % 2 == 1
     }
 
-    pub fn undo_count(&self, edit_id: clock::Local) -> u32 {
+    pub fn undo_count(&self, edit_id: clock::Lamport) -> u32 {
         let mut cursor = self.0.cursor::<UndoMapKey>();
         cursor.seek(
             &UndoMapKey {