#![doc = include_str!("../README.md")]
#![allow(clippy::uninlined_format_args)]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
use event::DiffBatch;
use event::{DiffEvent, Subscriber};
pub use loro_common::InternalString;
pub use loro_internal::cursor::CannotFindRelativePosition;
use loro_internal::cursor::Cursor;
use loro_internal::cursor::PosQueryResult;
use loro_internal::cursor::Side;
pub use loro_internal::encoding::ImportStatus;
use loro_internal::handler::{HandlerTrait, ValueOrHandler};
pub use loro_internal::loro::ChangeTravelError;
pub use loro_internal::pre_commit::{
ChangeModifier, FirstCommitFromPeerCallback, FirstCommitFromPeerPayload, PreCommitCallback,
PreCommitCallbackPayload,
};
pub use loro_internal::sync;
pub use loro_internal::undo::{OnPop, UndoItemMeta, UndoOrRedo};
use loro_internal::version::shrink_frontiers;
pub use loro_internal::version::ImVersionVector;
use loro_internal::DocState;
use loro_internal::LoroDoc as InnerLoroDoc;
use loro_internal::OpLog;
use loro_internal::{
handler::Handler as InnerHandler, ListHandler as InnerListHandler,
MapHandler as InnerMapHandler, MovableListHandler as InnerMovableListHandler,
TextHandler as InnerTextHandler, TreeHandler as InnerTreeHandler,
UnknownHandler as InnerUnknownHandler,
};
use rustc_hash::FxHashSet;
use std::cmp::Ordering;
use std::ops::ControlFlow;
use std::ops::Deref;
use std::ops::Range;
use std::sync::Arc;
use tracing::info;
pub use loro_internal::diff::diff_impl::UpdateOptions;
pub use loro_internal::diff::diff_impl::UpdateTimeoutError;
pub use loro_internal::subscription::LocalUpdateCallback;
pub use loro_internal::subscription::PeerIdUpdateCallback;
pub use loro_internal::ChangeMeta;
pub use loro_internal::LORO_VERSION;
pub mod event;
pub use loro_internal::awareness;
pub use loro_internal::change::Timestamp;
pub use loro_internal::configure::Configure;
pub use loro_internal::configure::{StyleConfig, StyleConfigMap};
pub use loro_internal::container::richtext::ExpandType;
pub use loro_internal::container::{ContainerID, ContainerType, IntoContainerId};
pub use loro_internal::cursor;
pub use loro_internal::delta::{TreeDeltaItem, TreeDiff, TreeDiffItem, TreeExternalDiff};
pub use loro_internal::encoding::ImportBlobMetadata;
pub use loro_internal::encoding::{EncodedBlobMode, ExportMode};
pub use loro_internal::event::{EventTriggerKind, Index};
pub use loro_internal::handler::TextDelta;
pub use loro_internal::json;
pub use loro_internal::json::{
FutureOp as JsonFutureOp, FutureOpWrapper as JsonFutureOpWrapper, JsonChange, JsonOp,
JsonOpContent, JsonSchema, ListOp as JsonListOp, MapOp as JsonMapOp,
MovableListOp as JsonMovableListOp, TextOp as JsonTextOp, TreeOp as JsonTreeOp,
};
pub use loro_internal::kv_store::{KvStore, MemKvStore};
pub use loro_internal::loro::CommitOptions;
pub use loro_internal::loro::DocAnalysis;
pub use loro_internal::oplog::FrontiersNotIncluded;
pub use loro_internal::undo;
pub use loro_internal::version::{Frontiers, VersionRange, VersionVector, VersionVectorDiff};
pub use loro_internal::ApplyDiff;
pub use loro_internal::Subscription;
pub use loro_internal::UndoManager as InnerUndoManager;
pub use loro_internal::{loro_value, to_value};
pub use loro_internal::{
Counter, CounterSpan, FractionalIndex, IdLp, IdSpan, Lamport, PeerID, TreeID, TreeParentId, ID,
};
pub use loro_internal::{
LoroBinaryValue, LoroEncodeError, LoroError, LoroListValue, LoroMapValue, LoroResult,
LoroStringValue, LoroTreeError, LoroValue, ToJson,
};
pub use loro_kv_store as kv_store;
#[cfg(feature = "jsonpath")]
pub use loro_internal::jsonpath;
#[cfg(feature = "jsonpath")]
pub use loro_internal::jsonpath::SubscribeJsonPathCallback;
#[cfg(feature = "counter")]
mod counter;
#[cfg(feature = "counter")]
pub use counter::LoroCounter;
#[derive(Debug)]
pub struct LoroDoc {
doc: InnerLoroDoc,
#[cfg(debug_assertions)]
_temp: u8,
}
impl Default for LoroDoc {
fn default() -> Self {
Self::new()
}
}
impl Clone for LoroDoc {
fn clone(&self) -> Self {
let doc = self.doc.clone();
LoroDoc::_new(doc)
}
}
impl LoroDoc {
#[inline(always)]
fn _new(doc: InnerLoroDoc) -> Self {
Self {
doc,
#[cfg(debug_assertions)]
_temp: 0,
}
}
#[inline]
pub fn new() -> Self {
let doc = InnerLoroDoc::default();
doc.start_auto_commit();
LoroDoc::_new(doc)
}
#[inline]
pub fn fork(&self) -> Self {
let doc = self.doc.fork();
LoroDoc::_new(doc)
}
pub fn fork_at(&self, frontiers: &Frontiers) -> LoroResult<LoroDoc> {
let new_doc = self.doc.fork_at(frontiers)?;
new_doc.start_auto_commit();
Ok(LoroDoc::_new(new_doc))
}
#[inline]
pub fn config(&self) -> &Configure {
self.doc.config()
}
pub fn get_change(&self, id: ID) -> Option<ChangeMeta> {
let change = self.doc.oplog().lock().get_change_at(id)?;
Some(ChangeMeta::from_change(&change))
}
#[inline]
pub fn decode_import_blob_meta(
bytes: &[u8],
check_checksum: bool,
) -> LoroResult<ImportBlobMetadata> {
InnerLoroDoc::decode_import_blob_meta(bytes, check_checksum)
}
#[inline]
pub fn set_record_timestamp(&self, record: bool) {
self.doc.set_record_timestamp(record);
}
#[inline]
pub fn set_detached_editing(&self, enable: bool) {
self.doc.set_detached_editing(enable);
}
#[inline]
pub fn is_detached_editing_enabled(&self) -> bool {
self.doc.is_detached_editing_enabled()
}
#[inline]
pub fn set_change_merge_interval(&self, interval: i64) {
self.doc.set_change_merge_interval(interval);
}
#[inline]
pub fn config_text_style(&self, text_style: StyleConfigMap) {
self.doc.config_text_style(text_style)
}
pub fn config_default_text_style(&self, text_style: Option<StyleConfig>) {
self.doc.config_default_text_style(text_style);
}
#[inline]
pub fn attach(&self) {
self.doc.attach()
}
#[inline]
pub fn checkout(&self, frontiers: &Frontiers) -> LoroResult<()> {
self.doc.checkout(frontiers)
}
#[inline]
pub fn checkout_to_latest(&self) {
self.doc.checkout_to_latest()
}
#[inline]
pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
self.doc.cmp_with_frontiers(other)
}
#[inline]
pub fn cmp_frontiers(
&self,
a: &Frontiers,
b: &Frontiers,
) -> Result<Option<Ordering>, FrontiersNotIncluded> {
self.doc.cmp_frontiers(a, b)
}
#[inline]
pub fn detach(&self) {
self.doc.detach()
}
#[inline]
pub fn import_batch(&self, bytes: &[Vec<u8>]) -> LoroResult<ImportStatus> {
self.doc.import_batch(bytes)
}
#[inline]
pub fn get_container(&self, id: ContainerID) -> Option<Container> {
self.doc.get_handler(id).map(Container::from_handler)
}
#[inline]
pub fn get_movable_list<I: IntoContainerId>(&self, id: I) -> LoroMovableList {
LoroMovableList {
handler: self.doc.get_movable_list(id),
}
}
#[inline]
pub fn get_list<I: IntoContainerId>(&self, id: I) -> LoroList {
LoroList {
handler: self.doc.get_list(id),
}
}
#[inline]
pub fn get_map<I: IntoContainerId>(&self, id: I) -> LoroMap {
LoroMap {
handler: self.doc.get_map(id),
}
}
#[inline]
pub fn get_text<I: IntoContainerId>(&self, id: I) -> LoroText {
LoroText {
handler: self.doc.get_text(id),
}
}
#[inline]
pub fn get_tree<I: IntoContainerId>(&self, id: I) -> LoroTree {
LoroTree {
handler: self.doc.get_tree(id),
}
}
#[cfg(feature = "counter")]
#[inline]
pub fn get_counter<I: IntoContainerId>(&self, id: I) -> LoroCounter {
LoroCounter {
handler: self.doc.get_counter(id),
}
}
#[inline]
pub fn commit(&self) {
self.doc.commit_then_renew();
}
#[inline]
pub fn commit_with(&self, options: CommitOptions) {
self.doc.commit_with(options);
}
pub fn set_next_commit_message(&self, msg: &str) {
self.doc.set_next_commit_message(msg)
}
pub fn set_next_commit_origin(&self, origin: &str) {
self.doc.set_next_commit_origin(origin)
}
pub fn set_next_commit_timestamp(&self, timestamp: Timestamp) {
self.doc.set_next_commit_timestamp(timestamp)
}
pub fn set_next_commit_options(&self, options: CommitOptions) {
self.doc.set_next_commit_options(options);
}
pub fn clear_next_commit_options(&self) {
self.doc.clear_next_commit_options();
}
#[inline]
pub fn is_detached(&self) -> bool {
self.doc.is_detached()
}
pub fn from_snapshot(bytes: &[u8]) -> LoroResult<Self> {
let inner = InnerLoroDoc::from_snapshot(bytes)?;
inner.start_auto_commit();
Ok(Self::_new(inner))
}
#[inline]
pub fn import(&self, bytes: &[u8]) -> Result<ImportStatus, LoroError> {
self.doc.import_with(bytes, "".into())
}
#[inline]
pub fn import_with(&self, bytes: &[u8], origin: &str) -> Result<ImportStatus, LoroError> {
self.doc.import_with(bytes, origin.into())
}
#[inline]
pub fn import_json_updates<T: TryInto<JsonSchema>>(
&self,
json: T,
) -> Result<ImportStatus, LoroError> {
self.doc.import_json_updates(json)
}
#[inline]
pub fn export_json_updates(
&self,
start_vv: &VersionVector,
end_vv: &VersionVector,
) -> JsonSchema {
self.doc.export_json_updates(start_vv, end_vv, true)
}
#[inline]
pub fn export_json_updates_without_peer_compression(
&self,
start_vv: &VersionVector,
end_vv: &VersionVector,
) -> JsonSchema {
self.doc.export_json_updates(start_vv, end_vv, false)
}
pub fn export_json_in_id_span(&self, id_span: IdSpan) -> Vec<JsonChange> {
self.doc.export_json_in_id_span(id_span)
}
#[inline]
pub fn frontiers_to_vv(&self, frontiers: &Frontiers) -> Option<VersionVector> {
self.doc.frontiers_to_vv(frontiers)
}
pub fn minimize_frontiers(&self, frontiers: &Frontiers) -> Result<Frontiers, ID> {
self.with_oplog(|oplog| shrink_frontiers(frontiers, oplog.dag()))
}
#[inline]
pub fn vv_to_frontiers(&self, vv: &VersionVector) -> Frontiers {
self.doc.vv_to_frontiers(vv)
}
#[inline]
pub fn with_oplog<R>(&self, f: impl FnOnce(&OpLog) -> R) -> R {
let oplog = self.doc.oplog().lock();
f(&oplog)
}
#[inline]
pub fn with_state<R>(&self, f: impl FnOnce(&mut DocState) -> R) -> R {
let mut state = self.doc.app_state().lock();
f(&mut state)
}
#[inline]
pub fn oplog_vv(&self) -> VersionVector {
self.doc.oplog_vv()
}
#[inline]
pub fn state_vv(&self) -> VersionVector {
self.doc.state_vv()
}
#[inline]
pub fn shallow_since_vv(&self) -> ImVersionVector {
self.doc.shallow_since_vv()
}
#[inline]
pub fn shallow_since_frontiers(&self) -> Frontiers {
self.doc.shallow_since_frontiers()
}
#[inline]
pub fn len_ops(&self) -> usize {
self.doc.len_ops()
}
#[inline]
pub fn len_changes(&self) -> usize {
self.doc.len_changes()
}
#[inline]
pub fn get_value(&self) -> LoroValue {
self.doc.get_value()
}
#[inline]
pub fn get_deep_value(&self) -> LoroValue {
self.doc.get_deep_value()
}
pub fn get_deep_value_with_id(&self) -> LoroValue {
self.doc.app_state().lock().get_deep_value_with_id()
}
#[inline]
pub fn oplog_frontiers(&self) -> Frontiers {
self.doc.oplog_frontiers()
}
#[inline]
pub fn state_frontiers(&self) -> Frontiers {
self.doc.state_frontiers()
}
#[inline]
pub fn peer_id(&self) -> PeerID {
self.doc.peer_id()
}
#[inline]
pub fn set_peer_id(&self, peer: PeerID) -> LoroResult<()> {
self.doc.set_peer_id(peer)
}
#[inline]
pub fn subscribe(&self, container_id: &ContainerID, callback: Subscriber) -> Subscription {
self.doc.subscribe(
container_id,
Arc::new(move |e| {
callback(DiffEvent::from(e));
}),
)
}
#[inline]
pub fn subscribe_root(&self, callback: Subscriber) -> Subscription {
self.doc.subscribe_root(Arc::new(move |e| {
callback(DiffEvent::from(e));
}))
}
pub fn subscribe_local_update(&self, callback: LocalUpdateCallback) -> Subscription {
self.doc.subscribe_local_update(callback)
}
pub fn subscribe_peer_id_change(&self, callback: PeerIdUpdateCallback) -> Subscription {
self.doc.subscribe_peer_id_change(callback)
}
#[inline]
pub fn check_state_correctness_slow(&self) {
self.doc.check_state_diff_calc_consistency_slow()
}
#[inline]
pub fn get_by_path(&self, path: &[Index]) -> Option<ValueOrContainer> {
self.doc.get_by_path(path).map(ValueOrContainer::from)
}
#[inline]
pub fn get_by_str_path(&self, path: &str) -> Option<ValueOrContainer> {
self.doc.get_by_str_path(path).map(ValueOrContainer::from)
}
#[inline]
pub fn get_cursor_pos(
&self,
cursor: &Cursor,
) -> Result<PosQueryResult, CannotFindRelativePosition> {
self.doc.query_pos(cursor)
}
#[inline]
pub fn inner(&self) -> &InnerLoroDoc {
&self.doc
}
#[inline]
pub fn has_history_cache(&self) -> bool {
self.doc.has_history_cache()
}
#[inline]
pub fn free_history_cache(&self) {
self.doc.free_history_cache()
}
#[inline]
pub fn free_diff_calculator(&self) {
self.doc.free_diff_calculator()
}
#[inline]
pub fn compact_change_store(&self) {
self.doc.compact_change_store()
}
pub fn export(&self, mode: ExportMode) -> Result<Vec<u8>, LoroEncodeError> {
self.doc.export(mode)
}
pub fn analyze(&self) -> DocAnalysis {
self.doc.analyze()
}
pub fn get_path_to_container(&self, id: &ContainerID) -> Option<Vec<(ContainerID, Index)>> {
self.doc.get_path_to_container(id)
}
#[inline]
#[cfg(feature = "jsonpath")]
pub fn jsonpath(&self, path: &str) -> Result<Vec<ValueOrContainer>, JsonPathError> {
self.doc
.jsonpath(path)
.map(|vec| vec.into_iter().map(ValueOrContainer::from).collect())
}
#[cfg(feature = "jsonpath")]
pub fn subscribe_jsonpath(
&self,
jsonpath: &str,
callback: SubscribeJsonPathCallback,
) -> LoroResult<Subscription> {
self.doc.subscribe_jsonpath(jsonpath, callback)
}
pub fn get_pending_txn_len(&self) -> usize {
self.doc.get_pending_txn_len()
}
pub fn travel_change_ancestors(
&self,
ids: &[ID],
f: &mut dyn FnMut(ChangeMeta) -> ControlFlow<()>,
) -> Result<(), ChangeTravelError> {
self.doc.travel_change_ancestors(ids, f)
}
pub fn is_shallow(&self) -> bool {
self.doc.is_shallow()
}
pub fn get_changed_containers_in(&self, id: ID, len: usize) -> FxHashSet<ContainerID> {
self.doc.get_changed_containers_in(id, len)
}
#[inline]
pub fn find_id_spans_between(&self, from: &Frontiers, to: &Frontiers) -> VersionVectorDiff {
self.doc.find_id_spans_between(from, to)
}
#[inline]
pub fn revert_to(&self, version: &Frontiers) -> LoroResult<()> {
self.doc.revert_to(version)
}
#[inline]
pub fn apply_diff(&self, diff: DiffBatch) -> LoroResult<()> {
self.doc.apply_diff(diff.into())
}
#[inline]
pub fn diff(&self, a: &Frontiers, b: &Frontiers) -> LoroResult<DiffBatch> {
self.doc.diff(a, b).map(|x| x.into())
}
pub fn has_container(&self, container_id: &ContainerID) -> bool {
self.doc.has_container(container_id)
}
pub fn subscribe_first_commit_from_peer(
&self,
callback: FirstCommitFromPeerCallback,
) -> Subscription {
self.doc.subscribe_first_commit_from_peer(callback)
}
pub fn subscribe_pre_commit(&self, callback: PreCommitCallback) -> Subscription {
self.doc.subscribe_pre_commit(callback)
}
pub fn delete_root_container(&self, cid: ContainerID) {
self.doc.delete_root_container(cid);
}
pub fn set_hide_empty_root_containers(&self, hide: bool) {
self.doc.set_hide_empty_root_containers(hide);
}
}
#[allow(private_bounds)]
trait SealedTrait {}
#[allow(private_bounds)]
pub trait ContainerTrait: SealedTrait {
type Handler: HandlerTrait;
fn id(&self) -> ContainerID;
fn to_container(&self) -> Container;
fn to_handler(&self) -> Self::Handler;
fn from_handler(handler: Self::Handler) -> Self;
fn try_from_container(container: Container) -> Option<Self>
where
Self: Sized;
fn is_attached(&self) -> bool;
fn get_attached(&self) -> Option<Self>
where
Self: Sized;
fn is_deleted(&self) -> bool;
fn doc(&self) -> Option<LoroDoc>;
fn subscribe(&self, callback: Subscriber) -> Option<Subscription> {
self.doc().map(|doc| doc.subscribe(&self.id(), callback))
}
}
#[derive(Clone, Debug)]
pub struct LoroList {
handler: InnerListHandler,
}
impl SealedTrait for LoroList {}
impl ContainerTrait for LoroList {
type Handler = InnerListHandler;
fn id(&self) -> ContainerID {
self.handler.id()
}
fn to_container(&self) -> Container {
Container::List(self.clone())
}
fn to_handler(&self) -> Self::Handler {
self.handler.clone()
}
fn from_handler(handler: Self::Handler) -> Self {
Self { handler }
}
fn is_attached(&self) -> bool {
self.handler.is_attached()
}
fn get_attached(&self) -> Option<Self> {
self.handler.get_attached().map(Self::from_handler)
}
fn try_from_container(container: Container) -> Option<Self> {
container.into_list().ok()
}
fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
fn doc(&self) -> Option<LoroDoc> {
self.handler.doc().map(|doc| {
doc.start_auto_commit();
LoroDoc::_new(doc)
})
}
}
impl LoroList {
pub fn new() -> Self {
Self {
handler: InnerListHandler::new_detached(),
}
}
pub fn is_attached(&self) -> bool {
self.handler.is_attached()
}
pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
self.handler.insert(pos, v)
}
#[inline]
pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
self.handler.delete(pos, len)
}
#[inline]
pub fn get(&self, index: usize) -> Option<ValueOrContainer> {
self.handler.get_(index).map(ValueOrContainer::from)
}
#[inline]
pub fn get_deep_value(&self) -> LoroValue {
self.handler.get_deep_value()
}
#[inline]
pub fn get_value(&self) -> LoroValue {
self.handler.get_value()
}
#[inline]
pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
self.handler.pop()
}
#[inline]
pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
self.handler.push(v.into())
}
#[inline]
pub fn push_container<C: ContainerTrait>(&self, child: C) -> LoroResult<C> {
let pos = self.handler.len();
Ok(C::from_handler(
self.handler.insert_container(pos, child.to_handler())?,
))
}
pub fn for_each<I>(&self, mut f: I)
where
I: FnMut(ValueOrContainer),
{
self.handler.for_each(&mut |v| {
f(ValueOrContainer::from(v));
})
}
#[inline]
pub fn len(&self) -> usize {
self.handler.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.handler.is_empty()
}
#[inline]
pub fn insert_container<C: ContainerTrait>(&self, pos: usize, child: C) -> LoroResult<C> {
Ok(C::from_handler(
self.handler.insert_container(pos, child.to_handler())?,
))
}
pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
self.handler.get_cursor(pos, side)
}
pub fn to_vec(&self) -> Vec<LoroValue> {
self.get_value().into_list().unwrap().unwrap()
}
pub fn clear(&self) -> LoroResult<()> {
self.handler.clear()
}
pub fn get_id_at(&self, pos: usize) -> Option<ID> {
self.handler.get_id_at(pos)
}
}
impl Default for LoroList {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct LoroMap {
handler: InnerMapHandler,
}
impl SealedTrait for LoroMap {}
impl ContainerTrait for LoroMap {
type Handler = InnerMapHandler;
fn id(&self) -> ContainerID {
self.handler.id()
}
fn to_container(&self) -> Container {
Container::Map(self.clone())
}
fn to_handler(&self) -> Self::Handler {
self.handler.clone()
}
fn from_handler(handler: Self::Handler) -> Self {
Self { handler }
}
fn is_attached(&self) -> bool {
self.handler.is_attached()
}
fn get_attached(&self) -> Option<Self> {
self.handler.get_attached().map(Self::from_handler)
}
fn try_from_container(container: Container) -> Option<Self> {
container.into_map().ok()
}
fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
fn doc(&self) -> Option<LoroDoc> {
self.handler.doc().map(|doc| {
doc.start_auto_commit();
LoroDoc::_new(doc)
})
}
}
impl LoroMap {
pub fn new() -> Self {
Self {
handler: InnerMapHandler::new_detached(),
}
}
pub fn is_attached(&self) -> bool {
self.handler.is_attached()
}
pub fn delete(&self, key: &str) -> LoroResult<()> {
self.handler.delete(key)
}
pub fn for_each<I>(&self, mut f: I)
where
I: FnMut(&str, ValueOrContainer),
{
self.handler.for_each(|k, v| {
f(k, ValueOrContainer::from(v));
})
}
pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
self.handler.insert(key, value)
}
pub fn len(&self) -> usize {
self.handler.len()
}
pub fn is_empty(&self) -> bool {
self.handler.is_empty()
}
pub fn get(&self, key: &str) -> Option<ValueOrContainer> {
self.handler.get_(key).map(ValueOrContainer::from)
}
pub fn insert_container<C: ContainerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
Ok(C::from_handler(
self.handler.insert_container(key, child.to_handler())?,
))
}
pub fn get_value(&self) -> LoroValue {
self.handler.get_value()
}
pub fn get_deep_value(&self) -> LoroValue {
self.handler.get_deep_value()
}
pub fn get_or_create_container<C: ContainerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
Ok(C::from_handler(
self.handler
.get_or_create_container(key, child.to_handler())?,
))
}
pub fn clear(&self) -> LoroResult<()> {
self.handler.clear()
}
pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
self.handler.keys()
}
pub fn values(&self) -> impl Iterator<Item = ValueOrContainer> + '_ {
self.handler.values().map(ValueOrContainer::from)
}
pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
self.handler.get_last_editor(key)
}
}
impl Default for LoroMap {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct LoroText {
handler: InnerTextHandler,
}
impl SealedTrait for LoroText {}
impl ContainerTrait for LoroText {
type Handler = InnerTextHandler;
fn id(&self) -> ContainerID {
self.handler.id()
}
fn to_container(&self) -> Container {
Container::Text(self.clone())
}
fn to_handler(&self) -> Self::Handler {
self.handler.clone()
}
fn from_handler(handler: Self::Handler) -> Self {
Self { handler }
}
fn is_attached(&self) -> bool {
self.handler.is_attached()
}
fn get_attached(&self) -> Option<Self> {
self.handler.get_attached().map(Self::from_handler)
}
fn try_from_container(container: Container) -> Option<Self> {
container.into_text().ok()
}
fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
fn doc(&self) -> Option<LoroDoc> {
self.handler.doc().map(|doc| {
doc.start_auto_commit();
LoroDoc::_new(doc)
})
}
}
impl LoroText {
pub fn new() -> Self {
Self {
handler: InnerTextHandler::new_detached(),
}
}
pub fn is_attached(&self) -> bool {
self.handler.is_attached()
}
pub fn iter(&self, callback: impl FnMut(&str) -> bool) {
self.handler.iter(callback);
}
pub fn insert(&self, pos: usize, s: &str) -> LoroResult<()> {
self.handler.insert_unicode(pos, s)
}
pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
self.handler.insert_utf8(pos, s)
}
pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
self.handler.insert_utf16(pos, s)
}
pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
self.handler.delete_unicode(pos, len)
}
pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
self.handler.delete_utf8(pos, len)
}
pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
self.handler.delete_utf16(pos, len)
}
pub fn slice(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
self.handler
.slice(start_index, end_index, cursor::PosType::Unicode)
}
pub fn slice_utf16(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
self.handler.slice_utf16(start_index, end_index)
}
pub fn slice_delta(
&self,
start: usize,
end: usize,
pos_type: cursor::PosType,
) -> LoroResult<Vec<TextDelta>> {
self.handler.slice_delta(start, end, pos_type)
}
pub fn char_at(&self, pos: usize) -> LoroResult<char> {
self.handler.char_at(pos, cursor::PosType::Unicode)
}
pub fn splice(&self, pos: usize, len: usize, s: &str) -> LoroResult<String> {
self.handler.splice(pos, len, s, cursor::PosType::Unicode)
}
pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
self.handler.splice_utf16(pos, len, s)
}
pub fn is_empty(&self) -> bool {
self.handler.is_empty()
}
pub fn len_utf8(&self) -> usize {
self.handler.len_utf8()
}
pub fn len_unicode(&self) -> usize {
self.handler.len_unicode()
}
pub fn len_utf16(&self) -> usize {
self.handler.len_utf16()
}
pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
self.handler.update(text, options)
}
pub fn update_by_line(
&self,
text: &str,
options: UpdateOptions,
) -> Result<(), UpdateTimeoutError> {
self.handler.update_by_line(text, options)
}
pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
self.handler.apply_delta(delta)
}
pub fn mark(
&self,
range: Range<usize>,
key: &str,
value: impl Into<LoroValue>,
) -> LoroResult<()> {
self.handler.mark(
range.start,
range.end,
key,
value.into(),
cursor::PosType::Unicode,
)
}
pub fn mark_utf8(
&self,
range: Range<usize>,
key: &str,
value: impl Into<LoroValue>,
) -> LoroResult<()> {
self.handler.mark(
range.start,
range.end,
key,
value.into(),
cursor::PosType::Bytes,
)
}
pub fn mark_utf16(
&self,
range: Range<usize>,
key: &str,
value: impl Into<LoroValue>,
) -> LoroResult<()> {
self.handler.mark(
range.start,
range.end,
key,
value.into(),
cursor::PosType::Utf16,
)
}
pub fn unmark(&self, range: Range<usize>, key: &str) -> LoroResult<()> {
self.handler
.unmark(range.start, range.end, key, cursor::PosType::Unicode)
}
pub fn unmark_utf16(&self, range: Range<usize>, key: &str) -> LoroResult<()> {
self.handler
.unmark(range.start, range.end, key, cursor::PosType::Utf16)
}
pub fn to_delta(&self) -> Vec<TextDelta> {
let delta = self.handler.get_richtext_value().into_list().unwrap();
delta
.iter()
.map(|x| {
let map = x.as_map().unwrap();
let insert = map.get("insert").unwrap().as_string().unwrap().to_string();
let attributes = map
.get("attributes")
.map(|v| v.as_map().unwrap().deref().clone());
TextDelta::Insert { insert, attributes }
})
.collect()
}
pub fn get_richtext_value(&self) -> LoroValue {
self.handler.get_richtext_value()
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.handler.to_string()
}
pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
self.handler.get_cursor(pos, side)
}
pub fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
pub fn convert_pos(
&self,
index: usize,
from: cursor::PosType,
to: cursor::PosType,
) -> Option<usize> {
self.handler.convert_pos(index, from, to)
}
pub fn push_str(&self, s: &str) -> LoroResult<()> {
self.handler.push_str(s)
}
pub fn get_editor_at_unicode_pos(&self, pos: usize) -> Option<PeerID> {
self.handler
.get_cursor(pos, Side::Middle)
.and_then(|x| x.id.map(|id| id.peer))
}
}
impl Default for LoroText {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct LoroTree {
handler: InnerTreeHandler,
}
impl SealedTrait for LoroTree {}
impl ContainerTrait for LoroTree {
type Handler = InnerTreeHandler;
fn id(&self) -> ContainerID {
self.handler.id()
}
fn to_container(&self) -> Container {
Container::Tree(self.clone())
}
fn to_handler(&self) -> Self::Handler {
self.handler.clone()
}
fn from_handler(handler: Self::Handler) -> Self {
Self { handler }
}
fn is_attached(&self) -> bool {
self.handler.is_attached()
}
fn get_attached(&self) -> Option<Self> {
self.handler.get_attached().map(Self::from_handler)
}
fn try_from_container(container: Container) -> Option<Self> {
container.into_tree().ok()
}
fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
fn doc(&self) -> Option<LoroDoc> {
self.handler.doc().map(|doc| {
doc.start_auto_commit();
LoroDoc::_new(doc)
})
}
}
#[derive(Debug, Clone)]
pub struct TreeNode {
pub id: TreeID,
pub parent: TreeParentId,
pub fractional_index: FractionalIndex,
pub index: usize,
}
impl LoroTree {
pub fn new() -> Self {
Self {
handler: InnerTreeHandler::new_detached(),
}
}
pub fn is_attached(&self) -> bool {
self.handler.is_attached()
}
pub fn create<T: Into<TreeParentId>>(&self, parent: T) -> LoroResult<TreeID> {
self.handler.create(parent.into())
}
pub fn roots(&self) -> Vec<TreeID> {
self.handler.roots()
}
pub fn create_at<T: Into<TreeParentId>>(&self, parent: T, index: usize) -> LoroResult<TreeID> {
if !self.handler.is_fractional_index_enabled() {
return Err(LoroTreeError::FractionalIndexNotEnabled.into());
}
self.handler.create_at(parent.into(), index)
}
pub fn mov<T: Into<TreeParentId>>(&self, target: TreeID, parent: T) -> LoroResult<()> {
self.handler.mov(target, parent.into())
}
pub fn mov_to<T: Into<TreeParentId>>(
&self,
target: TreeID,
parent: T,
to: usize,
) -> LoroResult<()> {
if !self.handler.is_fractional_index_enabled() {
return Err(LoroTreeError::FractionalIndexNotEnabled.into());
}
self.handler.move_to(target, parent.into(), to)
}
pub fn mov_after(&self, target: TreeID, after: TreeID) -> LoroResult<()> {
if !self.handler.is_fractional_index_enabled() {
return Err(LoroTreeError::FractionalIndexNotEnabled.into());
}
self.handler.mov_after(target, after)
}
pub fn mov_before(&self, target: TreeID, before: TreeID) -> LoroResult<()> {
if !self.handler.is_fractional_index_enabled() {
return Err(LoroTreeError::FractionalIndexNotEnabled.into());
}
self.handler.mov_before(target, before)
}
pub fn delete(&self, target: TreeID) -> LoroResult<()> {
self.handler.delete(target)
}
pub fn get_meta(&self, target: TreeID) -> LoroResult<LoroMap> {
self.handler
.get_meta(target)
.map(|h| LoroMap { handler: h })
}
pub fn parent(&self, target: TreeID) -> Option<TreeParentId> {
self.handler.get_node_parent(&target)
}
pub fn contains(&self, target: TreeID) -> bool {
self.handler.contains(target)
}
pub fn is_node_deleted(&self, target: &TreeID) -> LoroResult<bool> {
self.handler.is_node_deleted(target)
}
pub fn nodes(&self) -> Vec<TreeID> {
self.handler.nodes()
}
pub fn get_nodes(&self, with_deleted: bool) -> Vec<TreeNode> {
let mut ans = self.handler.get_nodes_under(TreeParentId::Root);
if with_deleted {
ans.extend(self.handler.get_nodes_under(TreeParentId::Deleted));
}
ans.into_iter()
.map(|x| TreeNode {
id: x.id,
parent: x.parent,
fractional_index: x.fractional_index,
index: x.index,
})
.collect()
}
pub fn children<T: Into<TreeParentId>>(&self, parent: T) -> Option<Vec<TreeID>> {
self.handler.children(&parent.into())
}
pub fn children_num<T: Into<TreeParentId>>(&self, parent: T) -> Option<usize> {
let parent: TreeParentId = parent.into();
self.handler.children_num(&parent)
}
pub fn fractional_index(&self, target: TreeID) -> Option<String> {
self.handler
.get_position_by_tree_id(&target)
.map(|x| x.to_string())
}
pub fn get_value(&self) -> LoroValue {
self.handler.get_value()
}
pub fn get_value_with_meta(&self) -> LoroValue {
self.handler.get_deep_value()
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn __internal__next_tree_id(&self) -> TreeID {
self.handler.__internal__next_tree_id()
}
pub fn is_fractional_index_enabled(&self) -> bool {
self.handler.is_fractional_index_enabled()
}
#[inline]
pub fn enable_fractional_index(&self, jitter: u8) {
self.handler.enable_fractional_index(jitter);
}
#[inline]
pub fn disable_fractional_index(&self) {
self.handler.disable_fractional_index();
}
#[inline]
pub fn is_empty(&self) -> bool {
self.handler.is_empty()
}
pub fn get_last_move_id(&self, target: &TreeID) -> Option<ID> {
self.handler.get_last_move_id(target)
}
}
impl Default for LoroTree {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct LoroMovableList {
handler: InnerMovableListHandler,
}
impl SealedTrait for LoroMovableList {}
impl ContainerTrait for LoroMovableList {
type Handler = InnerMovableListHandler;
fn id(&self) -> ContainerID {
self.handler.id()
}
fn to_container(&self) -> Container {
Container::MovableList(self.clone())
}
fn to_handler(&self) -> Self::Handler {
self.handler.clone()
}
fn from_handler(handler: Self::Handler) -> Self {
Self { handler }
}
fn try_from_container(container: Container) -> Option<Self>
where
Self: Sized,
{
match container {
Container::MovableList(x) => Some(x),
_ => None,
}
}
fn is_attached(&self) -> bool {
self.handler.is_attached()
}
fn get_attached(&self) -> Option<Self>
where
Self: Sized,
{
self.handler.get_attached().map(Self::from_handler)
}
fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
fn doc(&self) -> Option<LoroDoc> {
self.handler.doc().map(|doc| {
doc.start_auto_commit();
LoroDoc::_new(doc)
})
}
}
impl LoroMovableList {
pub fn new() -> LoroMovableList {
Self {
handler: InnerMovableListHandler::new_detached(),
}
}
pub fn is_attached(&self) -> bool {
self.handler.is_attached()
}
pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
self.handler.insert(pos, v)
}
pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
self.handler.delete(pos, len)
}
pub fn get(&self, index: usize) -> Option<ValueOrContainer> {
self.handler.get_(index).map(ValueOrContainer::from)
}
pub fn len(&self) -> usize {
self.handler.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get_value(&self) -> LoroValue {
self.handler.get_value()
}
pub fn get_deep_value(&self) -> LoroValue {
self.handler.get_deep_value()
}
pub fn pop(&self) -> LoroResult<Option<ValueOrContainer>> {
let ans = self.handler.pop_()?.map(ValueOrContainer::from);
Ok(ans)
}
pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
self.handler.push(v.into())
}
pub fn push_container<C: ContainerTrait>(&self, child: C) -> LoroResult<C> {
let pos = self.handler.len();
Ok(C::from_handler(
self.handler.insert_container(pos, child.to_handler())?,
))
}
pub fn set(&self, pos: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
self.handler.set(pos, value.into())
}
pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
self.handler.mov(from, to)
}
pub fn insert_container<C: ContainerTrait>(&self, pos: usize, child: C) -> LoroResult<C> {
Ok(C::from_handler(
self.handler.insert_container(pos, child.to_handler())?,
))
}
pub fn set_container<C: ContainerTrait>(&self, pos: usize, child: C) -> LoroResult<C> {
Ok(C::from_handler(
self.handler.set_container(pos, child.to_handler())?,
))
}
pub fn log_internal_state(&self) {
info!(
"movable_list internal state: {}",
self.handler.log_internal_state()
)
}
pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
self.handler.get_cursor(pos, side)
}
pub fn to_vec(&self) -> Vec<LoroValue> {
self.get_value().into_list().unwrap().unwrap()
}
pub fn clear(&self) -> LoroResult<()> {
self.handler.clear()
}
pub fn for_each<I>(&self, mut f: I)
where
I: FnMut(ValueOrContainer),
{
self.handler.for_each(&mut |v| {
f(ValueOrContainer::from(v));
})
}
pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
self.handler.get_creator_at(pos)
}
pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
self.handler.get_last_mover_at(pos)
}
pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
self.handler.get_last_editor_at(pos)
}
}
impl Default for LoroMovableList {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct LoroUnknown {
handler: InnerUnknownHandler,
}
impl SealedTrait for LoroUnknown {}
impl ContainerTrait for LoroUnknown {
type Handler = InnerUnknownHandler;
fn id(&self) -> ContainerID {
self.handler.id()
}
fn to_container(&self) -> Container {
Container::Unknown(self.clone())
}
fn to_handler(&self) -> Self::Handler {
self.handler.clone()
}
fn from_handler(handler: Self::Handler) -> Self {
Self { handler }
}
fn try_from_container(container: Container) -> Option<Self>
where
Self: Sized,
{
match container {
Container::Unknown(x) => Some(x),
_ => None,
}
}
fn is_attached(&self) -> bool {
self.handler.is_attached()
}
fn get_attached(&self) -> Option<Self>
where
Self: Sized,
{
self.handler.get_attached().map(Self::from_handler)
}
fn is_deleted(&self) -> bool {
self.handler.is_deleted()
}
fn doc(&self) -> Option<LoroDoc> {
self.handler.doc().map(|doc| {
doc.start_auto_commit();
LoroDoc::_new(doc)
})
}
}
use enum_as_inner::EnumAsInner;
#[cfg(feature = "jsonpath")]
use loro_internal::jsonpath::jsonpath_impl::JsonPathError;
#[derive(Clone, Debug, EnumAsInner)]
pub enum Container {
List(LoroList),
Map(LoroMap),
Text(LoroText),
Tree(LoroTree),
MovableList(LoroMovableList),
#[cfg(feature = "counter")]
Counter(counter::LoroCounter),
Unknown(LoroUnknown),
}
impl SealedTrait for Container {}
impl ContainerTrait for Container {
type Handler = loro_internal::handler::Handler;
fn id(&self) -> ContainerID {
match self {
Container::List(x) => x.id(),
Container::Map(x) => x.id(),
Container::Text(x) => x.id(),
Container::Tree(x) => x.id(),
Container::MovableList(x) => x.id(),
#[cfg(feature = "counter")]
Container::Counter(x) => x.id(),
Container::Unknown(x) => x.id(),
}
}
fn to_container(&self) -> Container {
self.clone()
}
fn to_handler(&self) -> Self::Handler {
match self {
Container::List(x) => Self::Handler::List(x.to_handler()),
Container::Map(x) => Self::Handler::Map(x.to_handler()),
Container::Text(x) => Self::Handler::Text(x.to_handler()),
Container::Tree(x) => Self::Handler::Tree(x.to_handler()),
Container::MovableList(x) => Self::Handler::MovableList(x.to_handler()),
#[cfg(feature = "counter")]
Container::Counter(x) => Self::Handler::Counter(x.to_handler()),
Container::Unknown(x) => Self::Handler::Unknown(x.to_handler()),
}
}
fn from_handler(handler: Self::Handler) -> Self {
match handler {
InnerHandler::Text(x) => Container::Text(LoroText { handler: x }),
InnerHandler::Map(x) => Container::Map(LoroMap { handler: x }),
InnerHandler::List(x) => Container::List(LoroList { handler: x }),
InnerHandler::MovableList(x) => Container::MovableList(LoroMovableList { handler: x }),
InnerHandler::Tree(x) => Container::Tree(LoroTree { handler: x }),
#[cfg(feature = "counter")]
InnerHandler::Counter(x) => Container::Counter(counter::LoroCounter { handler: x }),
InnerHandler::Unknown(x) => Container::Unknown(LoroUnknown { handler: x }),
}
}
fn is_attached(&self) -> bool {
match self {
Container::List(x) => x.is_attached(),
Container::Map(x) => x.is_attached(),
Container::Text(x) => x.is_attached(),
Container::Tree(x) => x.is_attached(),
Container::MovableList(x) => x.is_attached(),
#[cfg(feature = "counter")]
Container::Counter(x) => x.is_attached(),
Container::Unknown(x) => x.is_attached(),
}
}
fn get_attached(&self) -> Option<Self> {
match self {
Container::List(x) => x.get_attached().map(Container::List),
Container::MovableList(x) => x.get_attached().map(Container::MovableList),
Container::Map(x) => x.get_attached().map(Container::Map),
Container::Text(x) => x.get_attached().map(Container::Text),
Container::Tree(x) => x.get_attached().map(Container::Tree),
#[cfg(feature = "counter")]
Container::Counter(x) => x.get_attached().map(Container::Counter),
Container::Unknown(x) => x.get_attached().map(Container::Unknown),
}
}
fn try_from_container(container: Container) -> Option<Self>
where
Self: Sized,
{
Some(container)
}
fn is_deleted(&self) -> bool {
match self {
Container::List(x) => x.is_deleted(),
Container::Map(x) => x.is_deleted(),
Container::Text(x) => x.is_deleted(),
Container::Tree(x) => x.is_deleted(),
Container::MovableList(x) => x.is_deleted(),
#[cfg(feature = "counter")]
Container::Counter(x) => x.is_deleted(),
Container::Unknown(x) => x.is_deleted(),
}
}
fn doc(&self) -> Option<LoroDoc> {
match self {
Container::List(x) => x.doc(),
Container::Map(x) => x.doc(),
Container::Text(x) => x.doc(),
Container::Tree(x) => x.doc(),
Container::MovableList(x) => x.doc(),
#[cfg(feature = "counter")]
Container::Counter(x) => x.doc(),
Container::Unknown(x) => x.doc(),
}
}
}
impl Container {
pub fn new(kind: ContainerType) -> Self {
match kind {
ContainerType::List => Container::List(LoroList::new()),
ContainerType::MovableList => Container::MovableList(LoroMovableList::new()),
ContainerType::Map => Container::Map(LoroMap::new()),
ContainerType::Text => Container::Text(LoroText::new()),
ContainerType::Tree => Container::Tree(LoroTree::new()),
#[cfg(feature = "counter")]
ContainerType::Counter => Container::Counter(counter::LoroCounter::new()),
ContainerType::Unknown(_) => unreachable!(),
}
}
pub fn get_type(&self) -> ContainerType {
match self {
Container::List(_) => ContainerType::List,
Container::MovableList(_) => ContainerType::MovableList,
Container::Map(_) => ContainerType::Map,
Container::Text(_) => ContainerType::Text,
Container::Tree(_) => ContainerType::Tree,
#[cfg(feature = "counter")]
Container::Counter(_) => ContainerType::Counter,
Container::Unknown(x) => x.handler.id().container_type(),
}
}
}
impl From<InnerHandler> for Container {
fn from(value: InnerHandler) -> Self {
match value {
InnerHandler::Text(x) => Container::Text(LoroText { handler: x }),
InnerHandler::Map(x) => Container::Map(LoroMap { handler: x }),
InnerHandler::List(x) => Container::List(LoroList { handler: x }),
InnerHandler::Tree(x) => Container::Tree(LoroTree { handler: x }),
InnerHandler::MovableList(x) => Container::MovableList(LoroMovableList { handler: x }),
#[cfg(feature = "counter")]
InnerHandler::Counter(x) => Container::Counter(counter::LoroCounter { handler: x }),
InnerHandler::Unknown(x) => Container::Unknown(LoroUnknown { handler: x }),
}
}
}
#[derive(Debug, Clone, EnumAsInner)]
pub enum ValueOrContainer {
Value(LoroValue),
Container(Container),
}
impl ValueOrContainer {
pub fn get_deep_value(&self) -> LoroValue {
match self {
ValueOrContainer::Value(v) => v.clone(),
ValueOrContainer::Container(c) => match c {
Container::List(c) => c.get_deep_value(),
Container::Map(c) => c.get_deep_value(),
Container::Text(c) => c.to_string().into(),
Container::Tree(c) => c.get_value(),
Container::MovableList(c) => c.get_deep_value(),
#[cfg(feature = "counter")]
Container::Counter(c) => c.get_value().into(),
Container::Unknown(_) => LoroValue::Null,
},
}
}
pub(crate) fn into_value_or_handler(self) -> ValueOrHandler {
match self {
ValueOrContainer::Value(v) => ValueOrHandler::Value(v),
ValueOrContainer::Container(c) => ValueOrHandler::Handler(c.to_handler()),
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct UndoManager(InnerUndoManager);
impl UndoManager {
pub fn new(doc: &LoroDoc) -> Self {
let inner = InnerUndoManager::new(&doc.doc);
inner.set_max_undo_steps(100);
Self(inner)
}
pub fn undo(&mut self) -> LoroResult<bool> {
self.0.undo()
}
pub fn redo(&mut self) -> LoroResult<bool> {
self.0.redo()
}
pub fn record_new_checkpoint(&mut self) -> LoroResult<()> {
self.0.record_new_checkpoint()
}
pub fn can_undo(&self) -> bool {
self.0.can_undo()
}
pub fn can_redo(&self) -> bool {
self.0.can_redo()
}
pub fn undo_count(&self) -> usize {
self.0.undo_count()
}
pub fn redo_count(&self) -> usize {
self.0.redo_count()
}
pub fn add_exclude_origin_prefix(&mut self, prefix: &str) {
self.0.add_exclude_origin_prefix(prefix)
}
pub fn set_max_undo_steps(&mut self, size: usize) {
self.0.set_max_undo_steps(size)
}
pub fn set_merge_interval(&mut self, interval: i64) {
self.0.set_merge_interval(interval)
}
pub fn set_on_push(&mut self, on_push: Option<OnPush>) {
if let Some(on_push) = on_push {
self.0.set_on_push(Some(Box::new(move |u, c, e| {
on_push(u, c, e.map(|x| x.into()))
})));
} else {
self.0.set_on_push(None);
}
}
pub fn set_on_pop(&mut self, on_pop: Option<OnPop>) {
self.0.set_on_pop(on_pop);
}
pub fn clear(&self) {
self.0.clear();
}
pub fn group_start(&mut self) -> LoroResult<()> {
self.0.group_start()
}
pub fn group_end(&mut self) {
self.0.group_end();
}
pub fn peer(&self) -> PeerID {
self.0.peer()
}
pub fn top_undo_meta(&self) -> Option<UndoItemMeta> {
self.0.top_undo_meta()
}
pub fn top_redo_meta(&self) -> Option<UndoItemMeta> {
self.0.top_redo_meta()
}
pub fn top_undo_value(&self) -> Option<LoroValue> {
self.0.top_undo_value()
}
pub fn top_redo_value(&self) -> Option<LoroValue> {
self.0.top_redo_value()
}
}
pub type OnPush =
Box<dyn for<'a> Fn(UndoOrRedo, CounterSpan, Option<DiffEvent>) -> UndoItemMeta + Send + Sync>;