///|
/// A Quill Delta operation. Text positions are UTF-16 code-unit offsets.
/// Attributes are JSON objects; an empty object means no formatting change.
pub(all) enum Op {
InsertText(String, Json)
InsertEmbed(Json, Json)
Retain(Int32, Json)
RetainEmbed(Json, Json)
Delete(Int32)
} derive(Eq, Debug)
///|
/// A sequence of rich-text editing operations. Builders normalize adjacent
/// operations, put inserts before deletes at the same position, and discard
/// operations with zero length.
pub(all) struct Delta {
mut ops : Array[Op]
} derive(Eq, Debug)
///|
/// Validation errors include the path in the source JSON whenever possible.
pub suberror DeltaError {
Invalid(String)
} derive(Debug)
///|
pub fn empty_attributes() -> Json {
Json::object({})
}
///|
fn attributes_empty(attributes : Json) -> Bool {
match attributes {
Object(map) => map.is_empty()
_ => false
}
}
///|
fn no_attributes(attributes : Json) -> Bool {
attributes_empty(attributes)
}
///|
pub fn Op::length(self : Op) -> Int32 {
match self {
InsertText(text, _) => text.length()
InsertEmbed(_, _) | RetainEmbed(_, _) => 1
Retain(n, _) | Delete(n) => n
}
}
///|
pub fn Op::attributes(self : Op) -> Json {
match self {
InsertText(_, attrs)
| InsertEmbed(_, attrs)
| Retain(_, attrs)
| RetainEmbed(_, attrs) => attrs
Delete(_) => empty_attributes()
}
}
///|
pub fn Op::kind(self : Op) -> String {
match self {
InsertText(_, _) | InsertEmbed(_, _) => "insert"
Retain(_, _) | RetainEmbed(_, _) => "retain"
Delete(_) => "delete"
}
}
///|
pub fn Delta::new() -> Delta {
{ ops: [], }
}
///|
pub fn Delta::from_ops(ops : Array[Op]) -> Delta {
let delta = Delta::new()
for op in ops {
if op.length() <= 0 {
continue
}
ignore(delta.push(op))
}
delta
}
///|
/// Construct a Delta from operations without normalizing their order or
/// merging adjacent operations, matching Quill Delta's array constructor.
/// Callers must supply well-formed operations. The array is shared, as in
/// the JavaScript constructor.
pub fn Delta::from_ops_raw(operations : Array[Op]) -> Delta {
{ ops: operations, }
}
///|
pub fn Delta::operations(self : Delta) -> Array[Op] {
let copy : Array[Op] = []
for op in self.ops {
copy.push(op)
}
copy
}
///|
pub fn Delta::count(self : Delta) -> Int32 {
self.ops.length()
}
///|
pub fn Delta::is_empty(self : Delta) -> Bool {
self.ops.is_empty()
}
///|
pub fn Delta::length(self : Delta) -> Int32 {
let mut total = 0
for op in self.ops {
total += op.length()
}
total
}
///|
pub fn Delta::change_length(self : Delta) -> Int32 {
let mut total = 0
for op in self.ops {
match op {
InsertText(_, _) | InsertEmbed(_, _) => total += op.length()
Delete(n) => total -= n
Retain(_, _) | RetainEmbed(_, _) => ()
}
}
total
}
///|
pub fn Delta::is_document(self : Delta) -> Bool {
for op in self.ops {
match op {
InsertText(_, _) | InsertEmbed(_, _) => ()
_ => return false
}
}
true
}
///|
pub fn Delta::plain_text(self : Delta) -> String? {
let builder = StringBuilder()
for op in self.ops {
match op {
InsertText(text, _) => builder.write_string(text)
_ => return None
}
}
Some(builder.to_string())
}
///|
pub extend Op with Eq::{equal, not_equal}
///|
pub extend Op with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend Delta with Eq::{equal, not_equal}
///|
pub extend Delta with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend DeltaError with @moonbitlang/core/debug.Debug::{to_repr}