Skip to main content

TextOperation

Struct TextOperation 

Source
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

Source

pub fn new() -> TextOperation

构造函数,创建一个无操作的 TextOperation

§Example
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
assert_eq!("(0->0){}", ops.to_string());
Source

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());
Source

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()
);
Source

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());
Source

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());
Source

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()
);
Source

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()
);
Source

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());
Source

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));
Source

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})

Source

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

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TextOperation

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl PartialEq for TextOperation

Source§

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);
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl ToString for TextOperation

Source§

fn to_string(&self) -> String

§Example
use ot_rs::core::TextOperation;
let mut ops = TextOperation::new();
assert_eq!("(0->0){}", ops.to_string());
ops.retain(1).delete(1).retain(1).insert("de\"");
assert_eq!(
    "(3->5){retain(1).delete(1).retain(1).insert(\"de\\\"\")}",
    ops.to_string()
);

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.