Skip to content

Commit

Permalink
fix: define the fork behavior when the doc is detached (#537)
Browse files Browse the repository at this point in the history
* fix: define the fork behavior when the doc is detached

* chore: add release note
  • Loading branch information
zxch3n authored Nov 4, 2024
1 parent 7bf6db7 commit 9b60d01
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 7 deletions.
7 changes: 7 additions & 0 deletions .changeset/metal-fishes-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"loro-crdt": patch
---

Define the behavior of `doc.fork()` when the doc is detached

It will fork at the current state_frontiers, which is equivalent to calling `doc.fork_at(&doc.state_frontiers())`
4 changes: 4 additions & 0 deletions crates/loro-internal/src/loro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ impl LoroDoc {
}

pub fn fork(&self) -> Self {
if self.is_detached() {
return self.fork_at(&self.state_frontiers());
}

self.commit_then_stop();
let snapshot = encoding::fast_snapshot::encode_snapshot_inner(self);
let doc = Self::new();
Expand Down
20 changes: 20 additions & 0 deletions crates/loro-wasm/deno_tests/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,23 @@ Deno.test("basic", () => {
doc.getText("text").insert(0, "Hello, world!");
expect(doc.getText("text").toString()).toBe("Hello, world!");
});

Deno.test("fork when detached", () => {
const doc = new LoroDoc();
doc.setPeerId("0");
doc.getText("text").insert(0, "Hello, world!");
doc.checkout([{ peer: "0", counter: 5 }]);
const newDoc = doc.fork();
newDoc.setPeerId("1");
newDoc.getText("text").insert(6, " Alice!");
// ┌───────────────┐ ┌───────────────┐
// │ Hello, │◀─┬──│ world! │
// └───────────────┘ │ └───────────────┘
// │
// │ ┌───────────────┐
// └──│ Alice! │
// └───────────────┘
doc.import(newDoc.export({ mode: "update" }));
doc.checkoutToLatest();
console.log(doc.getText("text").toString()); // "Hello, world! Alice!"
});
5 changes: 5 additions & 0 deletions crates/loro-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,16 @@ impl LoroDoc {
/// Duplicate the document with a different PeerID
///
/// The time complexity and space complexity of this operation are both O(n),
///
/// When called in detached mode, it will fork at the current state frontiers.
/// It will have the same effect as `forkAt(&self.frontiers())`.
pub fn fork(&self) -> Self {
Self(Arc::new(self.0.fork()))
}

/// Creates a new LoroDoc at a specified version (Frontiers)
///
/// The created doc will only contain the history before the specified frontiers.
#[wasm_bindgen(js_name = "forkAt")]
pub fn fork_at(&self, frontiers: Vec<JsID>) -> JsResult<LoroDoc> {
Ok(Self(Arc::new(
Expand Down
19 changes: 12 additions & 7 deletions crates/loro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,24 @@ impl LoroDoc {
/// Duplicate the document with a different PeerID
///
/// The time complexity and space complexity of this operation are both O(n),
///
/// When called in detached mode, it will fork at the current state frontiers.
/// It will have the same effect as `fork_at(&self.state_frontiers())`.
#[inline]
pub fn fork(&self) -> Self {
let doc = self.doc.fork();
LoroDoc::_new(doc)
}

/// Fork the document at the given frontiers.
///
/// The created doc will only contain the history before the specified frontiers.
pub fn fork_at(&self, frontiers: &Frontiers) -> LoroDoc {
let new_doc = self.doc.fork_at(frontiers);
new_doc.start_auto_commit();
LoroDoc::_new(new_doc)
}

/// Get the configurations of the document.
#[inline]
pub fn config(&self) -> &Configure {
Expand Down Expand Up @@ -802,13 +814,6 @@ impl LoroDoc {
})
}

/// Fork the document at the given frontiers.
pub fn fork_at(&self, frontiers: &Frontiers) -> LoroDoc {
let new_doc = self.doc.fork_at(frontiers);
new_doc.start_auto_commit();
LoroDoc::_new(new_doc)
}

/// Get the number of operations in the pending transaction.
///
/// The pending transaction is the one that is not committed yet. It will be committed
Expand Down
22 changes: 22 additions & 0 deletions crates/loro/tests/loro_rust_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1971,3 +1971,25 @@ fn test_fork_at_should_restore_attached_state() {
doc.fork_at(&[ID::new(0, 0)].into());
assert!(doc.is_detached());
}

#[test]
fn test_fork_when_detached() {
let doc = LoroDoc::new();
doc.set_peer_id(0).unwrap();
doc.get_text("text").insert(0, "Hello, world!").unwrap();
doc.checkout(&[ID::new(0, 5)].into()).unwrap();
let new_doc = doc.fork();
new_doc.set_peer_id(1).unwrap();
new_doc.get_text("text").insert(6, " Alice!").unwrap();
// ┌───────────────┐ ┌───────────────┐
// │ Hello, │◀─┬──│ world! │
// └───────────────┘ │ └───────────────┘
// │
// │ ┌───────────────┐
// └──│ Alice! │
// └───────────────┘
doc.import(&new_doc.export(loro::ExportMode::all_updates()).unwrap())
.unwrap();
doc.checkout_to_latest();
assert_eq!(doc.get_text("text").to_string(), "Hello, world! Alice!");
}

0 comments on commit 9b60d01

Please sign in to comment.