map and array insert, delete for apply()
This commit is contained in:
parent
b5742315ef
commit
47fa3ae218
8 changed files with 377 additions and 38 deletions
automerge-wasm
automerge/src
15
automerge-wasm/index.d.ts
vendored
15
automerge-wasm/index.d.ts
vendored
|
@ -1,2 +1,17 @@
|
|||
import { Automerge as VanillaAutomerge } from "automerge-types"
|
||||
|
||||
export * from "automerge-types"
|
||||
export { default } from "automerge-types"
|
||||
|
||||
export class Automerge extends VanillaAutomerge {
|
||||
// experimental api can go here
|
||||
applyPatches(obj: any): any;
|
||||
|
||||
// override old methods that return automerge
|
||||
clone(actor?: string): Automerge;
|
||||
fork(actor?: string): Automerge;
|
||||
forkAt(heads: Heads, actor?: string): Automerge;
|
||||
}
|
||||
|
||||
export function create(actor?: Actor): Automerge;
|
||||
export function load(data: Uint8Array, actor?: Actor): Automerge;
|
||||
|
|
|
@ -2,13 +2,13 @@ use crate::AutoCommit;
|
|||
use automerge as am;
|
||||
use automerge::transaction::Transactable;
|
||||
use automerge::{Change, ChangeHash, Prop};
|
||||
use js_sys::{Array, Object, Reflect, Uint8Array};
|
||||
use js_sys::{Array, Function, Object, Reflect, Uint8Array};
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
use std::fmt::Display;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
use crate::{ObjId, ScalarValue, Value};
|
||||
use crate::{observer::Patch, ObjId, ScalarValue, Value};
|
||||
|
||||
pub(crate) struct JS(pub(crate) JsValue);
|
||||
pub(crate) struct AR(pub(crate) Array);
|
||||
|
@ -462,6 +462,12 @@ pub(crate) fn list_to_js_at(doc: &AutoCommit, obj: &ObjId, heads: &[ChangeHash])
|
|||
array.into()
|
||||
}
|
||||
|
||||
/*
|
||||
pub(crate) fn export_values<'a, V: Iterator<Item = Value<'a>>>(val: V) -> Array {
|
||||
val.map(|v| export_value(&v)).collect()
|
||||
}
|
||||
*/
|
||||
|
||||
pub(crate) fn export_value(val: &Value<'_>) -> JsValue {
|
||||
match val {
|
||||
Value::Object(o) if o == &am::ObjType::Map || o == &am::ObjType::Table => {
|
||||
|
@ -471,3 +477,99 @@ pub(crate) fn export_value(val: &Value<'_>) -> JsValue {
|
|||
Value::Scalar(v) => ScalarValue(v.clone()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_patch(obj: JsValue, patch: &Patch) -> Result<JsValue, JsValue> {
|
||||
apply_patch2(obj, patch, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn apply_patch2(obj: JsValue, patch: &Patch, depth: usize) -> Result<JsValue, JsValue> {
|
||||
match (js_to_map_seq(&obj)?, patch.path().get(depth)) {
|
||||
(JsObj::Map(o), Some(Prop::Map(key))) => {
|
||||
let sub_obj = Reflect::get(&obj, &key.into())?;
|
||||
let new_value = apply_patch2(sub_obj, patch, depth + 1)?;
|
||||
let result =
|
||||
Reflect::construct(&o.constructor(), &Array::new())?.dyn_into::<Object>()?;
|
||||
let result = Object::assign(&result, &o).into();
|
||||
Reflect::set(&result, &key.into(), &new_value)?;
|
||||
Ok(result)
|
||||
}
|
||||
(JsObj::Seq(a), Some(Prop::Seq(index))) => {
|
||||
let index = JsValue::from_f64(*index as f64);
|
||||
let sub_obj = Reflect::get(&obj, &index)?;
|
||||
let new_value = apply_patch2(sub_obj, patch, depth + 1)?;
|
||||
let result = Reflect::construct(&a.constructor(), &a)?;
|
||||
//web_sys::console::log_2(&format!("NEW VAL {}: ", tmpi).into(), &new_value);
|
||||
Reflect::set(&result, &index, &new_value)?;
|
||||
Ok(result)
|
||||
}
|
||||
(JsObj::Map(o), None) => {
|
||||
let result =
|
||||
Reflect::construct(&o.constructor(), &Array::new())?.dyn_into::<Object>()?;
|
||||
let result = Object::assign(&result, &o);
|
||||
match patch {
|
||||
Patch::PutMap { key, value, .. } => {
|
||||
let result = result.into();
|
||||
Reflect::set(&result, &key.into(), &export_value(value))?;
|
||||
Ok(result)
|
||||
}
|
||||
Patch::DeleteMap { key, .. } => {
|
||||
Reflect::delete_property(&result, &key.into())?;
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::Insert { .. } => Err(to_js_err("cannot insert into map")),
|
||||
Patch::DeleteSeq { .. } => Err(to_js_err("cannot splice a map")),
|
||||
Patch::PutSeq { .. } => Err(to_js_err("cannot array index a map")),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
(JsObj::Seq(a), None) => {
|
||||
match patch {
|
||||
Patch::PutSeq { index, value, .. } => {
|
||||
let result = Reflect::construct(&a.constructor(), &a)?;
|
||||
Reflect::set(&result, &(*index as f64).into(), &export_value(value))?;
|
||||
Ok(result)
|
||||
}
|
||||
Patch::DeleteSeq { index, .. } => {
|
||||
let result = &a.dyn_into::<Array>()?;
|
||||
let mut f = |_, i, _| i != *index as u32;
|
||||
let result = result.filter(&mut f);
|
||||
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::Insert { index, values, .. } => {
|
||||
let from = Reflect::get(&a.constructor().into(), &"from".into())?
|
||||
.dyn_into::<Function>()?;
|
||||
let result = from.call1(&JsValue::undefined(), &a)?.dyn_into::<Array>()?;
|
||||
// FIXME: should be one function call
|
||||
for v in values {
|
||||
result.splice(*index as u32, 0, &export_value(v));
|
||||
}
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::DeleteMap { .. } => Err(to_js_err("cannot delete from a seq")),
|
||||
Patch::PutMap { .. } => Err(to_js_err("cannot set key in seq")),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
(_, _) => Err(to_js_err(format!(
|
||||
"object/patch missmatch {:?} depth={:?}",
|
||||
patch, depth
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum JsObj {
|
||||
Map(Object),
|
||||
Seq(Array),
|
||||
}
|
||||
|
||||
fn js_to_map_seq(value: &JsValue) -> Result<JsObj, JsValue> {
|
||||
if let Ok(array) = value.clone().dyn_into::<Array>() {
|
||||
Ok(JsObj::Seq(array))
|
||||
} else if let Ok(obj) = value.clone().dyn_into::<Object>() {
|
||||
Ok(JsObj::Map(obj))
|
||||
} else {
|
||||
Err(to_js_err("obj is not Object or Array"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ mod value;
|
|||
use observer::Observer;
|
||||
|
||||
use interop::{
|
||||
get_heads, js_get, js_set, list_to_js, list_to_js_at, map_to_js, map_to_js_at, to_js_err,
|
||||
to_objtype, to_prop, AR, JS,
|
||||
apply_patch, get_heads, js_get, js_set, list_to_js, list_to_js_at, map_to_js, map_to_js_at,
|
||||
to_js_err, to_objtype, to_prop, AR, JS,
|
||||
};
|
||||
use sync::SyncState;
|
||||
use value::{datatype, ScalarValue};
|
||||
|
@ -426,22 +426,29 @@ impl Automerge {
|
|||
|
||||
#[wasm_bindgen(js_name = enablePatches)]
|
||||
pub fn enable_patches(&mut self, enable: JsValue) -> Result<(), JsValue> {
|
||||
self.doc.ensure_transaction_closed();
|
||||
let enable = enable
|
||||
.as_bool()
|
||||
.ok_or_else(|| to_js_err("must pass a bool to enable_patches"))?;
|
||||
self.doc.op_observer.enable(enable);
|
||||
self.doc.observer().enable(enable);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = applyPatches)]
|
||||
pub fn apply_patches(&mut self, mut object: JsValue) -> Result<JsValue, JsValue> {
|
||||
let patches = self.doc.observer().take_patches();
|
||||
for p in patches {
|
||||
object = apply_patch(object, &p)?;
|
||||
}
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = popPatches)]
|
||||
pub fn pop_patches(&mut self) -> Result<Array, JsValue> {
|
||||
// transactions send out observer updates as they occur, not waiting for them to be
|
||||
// committed.
|
||||
// If we pop the patches then we won't be able to revert them.
|
||||
|
||||
self.doc.ensure_transaction_closed();
|
||||
let patches = self.doc.op_observer.take_patches();
|
||||
let patches = self.doc.observer().take_patches();
|
||||
let result = Array::new();
|
||||
for p in patches {
|
||||
result.push(&p.try_into()?);
|
||||
|
|
|
@ -21,14 +21,31 @@ impl Observer {
|
|||
}
|
||||
self.enabled = enable;
|
||||
}
|
||||
|
||||
fn push(&mut self, patch: Patch) {
|
||||
if let Some(tail) = self.patches.last_mut() {
|
||||
if let Some(p) = tail.merge(patch) {
|
||||
self.patches.push(p)
|
||||
}
|
||||
} else {
|
||||
self.patches.push(patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum Patch {
|
||||
Put {
|
||||
PutMap {
|
||||
obj: ObjId,
|
||||
path: Vec<Prop>,
|
||||
prop: Prop,
|
||||
key: String,
|
||||
value: Value<'static>,
|
||||
conflict: bool,
|
||||
},
|
||||
PutSeq {
|
||||
obj: ObjId,
|
||||
path: Vec<Prop>,
|
||||
index: usize,
|
||||
value: Value<'static>,
|
||||
conflict: bool,
|
||||
},
|
||||
|
@ -36,7 +53,7 @@ pub(crate) enum Patch {
|
|||
obj: ObjId,
|
||||
path: Vec<Prop>,
|
||||
index: usize,
|
||||
value: Value<'static>,
|
||||
values: Vec<Value<'static>>,
|
||||
},
|
||||
Increment {
|
||||
obj: ObjId,
|
||||
|
@ -44,10 +61,16 @@ pub(crate) enum Patch {
|
|||
prop: Prop,
|
||||
value: i64,
|
||||
},
|
||||
Delete {
|
||||
DeleteMap {
|
||||
obj: ObjId,
|
||||
path: Vec<Prop>,
|
||||
prop: Prop,
|
||||
key: String,
|
||||
},
|
||||
DeleteSeq {
|
||||
obj: ObjId,
|
||||
path: Vec<Prop>,
|
||||
index: usize,
|
||||
length: usize,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -66,7 +89,7 @@ impl OpObserver for Observer {
|
|||
path,
|
||||
obj,
|
||||
index,
|
||||
value,
|
||||
values: vec![value],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -82,13 +105,23 @@ impl OpObserver for Observer {
|
|||
if self.enabled {
|
||||
let path = parents.path().into_iter().map(|p| p.1).collect();
|
||||
let value = tagged_value.0.to_owned();
|
||||
self.patches.push(Patch::Put {
|
||||
path,
|
||||
obj,
|
||||
prop,
|
||||
value,
|
||||
conflict,
|
||||
})
|
||||
let patch = match prop {
|
||||
Prop::Map(key) => Patch::PutMap {
|
||||
path,
|
||||
obj,
|
||||
key,
|
||||
value,
|
||||
conflict,
|
||||
},
|
||||
Prop::Seq(index) => Patch::PutSeq {
|
||||
path,
|
||||
obj,
|
||||
index,
|
||||
value,
|
||||
conflict,
|
||||
},
|
||||
};
|
||||
self.patches.push(patch);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +147,16 @@ impl OpObserver for Observer {
|
|||
fn delete(&mut self, mut parents: Parents<'_>, obj: ObjId, prop: Prop) {
|
||||
if self.enabled {
|
||||
let path = parents.path().into_iter().map(|p| p.1).collect();
|
||||
self.patches.push(Patch::Delete { path, obj, prop })
|
||||
let patch = match prop {
|
||||
Prop::Map(key) => Patch::DeleteMap { path, obj, key },
|
||||
Prop::Seq(index) => Patch::DeleteSeq {
|
||||
path,
|
||||
obj,
|
||||
index,
|
||||
length: 1,
|
||||
},
|
||||
};
|
||||
self.patches.push(patch)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,35 +188,97 @@ fn export_path(path: &[Prop], end: &Prop) -> Array {
|
|||
result
|
||||
}
|
||||
|
||||
impl Patch {
|
||||
pub(crate) fn path(&self) -> &[Prop] {
|
||||
match &self {
|
||||
Self::PutMap { path, .. } => path.as_slice(),
|
||||
Self::PutSeq { path, .. } => path.as_slice(),
|
||||
Self::Increment { path, .. } => path.as_slice(),
|
||||
Self::Insert { path, .. } => path.as_slice(),
|
||||
Self::DeleteMap { path, .. } => path.as_slice(),
|
||||
Self::DeleteSeq { path, .. } => path.as_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: Patch) -> Option<Patch> {
|
||||
match (self, &other) {
|
||||
(
|
||||
Self::Insert {
|
||||
obj, index, values, ..
|
||||
},
|
||||
Self::Insert {
|
||||
obj: o2,
|
||||
values: v2,
|
||||
index: i2,
|
||||
..
|
||||
},
|
||||
) if obj == o2 && *index + values.len() == *i2 => {
|
||||
// TODO - there's a way to do this without the clone im sure
|
||||
values.extend_from_slice(v2.as_slice());
|
||||
None
|
||||
}
|
||||
_ => Some(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Patch> for JsValue {
|
||||
type Error = JsValue;
|
||||
|
||||
fn try_from(p: Patch) -> Result<Self, Self::Error> {
|
||||
let result = Object::new();
|
||||
match p {
|
||||
Patch::Put {
|
||||
Patch::PutMap {
|
||||
path,
|
||||
prop,
|
||||
key,
|
||||
value,
|
||||
conflict,
|
||||
..
|
||||
} => {
|
||||
js_set(&result, "action", "put")?;
|
||||
js_set(&result, "path", export_path(path.as_slice(), &prop))?;
|
||||
js_set(
|
||||
&result,
|
||||
"path",
|
||||
export_path(path.as_slice(), &Prop::Map(key)),
|
||||
)?;
|
||||
js_set(&result, "value", export_value(&value))?;
|
||||
js_set(&result, "conflict", &JsValue::from_bool(conflict))?;
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::Insert {
|
||||
path, index, value, ..
|
||||
Patch::PutSeq {
|
||||
path,
|
||||
index,
|
||||
value,
|
||||
conflict,
|
||||
..
|
||||
} => {
|
||||
js_set(&result, "action", "ins")?;
|
||||
js_set(&result, "action", "put")?;
|
||||
js_set(
|
||||
&result,
|
||||
"path",
|
||||
export_path(path.as_slice(), &Prop::Seq(index)),
|
||||
)?;
|
||||
js_set(&result, "value", export_value(&value))?;
|
||||
js_set(&result, "conflict", &JsValue::from_bool(conflict))?;
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::Insert {
|
||||
path,
|
||||
index,
|
||||
values,
|
||||
..
|
||||
} => {
|
||||
js_set(&result, "action", "splice")?;
|
||||
js_set(
|
||||
&result,
|
||||
"path",
|
||||
export_path(path.as_slice(), &Prop::Seq(index)),
|
||||
)?;
|
||||
js_set(
|
||||
&result,
|
||||
"values",
|
||||
values.iter().map(export_value).collect::<Array>(),
|
||||
)?;
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::Increment {
|
||||
|
@ -185,9 +289,30 @@ impl TryFrom<Patch> for JsValue {
|
|||
js_set(&result, "value", &JsValue::from_f64(value as f64))?;
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::Delete { path, prop, .. } => {
|
||||
Patch::DeleteMap { path, key, .. } => {
|
||||
js_set(&result, "action", "del")?;
|
||||
js_set(&result, "path", export_path(path.as_slice(), &prop))?;
|
||||
js_set(
|
||||
&result,
|
||||
"path",
|
||||
export_path(path.as_slice(), &Prop::Map(key)),
|
||||
)?;
|
||||
Ok(result.into())
|
||||
}
|
||||
Patch::DeleteSeq {
|
||||
path,
|
||||
index,
|
||||
length,
|
||||
..
|
||||
} => {
|
||||
js_set(&result, "action", "del")?;
|
||||
js_set(
|
||||
&result,
|
||||
"path",
|
||||
export_path(path.as_slice(), &Prop::Seq(index)),
|
||||
)?;
|
||||
if length > 1 {
|
||||
js_set(&result, "length", length)?;
|
||||
}
|
||||
Ok(result.into())
|
||||
}
|
||||
}
|
||||
|
|
85
automerge-wasm/test/apply.ts
Normal file
85
automerge-wasm/test/apply.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
|
||||
import { describe, it } from 'mocha';
|
||||
//@ts-ignore
|
||||
import assert from 'assert'
|
||||
//@ts-ignore
|
||||
import init, { create, load } from '..'
|
||||
|
||||
describe('Automerge', () => {
|
||||
describe('Patch Apply', () => {
|
||||
it('apply nested sets on maps', () => {
|
||||
let start : any = { hello: { mellow: { yellow: "world", x: 1 }, y : 2 } }
|
||||
let doc1 = create()
|
||||
doc1.putObject("/", "hello", start.hello);
|
||||
let mat = doc1.materialize("/")
|
||||
let doc2 = create()
|
||||
doc2.enablePatches(true)
|
||||
doc2.merge(doc1)
|
||||
|
||||
let base = doc2.applyPatches({})
|
||||
assert.deepEqual(mat, start)
|
||||
assert.deepEqual(base, start)
|
||||
|
||||
doc2.delete("/hello/mellow", "yellow");
|
||||
delete start.hello.mellow.yellow;
|
||||
base = doc2.applyPatches(base)
|
||||
mat = doc2.materialize("/")
|
||||
|
||||
assert.deepEqual(mat, start)
|
||||
assert.deepEqual(base, start)
|
||||
})
|
||||
|
||||
it('apply patches on lists', () => {
|
||||
//let start = { list: [1,2,3,4,5,6] }
|
||||
let start = { list: [1,2,3,4] }
|
||||
let doc1 = create()
|
||||
doc1.putObject("/", "list", start.list);
|
||||
let mat = doc1.materialize("/")
|
||||
let doc2 = create()
|
||||
doc2.enablePatches(true)
|
||||
doc2.merge(doc1)
|
||||
mat = doc1.materialize("/")
|
||||
let base = doc2.applyPatches({})
|
||||
assert.deepEqual(mat, start)
|
||||
assert.deepEqual(base, start)
|
||||
|
||||
doc2.delete("/list", 3);
|
||||
start.list.splice(3,1)
|
||||
base = doc2.applyPatches(base)
|
||||
|
||||
assert.deepEqual(base, start)
|
||||
})
|
||||
|
||||
it('apply patches on lists of lists of lists', () => {
|
||||
let start = { list:
|
||||
[
|
||||
[
|
||||
[ 1, 2, 3, 4, 5, 6],
|
||||
[ 7, 8, 9,10,11,12],
|
||||
],
|
||||
[
|
||||
[ 7, 8, 9,10,11,12],
|
||||
[ 1, 2, 3, 4, 5, 6],
|
||||
]
|
||||
]
|
||||
}
|
||||
let doc1 = create()
|
||||
doc1.enablePatches(true)
|
||||
doc1.putObject("/", "list", start.list);
|
||||
let mat = doc1.materialize("/")
|
||||
let base = doc1.applyPatches({})
|
||||
assert.deepEqual(mat, start)
|
||||
|
||||
doc1.delete("/list/0/1", 3)
|
||||
start.list[0][1].splice(3,1)
|
||||
|
||||
doc1.delete("/list/0", 0)
|
||||
start.list[0].splice(0,1)
|
||||
|
||||
mat = doc1.materialize("/")
|
||||
base = doc1.applyPatches(base)
|
||||
assert.deepEqual(mat, start)
|
||||
assert.deepEqual(base, start)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -548,8 +548,8 @@ describe('Automerge', () => {
|
|||
doc2.loadIncremental(doc1.saveIncremental())
|
||||
assert.deepEqual(doc2.popPatches(), [
|
||||
{ action: 'put', path: [ 'birds' ], value: [], conflict: false },
|
||||
{ action: 'ins', path: [ 'birds', 0 ], value: 'Goldfinch' },
|
||||
{ action: 'ins', path: [ 'birds', 1 ], value: 'Chaffinch' }
|
||||
{ action: 'splice', path: [ 'birds', 0 ], values: ['Goldfinch'] },
|
||||
{ action: 'splice', path: [ 'birds', 1 ], values: ['Chaffinch'] }
|
||||
])
|
||||
doc1.free()
|
||||
doc2.free()
|
||||
|
@ -563,7 +563,7 @@ describe('Automerge', () => {
|
|||
doc2.enablePatches(true)
|
||||
doc2.loadIncremental(doc1.saveIncremental())
|
||||
assert.deepEqual(doc2.popPatches(), [
|
||||
{ action: 'ins', path: [ 'birds', 0 ], value: {} },
|
||||
{ action: 'splice', path: [ 'birds', 0 ], values: [{}] },
|
||||
{ action: 'put', path: [ 'birds', 0, 'species' ], value: 'Goldfinch', conflict: false },
|
||||
{ action: 'put', path: [ 'birds', 0, 'count', ], value: 3, conflict: false }
|
||||
])
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
pub struct AutoCommitWithObs<Obs: OpObserver> {
|
||||
doc: Automerge,
|
||||
transaction: Option<(Obs, TransactionInner)>,
|
||||
pub op_observer: Obs,
|
||||
op_observer: Obs,
|
||||
}
|
||||
|
||||
pub type AutoCommit = AutoCommitWithObs<()>;
|
||||
|
@ -43,6 +43,11 @@ impl AutoCommit {
|
|||
}
|
||||
|
||||
impl<Obs: OpObserver> AutoCommitWithObs<Obs> {
|
||||
pub fn observer(&mut self) -> &mut Obs {
|
||||
self.ensure_transaction_closed();
|
||||
&mut self.op_observer
|
||||
}
|
||||
|
||||
pub fn with_observer<Obs2: OpObserver>(self, op_observer: Obs2) -> AutoCommitWithObs<Obs2> {
|
||||
AutoCommitWithObs {
|
||||
doc: self.doc,
|
||||
|
@ -98,7 +103,7 @@ impl<Obs: OpObserver> AutoCommitWithObs<Obs> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn ensure_transaction_closed(&mut self) {
|
||||
fn ensure_transaction_closed(&mut self) {
|
||||
if let Some((current, tx)) = self.transaction.take() {
|
||||
self.op_observer.merge(¤t);
|
||||
tx.commit(&mut self.doc, None, None);
|
||||
|
|
|
@ -1441,7 +1441,7 @@ fn observe_counter_change_application_overwrite() {
|
|||
doc3.merge(&mut doc2).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
doc3.op_observer.take_patches(),
|
||||
doc3.observer().take_patches(),
|
||||
vec![Patch::Put {
|
||||
obj: ExId::Root,
|
||||
path: vec![],
|
||||
|
@ -1458,7 +1458,7 @@ fn observe_counter_change_application_overwrite() {
|
|||
doc4.merge(&mut doc1).unwrap();
|
||||
|
||||
// no patches as the increments operate on an invisible counter
|
||||
assert_eq!(doc4.op_observer.take_patches(), vec![]);
|
||||
assert_eq!(doc4.observer().take_patches(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1472,7 +1472,7 @@ fn observe_counter_change_application() {
|
|||
let mut new_doc = AutoCommit::new().with_observer(VecOpObserver::default());
|
||||
new_doc.apply_changes(changes).unwrap();
|
||||
assert_eq!(
|
||||
new_doc.op_observer.take_patches(),
|
||||
new_doc.observer().take_patches(),
|
||||
vec![
|
||||
Patch::Put {
|
||||
obj: ExId::Root,
|
||||
|
|
Loading…
Add table
Reference in a new issue