pub struct Content {
pub text: String,
pub lines: Vec<Line>,
pub marks: Vec<Mark>,
pub islands: Vec<Island>,
}Expand description
The canonical content model, the pre-built value the document mutators accept, and the canonical-form token every content read answers in. One content field as a content: the text plus the structure that rides on it.
The mint (Content::into_normalized) establishes the canonical form:
marks sorted and unioned, container paths renumbered, a line’s kind agreeing
with its text, a block island’s slot alone on its line, table props on one
column count. Content::validate reports what the mint cannot repair: the
text holds no \r, no bidi controls, and no line separator (every character
Typst reads as a newline but \n); the count of ISLAND_SLOT equals
islands.len(); lines.len() equals the number of \n-separated segments.
Fields§
§text: StringThe content. \n is a line boundary; ISLAND_SLOT is an island slot.
lines: Vec<Line>One entry per \n-separated segment of text, in order. The line tree
is derived from this flat list plus each line’s containers path,
never stored, so a split/join is a single-char edit with no paragraph
identity to reconcile.
marks: Vec<Mark>Marks over char ranges, kept normalized: sorted by
(start, end, type, attrs), same-kind formatting marks unioned.
islands: Vec<Island>One entry per ISLAND_SLOT, in slot order (ascending char position).
Implementations§
Source§impl Content
impl Content
Sourcepub fn new(text: String, lines: Vec<Line>) -> Content
pub fn new(text: String, lines: Vec<Line>) -> Content
The text and its per-line attributes; marks and islands start empty.
Constructing neither normalizes nor checks: the canonical form is the
caller’s until into_normalized runs, and
validate reports what that cannot repair. The codecs
(crate::import, Content::from_canonical_json) do both.
Sourcepub fn into_normalized(self) -> Normalized
pub fn into_normalized(self) -> Normalized
Normalize and seal. With Normalized::empty, the only mint for
Normalized; the codecs decode through here.
pub fn with_marks(self, marks: Vec<Mark>) -> Content
Sourcepub fn with_islands(self, islands: Vec<Island>) -> Content
pub fn with_islands(self, islands: Vec<Island>) -> Content
Set the islands, one per ISLAND_SLOT in slot order.
Sourcepub fn is_inline(&self) -> bool
pub fn is_inline(&self) -> bool
Whether this content satisfies the richtext(inline) constraint: exactly
one Para line, sitting in no container, with no islands.
Content::empty is inline, so a blank inline field passes.
Sourcepub fn is_plain(&self) -> bool
pub fn is_plain(&self) -> bool
Whether this content satisfies the plaintext constraint: no marks, no
islands, and every line a plain Para sitting in no container.
continues is unconstrained. Content::empty is plain.
The distinguishing property of plaintext over richtext { marks: [] } is
the literal codec (crate::import::from_plaintext), not this
predicate.
Sourcepub fn is_blank(&self) -> bool
pub fn is_blank(&self) -> bool
Whether the text is empty or whitespace-only. An ISLAND_SLOT is not
whitespace, so an island-bearing content is never blank.
Sourcepub fn segment_count(&self) -> usize
pub fn segment_count(&self) -> usize
Number of \n-separated segments: the required lines.len().
Sourcepub fn normalize(&mut self)
pub fn normalize(&mut self)
Normalize in place: canonicalize container ordinal/instance, break a
line around a block-only island’s slot, drop zero-width formatting, union
same-kind formatting that is adjacent or overlapping, recursively
key-sort island props, then sort marks canonically. Idempotent: the
fixed point the canonical serialization commits to.
Source§impl Content
impl Content
Sourcepub fn apply_text_delta(&mut self, delta: &Delta) -> Result<(), ApplyError>
pub fn apply_text_delta(&mut self, delta: &Delta) -> Result<(), ApplyError>
Splice text via delta, rebase marks, sync lines to \n changes,
cascade island removal for any deleted slot, then normalize.
Islands stay in lockstep with their ISLAND_SLOT chars: a delta that
deletes a slot drops the corresponding Island; a delta that
inserts a raw slot is rejected (ApplyError::IslandSlotInInsert).
Inserted text is sanitized first, mirroring what import applies at the
string boundary: \r and Unicode bidi controls are stripped, and a line
separator (VT, FF, NEL, U+2028, U+2029) becomes a space — the chars
Content::validate forbids.
Sourcepub fn apply_mark_ops(&mut self, ops: &[MarkOp]) -> Result<(), ApplyError>
pub fn apply_mark_ops(&mut self, ops: &[MarkOp]) -> Result<(), ApplyError>
Apply mark ops in final-text coordinates, then normalize.
Sourcepub fn apply_island_ops(&mut self, ops: &[IslandOp]) -> Result<(), ApplyError>
pub fn apply_island_ops(&mut self, ops: &[IslandOp]) -> Result<(), ApplyError>
Apply island ops: replace an entry by id, or insert a slot and its entry together.
Sourcepub fn apply_line_ops(&mut self, ops: &[LineOp]) -> Result<(), ApplyError>
pub fn apply_line_ops(&mut self, ops: &[LineOp]) -> Result<(), ApplyError>
Apply line ops: split/join splice \n; set ops touch metadata only.
Sourcepub fn apply_field_change(
&mut self,
bundle: &ChangeBundle,
) -> Result<(), ApplyError>
pub fn apply_field_change( &mut self, bundle: &ChangeBundle, ) -> Result<(), ApplyError>
One committed field edit bundle: text delta, then island ops, then line
ops, then marks, canonicalized by a single terminal
normalize.
All-or-nothing: on any op’s error self is left exactly as it was. A
bundle carrying ops stages on a scratch copy and swaps in only once every
stage succeeds; the pure-text-delta path skips that clone, since
apply_text_delta validates before mutating.
Stage order is a coordinate contract: each stage reads the text the
earlier ones left. An island insert splices a slot, so a
LineOp::SetKind { kind: Island } in the same bundle settles against a
line that already carries it, and Split/Join and every mark range are
then measured in a frame that includes the new slots.
One terminal normalize suffices because split/join rebase marks through
their \n splice, so the formatting-edge \n-trim commutes with the
line ops, and MarkOp::Remove is coverage-set subtraction, which
commutes with normalize’s same-kind union ((A ∪ B) \ R = (A\R) ∪ (B\R)).
Sourcepub fn map_marks(&self, bundle: &ChangeBundle) -> Result<Vec<Mark>, ApplyError>
pub fn map_marks(&self, bundle: &ChangeBundle) -> Result<Vec<Mark>, ApplyError>
Where bundle’s text-moving channels leave the marks this content
already holds: the final-text coordinates ChangeBundle::mark_ops are
written in, under the rebase rule stated on ChangeBundle.
bundle.mark_ops are ignored, so an editor building them diffs its
intended marks against this instead of predicting the rebase. The answer
is normalized, as the store’s is: marks a text move
drops (out of range, zero-width formatting) are absent, and same-kind
runs a move left adjacent arrive already unioned. A bundle whose
mark_ops are empty therefore names the marks the field will hold.
Errors are apply_field_change’s on the same
ops, minus those only a mark op raises.
Source§impl Content
impl Content
Sourcepub fn from_canonical_json(s: &str) -> Result<Normalized, ParseError>
pub fn from_canonical_json(s: &str) -> Result<Normalized, ParseError>
Parse canonical JSON, normalize, and validate. Returns
ParseError::Invalid for a content that violates its invariants, so
storage cannot silently round-trip a malformed value.
Trait Implementations§
Source§impl From<Content> for Normalized
impl From<Content> for Normalized
Source§fn from(rt: Content) -> Normalized
fn from(rt: Content) -> Normalized
impl StructuralPartialEq for Content
Auto Trait Implementations§
impl Freeze for Content
impl RefUnwindSafe for Content
impl Send for Content
impl Sync for Content
impl Unpin for Content
impl UnsafeUnpin for Content
impl UnwindSafe for Content
Blanket Implementations§
Source§impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
Source§fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
Source§fn adapt_into(self) -> D
fn adapt_into(self) -> D
Source§impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
Source§fn arrays_from(colors: C) -> T
fn arrays_from(colors: C) -> T
Source§impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
Source§fn arrays_into(self) -> C
fn arrays_into(self) -> C
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
Source§type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
parameters when converting.Source§fn cam16_into_unclamped(
self,
parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>,
) -> T
fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T
self into C, using the provided parameters.Source§impl<T> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
Source§fn components_from(colors: C) -> T
fn components_from(colors: C) -> T
Source§impl<T, C> ComponentsInto<C> for T
impl<T, C> ComponentsInto<C> for T
Source§fn components_into(self) -> C
fn components_into(self) -> C
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> FromAngle<T> for T
impl<T> FromAngle<T> for T
Source§fn from_angle(angle: T) -> T
fn from_angle(angle: T) -> T
angle.Source§impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
Source§fn from_stimulus(other: U) -> T
fn from_stimulus(other: U) -> T
other into Self, while performing the appropriate scaling,
rounding and clamping.Source§impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
Source§fn into_angle(self) -> U
fn into_angle(self) -> U
T.Source§impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
Source§type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
parameters when converting.Source§fn into_cam16_unclamped(
self,
parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>,
) -> T
fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T
self into C, using the provided parameters.Source§impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
Source§fn into_color(self) -> U
fn into_color(self) -> U
Source§impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
Source§fn into_color_unclamped(self) -> U
fn into_color_unclamped(self) -> U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoStimulus<T> for T
impl<T> IntoStimulus<T> for T
Source§fn into_stimulus(self) -> T
fn into_stimulus(self) -> T
self into T, while performing the appropriate scaling,
rounding and clamping.Source§impl<T> OverflowingAs for T
impl<T> OverflowingAs for T
Source§fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
Source§impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
Source§fn overflowing_cast_from(src: Src) -> (Dst, bool)
fn overflowing_cast_from(src: Src) -> (Dst, bool)
Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> SaturatingAs for T
impl<T> SaturatingAs for T
Source§fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
Source§impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
Source§fn saturating_cast_from(src: Src) -> Dst
fn saturating_cast_from(src: Src) -> Dst
Source§impl<T> StrictAs for T
impl<T> StrictAs for T
Source§fn strict_as<Dst>(self) -> Dstwhere
T: StrictCast<Dst>,
fn strict_as<Dst>(self) -> Dstwhere
T: StrictCast<Dst>,
Source§impl<Src, Dst> StrictCastFrom<Src> for Dstwhere
Src: StrictCast<Dst>,
impl<Src, Dst> StrictCastFrom<Src> for Dstwhere
Src: StrictCast<Dst>,
Source§fn strict_cast_from(src: Src) -> Dst
fn strict_cast_from(src: Src) -> Dst
Source§impl<U, T> ToOwnedObj<U> for Twhere
U: FromObjRef<T>,
impl<U, T> ToOwnedObj<U> for Twhere
U: FromObjRef<T>,
Source§fn to_owned_obj(&self, data: FontData<'_>) -> U
fn to_owned_obj(&self, data: FontData<'_>) -> U
T, using the provided data to resolve any offsets.Source§impl<U, T> ToOwnedTable<U> for Twhere
U: FromTableRef<T>,
impl<U, T> ToOwnedTable<U> for Twhere
U: FromTableRef<T>,
fn to_owned_table(&self) -> U
Source§impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
Source§type Error = <C as TryFromComponents<T>>::Error
type Error = <C as TryFromComponents<T>>::Error
try_into_colors fails to cast.Source§fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
Source§impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
Source§fn try_into_color(self) -> Result<U, OutOfBounds<U>>
fn try_into_color(self) -> Result<U, OutOfBounds<U>>
OutOfBounds error is returned which contains
the unclamped color. Read more