fix: get deep value & throw mismatched context err

This commit is contained in:
Zixuan Chen 2023-07-30 16:30:41 +08:00
parent c461edd828
commit 88003bdffe
6 changed files with 159 additions and 49 deletions

View file

@ -228,6 +228,7 @@ impl TextHandler {
pos: start, pos: start,
}), }),
Some(EventHint::Utf16 { pos, len: 0 }), Some(EventHint::Utf16 { pos, len: 0 }),
&self.state,
) )
} }
@ -254,6 +255,7 @@ impl TextHandler {
len: (end - start) as isize, len: (end - start) as isize,
})), })),
Some(EventHint::Utf16 { pos, len: del }), Some(EventHint::Utf16 { pos, len: del }),
&self.state,
) )
} }
} }
@ -280,6 +282,7 @@ impl ListHandler {
pos, pos,
}), }),
None, None,
&self.state,
) )
} }
@ -301,6 +304,7 @@ impl ListHandler {
pos, pos,
}), }),
None, None,
&self.state,
)?; )?;
Ok(child_idx) Ok(child_idx)
} }
@ -317,6 +321,7 @@ impl ListHandler {
len: len as isize, len: len as isize,
})), })),
None, None,
&self.state,
) )
} }
@ -344,6 +349,15 @@ impl ListHandler {
.get_value_by_idx(self.container_idx) .get_value_by_idx(self.container_idx)
} }
pub fn get_deep_value(&self) -> LoroValue {
self.state
.upgrade()
.unwrap()
.lock()
.unwrap()
.get_container_deep_value(self.container_idx)
}
pub fn id(&self) -> ContainerID { pub fn id(&self) -> ContainerID {
self.state self.state
.upgrade() .upgrade()
@ -390,6 +404,7 @@ impl MapHandler {
value, value,
}), }),
None, None,
&self.state,
) )
} }
@ -410,6 +425,7 @@ impl MapHandler {
value: LoroValue::Container(container_id), value: LoroValue::Container(container_id),
}), }),
None, None,
&self.state,
)?; )?;
Ok(child_idx) Ok(child_idx)
} }
@ -423,6 +439,7 @@ impl MapHandler {
value: LoroValue::Null, value: LoroValue::Null,
}), }),
None, None,
&self.state,
) )
} }
@ -435,6 +452,15 @@ impl MapHandler {
.get_value_by_idx(self.container_idx) .get_value_by_idx(self.container_idx)
} }
pub fn get_deep_value(&self) -> LoroValue {
self.state
.upgrade()
.unwrap()
.lock()
.unwrap()
.get_container_deep_value(self.container_idx)
}
pub fn get(&self, key: &str) -> Option<LoroValue> { pub fn get(&self, key: &str) -> Option<LoroValue> {
self.state self.state
.upgrade() .upgrade()

View file

@ -1,7 +1,7 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
mem::take, mem::take,
sync::{Arc, Mutex}, sync::{Arc, Mutex, Weak},
}; };
use debug_log::debug_dbg; use debug_log::debug_dbg;
@ -191,7 +191,16 @@ impl Transaction {
content: RawOpContent, content: RawOpContent,
// we need extra hint to reduce calculation for utf16 text op // we need extra hint to reduce calculation for utf16 text op
hint: Option<EventHint>, hint: Option<EventHint>,
// check whther context and txn are refering to the same state context
state_ref: &Weak<Mutex<DocState>>,
) -> LoroResult<()> { ) -> LoroResult<()> {
if Arc::as_ptr(&self.state) != Weak::as_ptr(state_ref) {
return Err(LoroError::UnmatchedContext {
expected: self.state.lock().unwrap().peer,
found: state_ref.upgrade().unwrap().lock().unwrap().peer,
});
}
let len = content.content_len(); let len = content.content_len();
let op = RawOp { let op = RawOp {
id: ID { id: ID {

View file

@ -291,7 +291,7 @@ fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) {
let ans = ob.call1(&event.into()); let ans = ob.call1(&event.into());
drop(copy); drop(copy);
if let Err(e) = ans { if let Err(e) = ans {
console_log!("Error when calling observer: {:#?}", e); console_error!("Error when calling observer: {:#?}", e);
} }
}); });
let _ = promise.then(&closure); let _ = promise.then(&closure);
@ -469,10 +469,9 @@ impl LoroMap {
value.into() value.into()
} }
#[wasm_bindgen(js_name = "getValueDeep")] #[wasm_bindgen(js_name = "getDeepValue")]
pub fn get_value_deep(&self) -> JsValue { pub fn get_value_deep(&self) -> JsValue {
todo!() self.0.get_deep_value().into()
// self.0.get_value_deep(ctx.deref()).into()
} }
#[wasm_bindgen(js_name = "insertContainer")] #[wasm_bindgen(js_name = "insertContainer")]
@ -556,11 +555,10 @@ impl LoroList {
self.0.get_value().into() self.0.get_value().into()
} }
#[wasm_bindgen(js_name = "getValueDeep")] #[wasm_bindgen(js_name = "getDeepValue")]
pub fn get_value_deep(&self) -> JsValue { pub fn get_deep_value(&self) -> JsValue {
todo!() let value = self.0.get_deep_value();
// let value = self.0.get_value_deep(ctx.deref()); value.into()
// value.into()
} }
#[wasm_bindgen(js_name = "insertContainer")] #[wasm_bindgen(js_name = "insertContainer")]

View file

@ -6,6 +6,8 @@ extern "C" {
// `log(..)` // `log(..)`
#[wasm_bindgen(js_namespace = console)] #[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str); pub fn log(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn error(s: &str);
} }
#[macro_export] #[macro_export]
@ -14,3 +16,10 @@ macro_rules! console_log {
// `bare_bones` // `bare_bones`
($($t:tt)*) => ($crate::log::log(&format_args!($($t)*).to_string())) ($($t:tt)*) => ($crate::log::log(&format_args!($($t)*).to_string()))
} }
#[macro_export]
macro_rules! console_error {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => ($crate::log::error(&format_args!($($t)*).to_string()))
}

View file

@ -32,8 +32,8 @@ Loro.prototype.transact = function (cb, origin) {
}); });
}; };
Loro.prototype.getTypedMap = Loro.prototype.getMap; Loro.prototype.getTypedMap = function (...args) { return this.getMap(...args) };
Loro.prototype.getTypedList = Loro.prototype.getList; Loro.prototype.getTypedList = function (...args) { return this.getList(...args) };
LoroList.prototype.getTyped = function (loro, index) { LoroList.prototype.getTyped = function (loro, index) {
const value = this.get(index); const value = this.get(index);
if (typeof value === "string" && isContainerId(value)) { if (typeof value === "string" && isContainerId(value)) {
@ -42,7 +42,9 @@ LoroList.prototype.getTyped = function (loro, index) {
return value; return value;
} }
}; };
LoroList.prototype.insertTyped = LoroList.prototype.insert; LoroList.prototype.insertTyped = function (...args) {
return this.insert(...args)
}
LoroMap.prototype.getTyped = function (loro, key) { LoroMap.prototype.getTyped = function (loro, key) {
const value = this.get(key); const value = this.get(key);
if (typeof value === "string" && isContainerId(value)) { if (typeof value === "string" && isContainerId(value)) {
@ -51,7 +53,7 @@ LoroMap.prototype.getTyped = function (loro, key) {
return value; return value;
} }
}; };
LoroMap.prototype.setTyped = LoroMap.prototype.set; LoroMap.prototype.setTyped = function (...args) { return this.set(...args) };
LoroText.prototype.insert = function (txn, pos, text) { LoroText.prototype.insert = function (txn, pos, text) {
this.__txn_insert(txn, pos, text); this.__txn_insert(txn, pos, text);

View file

@ -69,19 +69,29 @@ describe("subscribe", () => {
let i = 1; let i = 1;
const sub = loro.subscribe(() => { const sub = loro.subscribe(() => {
if (i > 0) { if (i > 0) {
list.insert(loro, 0, i); loro.transact(txn => {
list.insert(txn, 0, i);
i--; i--;
})
} }
count += 1; count += 1;
}); });
text.insert(loro, 0, "hello world"); loro.transact((txn) => {
text.insert(txn, 0, "hello world");
})
await one_ms(); await one_ms();
assertEquals(count, 2); assertEquals(count, 2);
text.insert(loro, 0, "hello world"); loro.transact((txn) => {
text.insert(txn, 0, "hello world");
});
await one_ms(); await one_ms();
assertEquals(count, 3); assertEquals(count, 3);
loro.unsubscribe(sub); loro.unsubscribe(sub);
text.insert(loro, 0, "hello world"); loro.transact(txn => {
text.insert(txn, 0, "hello world");
})
await one_ms(); await one_ms();
assertEquals(count, 3); assertEquals(count, 3);
}); });
@ -95,10 +105,16 @@ describe("subscribe", () => {
loro.unsubscribe(sub); loro.unsubscribe(sub);
}); });
assertEquals(count, 0); assertEquals(count, 0);
text.insert(loro, 0, "hello world"); loro.transact(txn => {
text.insert(txn, 0, "hello world");
})
await one_ms(); await one_ms();
assertEquals(count, 1); assertEquals(count, 1);
text.insert(loro, 0, "hello world"); loro.transact(txn => {
text.insert(txn, 0, "hello world");
})
assertEquals(count, 1); assertEquals(count, 1);
}); });
@ -109,14 +125,20 @@ describe("subscribe", () => {
const sub = loro.subscribe(() => { const sub = loro.subscribe(() => {
count += 1; count += 1;
}); });
loro.transact(loro => {
text.insert(loro, 0, "hello world"); text.insert(loro, 0, "hello world");
})
await one_ms(); await one_ms();
assertEquals(count, 1); assertEquals(count, 1);
loro.transact(loro => {
text.insert(loro, 0, "hello world"); text.insert(loro, 0, "hello world");
})
await one_ms(); await one_ms();
assertEquals(count, 2); assertEquals(count, 2);
loro.unsubscribe(sub); loro.unsubscribe(sub);
loro.transact(loro => {
text.insert(loro, 0, "hello world"); text.insert(loro, 0, "hello world");
})
await one_ms(); await one_ms();
assertEquals(count, 2); assertEquals(count, 2);
}); });
@ -144,7 +166,10 @@ describe("sync", () => {
}); });
const aText = a.getText("text"); const aText = a.getText("text");
const bText = b.getText("text"); const bText = b.getText("text");
aText.insert(a, 0, "abc"); a.transact(txn => {
aText.insert(txn, 0, "abc");
});
await one_ms(); await one_ms();
assertEquals(aText.toString(), bText.toString()); assertEquals(aText.toString(), bText.toString());
}); });
@ -152,17 +177,26 @@ describe("sync", () => {
it("sync", () => { it("sync", () => {
const loro = new Loro(); const loro = new Loro();
const text = loro.getText("text"); const text = loro.getText("text");
text.insert(loro, 0, "hello world"); loro.transact(txn => {
text.insert(txn, 0, "hello world");
});
const loro_bk = new Loro(); const loro_bk = new Loro();
loro_bk.import(loro.exportFrom(undefined)); loro_bk.import(loro.exportFrom(undefined));
assertEquals(loro_bk.toJson(), loro.toJson()); assertEquals(loro_bk.toJson(), loro.toJson());
const text_bk = loro_bk.getText("text"); const text_bk = loro_bk.getText("text");
assertEquals(text_bk.toString(), "hello world"); assertEquals(text_bk.toString(), "hello world");
text_bk.insert(loro_bk, 0, "a "); loro_bk.transact(txn => {
text_bk.insert(txn, 0, "a ");
});
loro.import(loro_bk.exportFrom(undefined)); loro.import(loro_bk.exportFrom(undefined));
assertEquals(text.toString(), "a hello world"); assertEquals(text.toString(), "a hello world");
const map = loro.getMap("map"); const map = loro.getMap("map");
map.set(loro, "key", "value"); loro.transact(txn => {
map.set(txn, "key", "value");
});
}); });
}); });
@ -198,10 +232,13 @@ describe("prelim", () => {
}); });
it("prelim map integrate", () => { it("prelim map integrate", () => {
map.set(loro, "text", prelim_text); loro.transact(txn => {
map.set(loro, "map", prelim_map); map.set(txn, "text", prelim_text);
map.set(loro, "list", prelim_list); map.set(txn, "map", prelim_map);
assertEquals(map.getValueDeep(loro), { map.set(txn, "list", prelim_list);
});
assertEquals(map.getDeepValue(), {
text: "hello everyone", text: "hello everyone",
map: { a: 1, ab: 123 }, map: { a: 1, ab: 123 },
list: [0, { a: 4 }], list: [0, { a: 4 }],
@ -212,10 +249,13 @@ describe("prelim", () => {
const prelim_text = new PrelimText("ttt"); const prelim_text = new PrelimText("ttt");
const prelim_map = new PrelimMap({ a: 1, b: 2 }); const prelim_map = new PrelimMap({ a: 1, b: 2 });
const prelim_list = new PrelimList([1, "2", { a: 4 }]); const prelim_list = new PrelimList([1, "2", { a: 4 }]);
list.insert(loro, 0, prelim_text); loro.transact(txn => {
list.insert(loro, 1, prelim_map); list.insert(txn, 0, prelim_text);
list.insert(loro, 2, prelim_list); list.insert(txn, 1, prelim_map);
assertEquals(list.getValueDeep(loro), ["ttt", { a: 1, b: 2 }, [1, "2", { list.insert(txn, 2, prelim_list);
});
assertEquals(list.getDeepValue(), ["ttt", { a: 1, b: 2 }, [1, "2", {
a: 4, a: 4,
}]]); }]]);
}); });
@ -225,27 +265,40 @@ describe("prelim", () => {
describe("wasm", () => { describe("wasm", () => {
const loro = new Loro(); const loro = new Loro();
const a = loro.getText("ha"); const a = loro.getText("ha");
a.insert(loro, 0, "hello world"); loro.transact(txn => {
a.delete(loro, 6, 5); a.insert(txn, 0, "hello world");
a.insert(loro, 6, "everyone");
const b = loro.getMap("ha");
b.set(loro, "ab", 123);
const bText = b.insertContainer(loro, "hh", "Text"); a.delete(txn, 6, 5);
a.insert(txn, 6, "everyone");
});
const b = loro.getMap("ha");
loro.transact(txn => {
b.set(txn, "ab", 123);
});
const bText = loro.transact(txn => {
return b.insertContainer(txn, "hh", "Text")
});
it("map get", () => { it("map get", () => {
assertEquals(b.get("ab"), 123); assertEquals(b.get("ab"), 123);
}); });
it("getValueDeep", () => { it("getValueDeep", () => {
bText.insert(loro, 0, "hello world Text"); loro.transact(txn => {
assertEquals(b.getValueDeep(loro), { ab: 123, hh: "hello world Text" }); bText.insert(txn, 0, "hello world Text");
});
assertEquals(b.getDeepValue(), { ab: 123, hh: "hello world Text" });
}); });
it("should throw error when using the wrong context", () => { it("should throw error when using the wrong context", () => {
expect(() => { expect(() => {
const loro2 = new Loro(); const loro2 = new Loro();
bText.insert(loro2, 0, "hello world Text"); loro2.transact(txn => {
bText.insert(txn, 0, "hello world Text");
});
}).toThrow(); }).toThrow();
}); });
@ -254,7 +307,10 @@ describe("wasm", () => {
const b2 = loro.getContainerById(id) as LoroMap; const b2 = loro.getContainerById(id) as LoroMap;
assertEquals(b2.value, b.value); assertEquals(b2.value, b.value);
assertEquals(b2.id, id); assertEquals(b2.id, id);
b2.set(loro, "0", 12); loro.transact(txn => {
b2.set(txn, "0", 12);
});
assertEquals(b2.value, b.value); assertEquals(b2.value, b.value);
}); });
}); });
@ -270,7 +326,10 @@ describe("type", () => {
it("test recursive map type", () => { it("test recursive map type", () => {
const loro = new Loro<{ map: LoroMap<{ map: LoroMap<{ name: "he" }> }> }>(); const loro = new Loro<{ map: LoroMap<{ map: LoroMap<{ name: "he" }> }> }>();
const map = loro.getTypedMap("map"); const map = loro.getTypedMap("map");
map.insertContainer(loro, "map", "Map"); loro.transact(txn => {
map.insertContainer(txn, "map", "Map");
});
const subMap = map.getTyped(loro, "map"); const subMap = map.getTyped(loro, "map");
const name = subMap.getTyped(loro, "name"); const name = subMap.getTyped(loro, "name");
expectTypeOf(name).toEqualTypeOf<"he">(); expectTypeOf(name).toEqualTypeOf<"he">();
@ -279,8 +338,15 @@ describe("type", () => {
it("works for list type", () => { it("works for list type", () => {
const loro = new Loro<{ list: LoroList<[string, number]> }>(); const loro = new Loro<{ list: LoroList<[string, number]> }>();
const list = loro.getTypedList("list"); const list = loro.getTypedList("list");
list.insertTyped(loro, 0, "123"); console.dir((list as any).__proto__);
list.insertTyped(loro, 1, 123); loro.transact(txn => {
list.insertTyped(txn, 0, "123");
});
loro.transact(txn => {
list.insertTyped(txn, 1, 123);
});
const v0 = list.getTyped(loro, 0); const v0 = list.getTyped(loro, 0);
expectTypeOf(v0).toEqualTypeOf<string>(); expectTypeOf(v0).toEqualTypeOf<string>();
const v1 = list.getTyped(loro, 1); const v1 = list.getTyped(loro, 1);