pub struct TextOperation { /* private fields */ }Expand description
ops
本质上是 [op] 类型, 定义了如何将一个字符串转换为另一个字符串的 op 序列。
注意:当 baseLength == base.len() 时,说明虚拟游标移动到该文本的尾部,由于 原子操作 Operation 定义的操作,
游标只能向后移动,所以此时,若需要进行 Retain 或者 Delete,则需要创建一个新的 TextOperation
§Example
针对字符串 abc,在 用户删除了 b,并在 c 会后插入了 d。
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
ops.retain(1).delete(1).retain(1).insert("d");
let base = "abc";
let after = "acd";
assert_eq!(after, ops.apply(base).unwrap());Implementations§
Source§impl TextOperation
impl TextOperation
Sourcepub fn new() -> TextOperation
pub fn new() -> TextOperation
构造函数,创建一个无操作的 TextOperation
§Example
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
assert_eq!("(0->0){}", ops.to_string());Sourcepub fn retain(&mut self, n: usize) -> &mut TextOperation
pub fn retain(&mut self, n: usize) -> &mut TextOperation
跳过给定数量的字符
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
ops.retain(1);
assert_eq!("(1->1){retain(1)}", ops.to_string());
ops.retain(1);
assert_eq!("(2->2){retain(2)}", ops.to_string());Sourcepub fn insert<T: Into<String>>(&mut self, str: T) -> &mut TextOperation
pub fn insert<T: Into<String>>(&mut self, str: T) -> &mut TextOperation
在当前位置插入一个字符串
§Example
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
ops.insert("a");
assert_eq!("(0->1){insert(\"a\")}", ops.to_string());
ops.insert("b");
// 两次连续的插入将合并
assert_eq!("(0->2){insert(\"ab\")}", ops.to_string());
ops.delete(1);
assert_eq!("(1->2){insert(\"ab\").delete(1)}", ops.to_string());
// I,D + I 将加入的 I 合并到前面的插入
ops.insert("c");
assert_eq!("(1->3){insert(\"abc\").delete(1)}", ops.to_string());
ops.retain(1).delete(1);
assert_eq!(
"(3->4){insert(\"abc\").delete(1).retain(1).delete(1)}",
ops.to_string()
);
// D + I 将变为 I,D
ops.insert("d");
assert_eq!(
"(3->5){insert(\"abc\").delete(1).retain(1).insert(\"d\").delete(1)}",
ops.to_string()
);Sourcepub fn delete(&mut self, n: usize) -> &mut TextOperation
pub fn delete(&mut self, n: usize) -> &mut TextOperation
删除当前位置的字符串
§Example
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
ops.delete(1);
assert_eq!("(1->0){delete(1)}", ops.to_string());
ops.delete(2);
assert_eq!("(3->0){delete(3)}", ops.to_string());Sourcepub fn is_noop(&self) -> bool
pub fn is_noop(&self) -> bool
测试该操作 apply 后是否不产生影响
§Example
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
assert!(ops.is_noop());
ops.retain(10);
assert!(ops.is_noop());Sourcepub fn apply<T: Into<String>>(&self, base: T) -> Result<String, OperationError>
pub fn apply<T: Into<String>>(&self, base: T) -> Result<String, OperationError>
将 操作 apply 应用到 base 字符串中,并返回一个新字符串; 如果输入的字符串和操作之间不匹配,抛出一个错误。
§Example
use ot_rs::core::{OperationError, TextOperation};
// 正常情况
let mut ops = TextOperation::new();
ops.retain(1).delete(1).retain(1).insert("d");
let base = "abc";
let after = "acd";
assert_eq!(after, ops.apply(base).unwrap());
// 异常情况
let mut ops = TextOperation::new();
assert_eq!(
OperationError::OperationApplyStringNotCompatible,
ops.insert("a").apply("---").unwrap_err()
);Sourcepub fn invert<T: Into<String>>(
&self,
base: T,
) -> Result<TextOperation, OperationError>
pub fn invert<T: Into<String>>( &self, base: T, ) -> Result<TextOperation, OperationError>
生成 该 Operation 的 逆操作,即求 ops’ 且满足 apply(apply(s, ops), ops') = s。可以用来实现 undo
§Example
use ot_rs::core::TextOperation;
let base = "abc";
let mut ops = TextOperation::new();
ops.retain(1).delete(1).retain(1).insert("d");
assert_eq!(
base,
ops.invert(base)
.unwrap()
.apply(ops.apply(base).unwrap())
.unwrap()
);Sourcepub fn compose(
&self,
operation2: &TextOperation,
) -> Result<TextOperation, OperationError>
pub fn compose( &self, operation2: &TextOperation, ) -> Result<TextOperation, OperationError>
合并连续的两个 文本操作,满足 apply(apply(S, A), B) = apply(S, compose(A, B))
§Example
use ot_rs::core::TextOperation;
let base = "abc";
let mut ops1 = TextOperation::new();
ops1.retain(1).insert("123").delete(1).retain(1);
let after1 = ops1.apply(base).unwrap();
assert_eq!("a123c", after1);
let mut ops2 = TextOperation::new();
ops2.retain(2)
.insert("$$$")
.delete(1)
.retain(1)
.insert("###")
.retain(1);
let after2 = ops2.apply(&after1).unwrap();
assert_eq!("a1$$$3###c", after2);
let compose_ops = ops1.compose(&ops2).unwrap();
assert_eq!(after2, compose_ops.apply(base).unwrap());Sourcepub fn should_be_composed_with(&self, other: &TextOperation) -> bool
pub fn should_be_composed_with(&self, other: &TextOperation) -> bool
当使用 ctrl-z 撤消最近的更改时,希望程序不会撤消每一次击键,而是撤消一口气写下的最后一句话或通过按住退格键所做的删除。
这可以通过在将撤消栈上的操作进行 compose 来实现。 这个方法可以帮助决定是否应该组合两个操作。
如果操作是 连续的插入操作 或 连续的删除操作,则返回 true。
可能希望包括其他因素,例如自上次更改决定以来的时间。
§Example
use ot_rs::core::TextOperation;
let mut ops1: TextOperation;
let mut ops2: TextOperation;
// noop;I / I;noop
ops1 = TextOperation::new();
ops1.retain(3);
ops2 = TextOperation::new();
ops2.retain(1).insert("xxx").retain(2);
assert!(ops1.should_be_composed_with(&ops2));
assert!(ops2.should_be_composed_with(&ops1));
// I;I 正常输入
ops1 = TextOperation::new();
ops1.retain(1).insert("a").retain(2);
ops2 = TextOperation::new();
ops2.retain(2).insert("b").retain(2);
assert!(ops1.should_be_composed_with(&ops2));
ops1.delete(3);
assert!(!ops1.should_be_composed_with(&ops2));
// I;I 插入后光标发生变化
ops1 = TextOperation::new();
ops1.retain(1).insert("b").retain(2);
ops2 = TextOperation::new();
ops2.retain(1).insert("a").retain(3);
assert!(!ops1.should_be_composed_with(&ops2));
// D;D 退格键方式
ops1 = TextOperation::new();
ops1.retain(4).delete(3).retain(10);
ops2 = TextOperation::new();
ops2.retain(2).delete(2).retain(10);
assert!(ops1.should_be_composed_with(&ops2));
// D;D delete键方式
ops2 = TextOperation::new();
ops2.retain(4).delete(7).retain(3);
assert!(ops1.should_be_composed_with(&ops2));
// D;D 不连续的删除
ops2 = TextOperation::new();
ops2.retain(2).delete(9).retain(3);
assert!(!ops1.should_be_composed_with(&ops2));Sourcepub fn should_be_composed_with_inverted(&self, other: &TextOperation) -> bool
pub fn should_be_composed_with_inverted(&self, other: &TextOperation) -> bool
决定两个操作如果被 invert 是否应该相互组合,即 should_be_composed_with_inverted(a, b) = should_be_composed_with_inverted(b^{-1}, a^{-1})
Sourcepub fn transform(
&self,
operation2: &TextOperation,
) -> Result<(TextOperation, TextOperation), OperationError>
pub fn transform( &self, operation2: &TextOperation, ) -> Result<(TextOperation, TextOperation), OperationError>
这个函数是 OT 算法的核心。
转换两个基于同一版本 S 的操作 A 和 B,返回 A’ 和 B’,使其满足
apply(apply(S, A), B') = apply(apply(S, B), A')。
Trait Implementations§
Source§impl Debug for TextOperation
impl Debug for TextOperation
Source§impl Default for TextOperation
impl Default for TextOperation
Source§impl PartialEq for TextOperation
impl PartialEq for TextOperation
Source§fn eq(&self, other: &Self) -> bool
fn eq(&self, other: &Self) -> bool
§Example
use ot_rs::core::TextOperation;
let mut ops1 = TextOperation::new();
let mut ops2 = TextOperation::new();
assert!(ops1 == ops2);
ops1.retain(1).delete(1).retain(1).insert("d");
assert!(ops1 != ops2);
ops2.retain(1).delete(1).retain(1).insert("d");
assert!(ops1 == ops2);
ops2.insert("1");
assert!(ops1 != ops2);