pub struct LeafDoc { /* private fields */ }Expand description
A live leaf document bound for a native Apple frontend: leaf_core::Doc plus
the wrap width the current viewport implies, behind a mutex. Constructed from
an in-memory string and driven entirely through method calls — there is no
filesystem behind it.
Implementations§
Source§impl LeafDoc
impl LeafDoc
Sourcepub fn new(source: String, format: String) -> Result<Arc<Self>, LeafError>
pub fn new(source: String, format: String) -> Result<Arc<Self>, LeafError>
Parse source as format ("markdown"/"md", "djot"/"dj",
"html", "xml") into a live, untitled document.
Sourcepub fn view(&self) -> DocView
pub fn view(&self) -> DocView
Resolve the current document to a renderable frame — the first paint.
Sourcepub fn set_width(&self, cols: u32) -> DocView
pub fn set_width(&self, cols: u32) -> DocView
Set the wrap width (in columns) the viewport implies and repaint. For a
fixed-cell frontend (a terminal); a proportional GUI uses [set_unwrapped].
Sourcepub fn set_unwrapped(&self) -> DocView
pub fn set_unwrapped(&self) -> DocView
Switch to unwrapped layout — one visual row per block, no column wrapping — and repaint. A proportional GUI calls this once at start-up, then wraps each row at its own pixel width (the caret/hit/selection geometry it derives from the pixel wrap; core still owns the caret model, in byte offsets). Idempotent and cheap to leave in place across edits.
Sourcepub fn set_dark_appearance(&self, dark: bool) -> DocView
pub fn set_dark_appearance(&self, dark: bool) -> DocView
Tell core whether the host is in a dark appearance, so a <picture>’s
prefers-color-scheme <source>s resolve to the right banner. Call it
from viewDidChangeEffectiveAppearance (AppKit) or
traitCollectionDidChange (UIKit).
Cheap to call repeatedly: resolving at the same appearance yields the same
URLs, and a renderer keying its views by src tears nothing down.
Sourcepub fn set_media_rows(&self, heights: Vec<MediaHeight>) -> DocView
pub fn set_media_rows(&self, heights: Vec<MediaHeight>) -> DocView
Report how many visual rows each block media actually needs, measured from
the views the renderer laid out, keyed by the media’s src.
Core does no I/O and can’t know how tall a picture or a player is, so this is the only way a placeholder grows past its default single row. The loop is: lay out at the current reservation → measure → call this → repaint if it changed. Handing over the same measurements again is a no-op, so a renderer can report its current state each frame without diffing first.
A frontend that lays media out in its own units and simply reserves the vertical space itself (the way the gpui GUI does with images) never needs to call this at all.
Sourcepub fn insert_media(
&self,
kind: MediaKind,
destination: String,
alt: String,
) -> DocView
pub fn insert_media( &self, kind: MediaKind, destination: String, alt: String, ) -> DocView
Insert a block-level image, video, or audio at the caret. Any selection
becomes the alt / fallback text. See leaf_core::Doc::insert_media for
the markup each kind spells.
Sourcepub fn insert_thematic_break(&self) -> DocView
pub fn insert_thematic_break(&self) -> DocView
Insert a thematic break (---) at the caret — the toolbar’s Horizontal
Rule button. See leaf_core::Doc::insert_thematic_break for how it
handles a selection, a blank line, and the caret sitting mid-paragraph,
mid-list, or inside a quote.
Sourcepub fn source(&self) -> String
pub fn source(&self) -> String
The current source text — for a save (write to disk / iCloud / a document wrapper) or a source-view display.
Sourcepub fn selected_text(&self) -> Option<String>
pub fn selected_text(&self) -> Option<String>
The selected text, if any — for a clipboard copy/cut.
Sourcepub fn mark_saved(&self) -> DocView
pub fn mark_saved(&self) -> DocView
Mark the buffer saved after the host persisted LeafDoc::source its own
way — clears the dirty flag without touching a filesystem.
pub fn insert(&self, text: String) -> DocView
pub fn paste(&self, text: String) -> DocView
pub fn newline(&self) -> DocView
Sourcepub fn indent(&self) -> DocView
pub fn indent(&self) -> DocView
Tab away from a table: indent the caret’s line (or the selected lines) one
level, nesting a list item under its sibling. The frontend calls this when
LeafDoc::cell_tab declined because the caret isn’t in a table.
Sourcepub fn outdent(&self) -> DocView
pub fn outdent(&self) -> DocView
Shift+Tab away from a table: take one indent level back off the caret’s
line (or the selected lines), unnesting a list item. The mirror of
LeafDoc::indent.
Sourcepub fn cell_tab(&self, forward: bool) -> Option<DocView>
pub fn cell_tab(&self, forward: bool) -> Option<DocView>
Tab (forward) / Shift+Tab hops to the next/previous cell; Tab past the
last cell appends a fresh row and enters it.
Sourcepub fn cell_return(&self) -> Option<DocView>
pub fn cell_return(&self) -> Option<DocView>
Return drops to the cell below in the same column, appending a row at the table’s bottom.
Sourcepub fn cell_line_break(&self) -> Option<DocView>
pub fn cell_line_break(&self) -> Option<DocView>
Shift+Return inserts a hard line break within the current cell.
pub fn backspace(&self) -> DocView
pub fn delete_forward(&self) -> DocView
pub fn delete_word_back(&self) -> DocView
pub fn delete_word_forward(&self) -> DocView
pub fn move_left(&self, extend: bool) -> DocView
pub fn move_right(&self, extend: bool) -> DocView
pub fn move_up(&self, extend: bool) -> DocView
pub fn move_down(&self, extend: bool) -> DocView
pub fn move_word_left(&self, extend: bool) -> DocView
pub fn move_word_right(&self, extend: bool) -> DocView
pub fn move_home(&self, extend: bool) -> DocView
pub fn move_end(&self, extend: bool) -> DocView
pub fn move_doc_start(&self, extend: bool) -> DocView
pub fn move_doc_end(&self, extend: bool) -> DocView
pub fn select_all(&self) -> DocView
Sourcepub fn click(&self, row: u32, col: u32, extend: bool) -> DocView
pub fn click(&self, row: u32, col: u32, extend: bool) -> DocView
Place the caret from a click, in core’s column grid: row indexes the
visual Rows and col is the glyph column within it. Core clamps both
to real caret stops. Prefer LeafDoc::click_ch from a proportional
renderer.
Sourcepub fn click_ch(&self, row: u32, ch: u32, extend: bool) -> DocView
pub fn click_ch(&self, row: u32, ch: u32, extend: bool) -> DocView
Place the caret from a click whose horizontal position is a UTF-16
offset into the visual row’s text — what characterIndex(for:) hands
back. Converted to core’s display column before clicking, so a proportional
renderer never reasons about column widths itself.
Sourcepub fn select_word_ch(&self, row: u32, ch: u32) -> DocView
pub fn select_word_ch(&self, row: u32, ch: u32) -> DocView
Select the word under a click (row, ch) — the double-click gesture.
Sourcepub fn select_block_ch(&self, row: u32, ch: u32) -> DocView
pub fn select_block_ch(&self, row: u32, ch: u32) -> DocView
Select the whole logical text block under a click (row, ch) — the
triple-click gesture. Grabs the entire block even where it soft-wraps.
Sourcepub fn set_selection(
&self,
anchor_row: u32,
anchor_ch: u32,
focus_row: u32,
focus_ch: u32,
) -> DocView
pub fn set_selection( &self, anchor_row: u32, anchor_ch: u32, focus_row: u32, focus_ch: u32, ) -> DocView
Mirror a native selection into the model: [anchor, focus] given as
row + UTF-16 offset pairs. Each is resolved to a source offset the way a
click is, then set as the selection’s fixed and moving ends. A collapsed
range (anchor == focus) just places the caret.
Sourcepub fn selection_html(&self) -> Option<String>
pub fn selection_html(&self) -> Option<String>
The current selection rendered to HTML by twig — the rich flavor a copy
writes alongside the plain LeafDoc::selected_text. None when nothing
is selected.
Sourcepub fn paste_rich(&self, html: Option<String>, text: String) -> DocView
pub fn paste_rich(&self, html: Option<String>, text: String) -> DocView
Paste, preferring the clipboard’s rich (text/html) flavor: twig parses
html into the document’s own markup and inserts it. Falls back to the
plain text when there’s no HTML or it doesn’t parse.
pub fn toggle_bold(&self) -> DocView
pub fn toggle_italic(&self) -> DocView
pub fn toggle_code(&self) -> DocView
pub fn toggle_mark(&self) -> DocView
pub fn toggle_underline(&self) -> DocView
pub fn toggle_strike(&self) -> DocView
pub fn set_paragraph(&self) -> DocView
Sourcepub fn set_heading(&self, level: u32) -> DocView
pub fn set_heading(&self, level: u32) -> DocView
Toggle the current block to a heading of level (1–6); toggling the
active level off returns it to a paragraph, per core.
pub fn toggle_blockquote(&self) -> DocView
pub fn toggle_list(&self, ordered: bool) -> DocView
Sourcepub fn toggle_task_checked(&self) -> DocView
pub fn toggle_task_checked(&self) -> DocView
Tick or untick the task item at the caret. See
leaf_core::Doc::toggle_task_checked.
Sourcepub fn toggle_task_at(&self, offset: u64) -> DocView
pub fn toggle_task_at(&self, offset: u64) -> DocView
Tick or untick the task item covering offset — a tap on a rendered
checkbox, which must not drag the caret across the document to get there.
Sourcepub fn toggle_task_item(&self) -> DocView
pub fn toggle_task_item(&self) -> DocView
Give the list item at the caret a checkbox, or take its checkbox away.
Sourcepub fn task_checked_at_caret(&self) -> Option<bool>
pub fn task_checked_at_caret(&self) -> Option<bool>
Whether the item at the caret has a box and which way it faces — None
for a plain list item or no item at all. Drives a toolbar’s checked state.
Sourcepub fn capabilities(&self) -> Capabilities
pub fn capabilities(&self) -> Capabilities
Which of the formatting commands above this document’s format can actually spell — one flag per control, for building the toolbar.
Read once when a document opens: the answer depends only on the format, so it cannot change under an edit. Every command refuses on its own regardless — the model is the authority, not the toolbar — so a frontend that ignores this stays correct, it just offers buttons whose only effect is a line in the status bar.
Don’t collapse it to one flag. An HTML document takes ⌘B, ⌘I and inline
code (its marks are a tag pair) while refusing every heading, list, quote
and link, and Markdown refuses the highlight djot spells — so a toolbar
driven by Self::authorable alone would be wrong in both directions.
Whether this document’s format offers any door in — false only for a
wholly parse-only one (XML), where an app may as well open the file
read-only and hide the formatting section outright. For anything finer,
including whether to dim an individual button, use Self::capabilities.
Sourcepub fn caret_in_table(&self) -> bool
pub fn caret_in_table(&self) -> bool
Whether the caret is inside a table — for enabling the table controls.
Pair it with Capabilities::table: the caret is genuinely inside an
HTML <table>, and the grid controls still cannot edit one.
Sourcepub fn table_insert_row(&self, below: bool) -> DocView
pub fn table_insert_row(&self, below: bool) -> DocView
Insert an empty row below (below) or above the caret’s row.
Sourcepub fn table_delete_row(&self) -> DocView
pub fn table_delete_row(&self) -> DocView
Delete the caret’s row (not the header or the last body row).
Sourcepub fn table_insert_column(&self, right: bool) -> DocView
pub fn table_insert_column(&self, right: bool) -> DocView
Insert an empty column right (right) or left of the caret’s column.
Sourcepub fn table_delete_column(&self) -> DocView
pub fn table_delete_column(&self) -> DocView
Delete the caret’s column (unless it is the only one).
Sourcepub fn table_set_alignment(&self, alignment: TableAlignment) -> DocView
pub fn table_set_alignment(&self, alignment: TableAlignment) -> DocView
Set the caret’s column to alignment.
Sourcepub fn table_move_row(&self, down: bool) -> DocView
pub fn table_move_row(&self, down: bool) -> DocView
Move the caret’s row one place down (down) or up.
Sourcepub fn table_move_column(&self, right: bool) -> DocView
pub fn table_move_column(&self, right: bool) -> DocView
Move the caret’s column one place right (right) or left.
pub fn insert_link(&self, destination: String) -> DocView
Sourcepub fn link_destination_at_caret(&self) -> Option<String>
pub fn link_destination_at_caret(&self) -> Option<String>
The destination of the link under the caret, if the caret is inside one —
so a frontend can open it (⌘-click / “Open Link”) or show it. None when the
caret isn’t on a link.
Sourcepub fn link_destination_at(&self, off: u32) -> Option<String>
pub fn link_destination_at(&self, off: u32) -> Option<String>
The destination of the link at byte offset off —
link_destination_at_caret for a place
the caret isn’t.
What a frontend drawing part of the document outside the document asks:
a footnote’s text in a popover has link runs in it, and this is how those
runs learn where they point, since a Run carries how a span looks and
not what it means.
Sourcepub fn locate(&self, id: String) -> Option<LandingView>
pub fn locate(&self, id: String) -> Option<LandingView>
Where the locator id lands in this document — the #v2 half of a
chapter.dj#v2, resolved to the block it names. None when nothing here
answers to it, which is a host’s cue to open the document at its top
rather than refuse to go.
The query that gives a link finer granularity than the file. It reads an
explicit {#v1}, a djot heading’s minted id, or (for Markdown, which
mints none) a heading’s own words slugged — see leaf_core::Doc::locate.
Asked of any document, not only the open one: a host peeking at a
citation builds a LeafDoc over the other file’s bytes and asks this,
which is what lets a hover show the verse instead of the filename.
Sourcepub fn insert_footnote(&self) -> DocView
pub fn insert_footnote(&self) -> DocView
Write a footnote at the caret — the toolbar’s Footnote button. Both the
[^1] and the definition it needs go in as one edit (one undo takes both
back), the label is the lowest number the document has free, and the caret
is left in the empty note ready to type it. Gate the button on
Capabilities::footnote; see leaf_core::Doc::insert_footnote.
Sourcepub fn footnote_at_caret(&self) -> Option<FootnoteView>
pub fn footnote_at_caret(&self) -> Option<FootnoteView>
The footnote reference under the caret, resolved to the note it names —
so a frontend can show the note when a reader activates a [1], instead
of the nothing a reference click used to do. None when the caret isn’t
on a reference; see FootnoteView for the reference that resolved to
no definition.
Sourcepub fn footnote_at(&self, off: u32) -> Option<FootnoteView>
pub fn footnote_at(&self, off: u32) -> Option<FootnoteView>
The footnote reference at byte offset off, resolved to the note it
names — footnote_at_caret for a place the
caret isn’t.
This is what a hover asks: a pointer resting on a [1] wants the note’s
text in a popover, and moving the caret to find out would yank the reader
out of wherever they were typing.
Sourcepub fn footnote_definition_at_caret(&self) -> Option<FootnoteDefView>
pub fn footnote_definition_at_caret(&self) -> Option<FootnoteDefView>
The footnote definition the caret stands in, and where the reference that
names it is — the return leg of footnote_at_caret,
so following a footnote is a round trip rather than a fall.
None when the caret isn’t in a definition, which is also how a frontend
tells the two directions apart: the reference query answers up top, this
one answers down in the notes, and never both at once.
pub fn undo(&self) -> DocView
pub fn redo(&self) -> DocView
Sourcepub fn toggle_view(&self) -> DocView
pub fn toggle_view(&self) -> DocView
Switch between the rendered WYSIWYG surface and the raw source.
Sourcepub fn markup_mode(&self) -> MarkupMode
pub fn markup_mode(&self) -> MarkupMode
The current markup-exposure preference (see MarkupMode).
Sourcepub fn set_markup_mode(&self, mode: MarkupMode) -> DocView
pub fn set_markup_mode(&self, mode: MarkupMode) -> DocView
Set the markup-exposure preference. Returns a fresh view so a frontend
can repaint — and under Full it must, because the returned view is the
first one showing the caret’s line raw. Diaryx leaves it at the None
default.
Sourcepub fn set_line_flow(&self, mode: LineFlow) -> DocView
pub fn set_line_flow(&self, mode: LineFlow) -> DocView
Set the soft-break flow preference. Returns a fresh view so a frontend can repaint: like the markup-exposure preference this one changes rendering immediately, laying preserved soft breaks out as their own rows.
Source§impl LeafDoc
impl LeafDoc
Sourcepub fn caret_offset(&self) -> u32
pub fn caret_offset(&self) -> u32
The caret’s source offset (the selection’s moving end).
Sourcepub fn anchor_offset(&self) -> u32
pub fn anchor_offset(&self) -> u32
The selection’s fixed end (equals the caret when there’s no selection).
Sourcepub fn doc_end_offset(&self) -> u32
pub fn doc_end_offset(&self) -> u32
The last caret stop in the document — UITextInput.endOfDocument.
Sourcepub fn snap_offset(&self, off: u32) -> u32
pub fn snap_offset(&self, off: u32) -> u32
Snap an arbitrary offset to the nearest valid caret stop.
Sourcepub fn pos_for_offset(&self, off: u32) -> RowCol
pub fn pos_for_offset(&self, off: u32) -> RowCol
Where a source offset sits on screen: its visual (row, ch).
Sourcepub fn row_range_for(&self, start: u32, end: u32) -> RowRange
pub fn row_range_for(&self, start: u32, end: u32) -> RowRange
The rows a source range covers, inclusive — for drawing a block away from where it sits (a footnote peek, a link peek, a landing flash).
Ask this rather than mapping start and end - 1 through
Self::pos_for_offset. That pair reads correctly and is wrong: a
block’s last byte is often hidden — a note or a paragraph ending in a
link ends inside the link’s destination — and pos_for_offset snaps a
hidden offset forward to the next visible glyph, which for a trailing
one is on the next block’s row. A peek slicing that span drew the block
after it too. pos_for_offset’s snap is right for a caret and wrong for
a span; this is the question spans should be asking.
Sourcepub fn offset_for_pos(&self, row: u32, ch: u32) -> u32
pub fn offset_for_pos(&self, row: u32, ch: u32) -> u32
The source offset at visual (row, ch) — the inverse of
Self::pos_for_offset, for hit-testing a point to a position.
Sourcepub fn step_offset(&self, off: u32, delta: i32) -> u32
pub fn step_offset(&self, off: u32, delta: i32) -> u32
Move off by delta caret stops (negative = left) — position(from:offset:).
Sourcepub fn distance_offset(&self, from: u32, to: u32) -> i32
pub fn distance_offset(&self, from: u32, to: u32) -> i32
The count of caret stops between two offsets (signed) — offset(from:to:).
Sourcepub fn vertical_offset(&self, off: u32, down: bool) -> Option<u32>
pub fn vertical_offset(&self, off: u32, down: bool) -> Option<u32>
The offset one navigable row up/down from off, keeping its column —
position(from:in: .up/.down). None at the top/bottom edge.
Sourcepub fn text_in_range(&self, from: u32, to: u32) -> String
pub fn text_in_range(&self, from: u32, to: u32) -> String
The visible text between two offsets — text(in:). In the WYSIWYG
view this is not the raw source slice: a hidden inline-mark
delimiter (**, `, _) contributes nothing, matching what
distance_offset/step_offset already count in this same offset
space — while a genuine block boundary the range spans (a paragraph
gap, a table rule, …) contributes one inserted '\n' that
distance_offset/step_offset do not count (a block boundary costs
caret motion zero stops there, by design — see
the_caret_skips_the_gap_between_two_paragraphs in leaf-core’s
doc.rs). So the relationship is
text_in_range(a, b).chars().count() >= distance_offset(a, b), not
strict equality: the two agree exactly when (a, b) spans no block
boundary, and text_in_range is never shorter, only ever as long or
longer, when it does. That inequality is still what
UITextInput‘s own word/line tokenizer needs (see
leaf_core::wysiwyg::VisualMap::visible_text for why): it only reads
this string to find a boundary and converts the result back to a
position via position(from:offset:), which walks stops — the
inserted character is never hit as one, it only keeps the tokenizer
from reading two paragraphs’ last/first words as a single run of
letters. The source view has nothing hidden to begin with, so there
this is still exactly the raw slice.
Sourcepub fn set_selection_offsets(&self, anchor: u32, focus: u32) -> DocView
pub fn set_selection_offsets(&self, anchor: u32, focus: u32) -> DocView
Set the selection to [anchor, focus] by source offsets — the setter behind
UITextInput.selectedTextRange and handle dragging.