gpui-editor API
The complete public API of gpui-editor — every exported item,
with its signature, parameters, return contract, edge cases, and cost. For the
what-and-why (the three views, quick start, the WYSIWYG opt-in recipe), see
the README.
Public API at a glance
Section titled “Public API at a glance”Everything below is the complete public surface — if it isn’t listed here, it
isn’t public. (SyntaxStyle, AlertIcons, MathAlign, and PropertyIconFn
are defined in this crate’s markdown_syntax module and re-exported at the
crate root; nothing from gpui-markdown is re-exported.)
| Item | Kind | Signature | Purpose |
|---|---|---|---|
bind_keys | fn | fn bind_keys(cx: &mut App) | Bind the editing keys (once, at startup) |
LINE_HEIGHT_RATIO | const | const LINE_HEIGHT_RATIO: f32 = 1.45 | Row height as a multiple of the font size |
EditorState::set_clipboard_writer | method | fn set_clipboard_writer(&mut self, writer: ClipboardWriter) | Host-owned Copy/Cut clipboard write |
EditorState::copy_plain | method | fn copy_plain(&mut self, window: &mut Window, cx: &mut Context<Self>) | Copy the selection as raw markdown only |
EditorState::set_search | method | fn set_search(&mut self, matches: Vec<Range<usize>>, active: Option<usize>, cx) | Find-match highlights behind the text |
EditorState::offset_screen_top | method | fn offset_screen_top(&self, offset: usize) -> Option<Pixels> | Window-space top of a byte offset’s row |
find_in_source | fn | fn find_in_source(content: &str, query: &str) -> Vec<Range<usize>> | Case-insensitive source match ranges |
ClipboardWriter | type alias | Rc<dyn Fn(&str, &mut App)> | The Copy/Cut write hook |
EditorState::row_layout | method | fn row_layout(&self) -> Vec<(Pixels, Pixels)> | Per-line (top, first-wrap-row height) for host gutters |
mermaid_sources | fn | fn mermaid_sources(content: &str) -> Vec<SharedString> | Every ```mermaid block’s source, for pre-rendering |
paint_doc_icon | fn | fn paint_doc_icon(x, y, w, h: Pixels, color: Hsla, window: &mut Window) | The file chips’ line-art document glyph |
math_sources | fn | fn math_sources(content: &str) -> Vec<SharedString> | Every $$…$$ block’s LaTeX, for pre-rendering |
inline_math_sources | fn | fn inline_math_sources(content: &str) -> Vec<SharedString> | Every inline $…$ formula’s LaTeX, for pre-rendering |
EditorState | struct | — | The editor entity (text, caret, undo, layout) |
EditorState::new | constructor | fn new(window: &mut Window, cx: &mut Context<Self>) -> Self | An empty editor |
EditorState::with_text | builder | fn with_text(self, text: impl Into<String>) -> Self | Start with text (caret at start) |
EditorState::with_placeholder | builder | fn with_placeholder(self, text: impl Into<SharedString>) -> Self | Placeholder shown when empty |
EditorState::text | method | fn text(&self) -> &str | The document, borrowed |
EditorState::value | method | fn value(&self) -> SharedString | The document, owned |
EditorState::set_text | method | fn set_text(&mut self, text: impl Into<String>, cx: &mut Context<Self>) | Replace the whole document (programmatic load) |
EditorState::replace_range | method | fn replace_range(&mut self, range: Range<usize>, text: &str, cx: &mut Context<Self>) | Splice a range as one undoable edit |
EditorState::cursor | method | fn cursor(&self) -> usize | Caret byte offset |
EditorState::set_cursor | method | fn set_cursor(&mut self, offset: usize, cx: &mut Context<Self>) | Place the caret (clamped, snapped) |
EditorState::focus | method | fn focus(&self, window: &mut Window, cx: &mut Context<Self>) | Take keyboard focus |
EditorState::bounds_for_offset | method | fn bounds_for_offset(&self, offset: usize) -> Option<Bounds<Pixels>> | Window-space caret box at an offset |
EditorState::caret_screen_bounds | method | fn caret_screen_bounds(&self) -> Option<Bounds<Pixels>> | The caret’s painted Y range, for scroll-into-view |
EditorState::last_edit_was_keystroke | method | fn last_edit_was_keystroke(&self) -> bool | Gate host auto-pairing on this |
EditorState::set_diagnostics | method | fn set_diagnostics(&mut self, diagnostics: Vec<Diagnostic>, cx: &mut Context<Self>) | Replace the underlined spans |
EditorState::on_suggest | method | fn on_suggest(&mut self, provider: impl Fn(&str) -> Vec<String> + 'static) | Lazy right-click suggestion provider |
EditorState::set_markdown_style | method | fn set_markdown_style(&mut self, style: SyntaxStyle, cx: &mut Context<Self>) | Turn on WYSIWYG styling |
EditorState::clear_markdown_style | method | fn clear_markdown_style(&mut self, cx: &mut Context<Self>) | Back to plain text at runtime |
EditorState::set_block_image_provider | method | fn set_block_image_provider(&mut self, impl Fn(&str) -> Option<Arc<RenderImage>> + 'static) | Standalone  → decoded image |
EditorState::set_block_chip_provider | method | fn set_block_chip_provider(&mut self, impl Fn(&str) -> Option<SharedString> + 'static) | Classify  as a file chip + label |
EditorState::set_embed_provider | method | fn set_embed_provider(&mut self, impl Fn(&str) -> Option<(AnyView, Pixels)> + 'static) | Standalone ![[target]] → host view + reserved height |
EditorState::set_block_mermaid_provider | method | fn set_block_mermaid_provider(&mut self, impl Fn(&str) -> Option<(Arc<RenderImage>, f32, f32)> + 'static) | ```mermaid → diagram bitmap + logical w/h |
EditorState::set_block_math_provider | method | fn set_block_math_provider(&mut self, impl Fn(&str) -> Option<(Arc<RenderImage>, f32, f32)> + 'static) | $$…$$ LaTeX → typeset bitmap + logical w/h |
EditorState::set_block_math_em | method | fn set_block_math_em(&mut self, em: f32) | Provider’s em size; enables inline $…$ |
EditorState::set_code_highlighter | method | fn set_code_highlighter(&mut self, impl Fn(&str, &str) -> Vec<(Range<usize>, HighlightStyle)> + 'static) | Token colors for fenced code |
EditorState::set_editing_block | method | fn set_editing_block(&mut self, range: Range<usize>, view: AnyView, height: Pixels, cx: &mut Context<Self>) | Seat a host editor in a block’s gap |
EditorState::end_editing_block | method | fn end_editing_block(&mut self, cx: &mut Context<Self>) -> Option<Range<usize>> | Unseat it; returns the range to overwrite |
EditorState::set_editing_inline | method | fn set_editing_inline(&mut self, range: Range<usize>, view: AnyView, cx: &mut Context<Self>) | Seat a host editor over an inline $…$ span |
EditorState::end_editing_inline | method | fn end_editing_inline(&mut self, cx: &mut Context<Self>) -> Option<Range<usize>> | Unseat it; returns the range to overwrite |
EditorState::is_math_block_range | method | fn is_math_block_range(&self, range: &Range<usize>) -> bool | Commit guard: range still starts a $$ block |
EditorState::is_inline_math_range | method | fn is_inline_math_range(&self, range: &Range<usize>) -> bool | Commit guard: range still bounds a $…$ span |
EditorState::find_math_block | method | fn find_math_block(&self, source: &str, approx: usize) -> Option<Range<usize>> | Re-find a $$ block by LaTeX after offsets shifted |
EditorState::find_inline_math | method | fn find_inline_math(&self, latex: &str, approx: usize) -> Option<Range<usize>> | Re-find an inline $…$ span by LaTeX |
EditorState::math_align | method | fn math_align(&self, block_start: usize) -> MathAlign | A $$ block’s alignment marker |
EditorState::math_marker_edit | method | fn math_marker_edit(&self, block: Range<usize>, align: MathAlign) -> (Range<usize>, String) | Fold an alignment change into a commit edit |
EditorState::edit_math_at_caret | method | fn edit_math_at_caret(&mut self, cx: &mut Context<Self>) | Emit EditMath for the caret’s $$ block |
EditorState::property_block_at_caret | method | fn property_block_at_caret(&self) -> Option<(Range<usize>, SharedString)> | The property block at the caret, for the host’s /property flow |
EditorState::exit_math | method | fn exit_math(&mut self, block: Range<usize>, after: bool, window: &mut Window, cx: &mut Context<Self>) | Seat the caret just outside a math block |
EditorState::set_auto_replace | method | fn set_auto_replace(&mut self, impl Fn(&str) -> Option<(Range<usize>, String)> + 'static) | Word-completion rewrite hook |
EditorState::take_replaced_selection | method | fn take_replaced_selection(&mut self) -> Option<String> | Text the last keystroke typed over (consumed) |
EditorState::set_tab_indent | method | fn set_tab_indent(&mut self, spaces: usize) | Spaces per Tab / list-nesting level |
EditorState::caret_table_align | method | fn caret_table_align(&self) -> Option<CellAlign> | Caret column’s alignment (header row only) |
EditorState::set_caret_table_align | method | fn set_caret_table_align(&mut self, align: CellAlign, cx: &mut Context<Self>) | Rewrite the |---| separator |
EditorState::insert_table_row | method | fn insert_table_row(&mut self, below: bool, cx: &mut Context<Self>) | Empty row above/below the caret’s |
EditorState::delete_table_row | method | fn delete_table_row(&mut self, cx: &mut Context<Self>) | Delete the caret’s row (body only) |
EditorState::insert_table_column | method | fn insert_table_column(&mut self, right: bool, cx: &mut Context<Self>) | Empty column left/right of the caret’s |
EditorState::delete_table_column | method | fn delete_table_column(&mut self, cx: &mut Context<Self>) | Delete the caret’s column (not the last) |
EditorState::delete_table | method | fn delete_table(&mut self, cx: &mut Context<Self>) | Delete the whole table block |
EditorEvent | enum | 8 variants | Everything the editor asks the host to do |
SyntaxStyle | struct | 21 public fields | Colors + fonts + icon hooks for WYSIWYG |
AlertIcons | struct | 5 public fields | SVG asset paths for alert title icons |
Diagnostic | struct | pub range: Range<usize> | A flagged (underlined) span |
CellAlign | enum | Left · Center · Right | A table column’s alignment |
MathAlign | enum | Left · Center (default) · Right | A $$ block’s horizontal alignment |
PropertyIconFn | type | Rc<dyn Fn(&str) -> Option<SharedString>> | Property key → icon asset path |
EditorState also implements Render, Focusable,
EventEmitter<EditorEvent>, and EntityInputHandler (the IME plumbing) — so
it is a self-rendering GPUI entity: render it as a child, subscribe with
cx.subscribe(&editor, …).
fn bind_keys
Section titled “fn bind_keys”pub fn bind_keys(cx: &mut App)Bind the editor’s editing keys. Call once at startup. All bindings are
scoped to the "Editor" key context, so they resolve only while an editor is
focused and never shadow the host’s shortcuts. Without this call the editor
still accepts plain typing and mouse input (those arrive via
EntityInputHandler), but every action key — arrows, backspace, enter,
copy/paste, undo, bold/italic — is dead.
The bound set (cmd-* and its ctrl-* twin are both bound, so the same table
works on macOS, Windows, and Linux): backspace/delete/enter; arrows,
home/end; shift- + any movement to extend the selection;
alt-left/alt-right (± shift) word-wise; cmd-a select all;
cmd-c/cmd-x/cmd-v; cmd-z undo, cmd-shift-z/ctrl-y redo;
tab/shift-tab indent/outdent; cmd-b/cmd-i/cmd-e
bold/italic/inline-code; ctrl-cmd-space character palette; escape
dismisses the built-in right-click menus.
const LINE_HEIGHT_RATIO
Section titled “const LINE_HEIGHT_RATIO”pub const LINE_HEIGHT_RATIO: f32 = 1.45;Row height as a multiple of the editor’s font size. The editor derives its row
height from its own font (not the ambient window.line_height(), which
tracks the host’s UI text style and would leave caret and rows mismatched
against differently-sized editor text). Public so a host’s scroll math (e.g.
click-to-edit caret prediction) can mirror the editor’s row heights exactly.
fn paint_doc_icon
Section titled “fn paint_doc_icon”pub fn paint_doc_icon(x: Pixels, y: Pixels, w: Pixels, h: Pixels, color: Hsla, window: &mut Window)Paint the flat, line-art document glyph (a page with a folded top-right corner
and two text lines) the editor draws on its file chips, in color at the
given bounds. Stroke-drawn — not a font emoji — so it reads flat and on-theme
at any text size. Public so a host’s read-only view can draw the identical
icon on its own file chips (cross-view parity); the editor sizes it
h = font_size × 0.92, w = h × 0.74.
EditorState::set_clipboard_writer
Section titled “EditorState::set_clipboard_writer”pub fn set_clipboard_writer(&mut self, writer: ClipboardWriter)Route Copy/Cut through writer instead of gpui’s plain-string copy. The
writer receives the markdown text the editor would have written (Copy’s
renumbered ordered-list form included), so a host can add clipboard flavors
gpui can’t express — e.g. rendered HTML beside the plain string. Paste is
unaffected (it still reads gpui’s clipboard).
EditorState::set_search
Section titled “EditorState::set_search”pub fn set_search(&mut self, matches: Vec<Range<usize>>, active: Option<usize>, cx: &mut Context<Self>)Highlight matches (source byte ranges) behind the text — soft yellow, with
active in the stronger current-match orange (the reader’s browser-style find
colors). The quads reuse the selection’s multi-wrap-row geometry, tables
included. An empty vec clears. Host-driven: a find bar computes matches (see
find_in_source) and steps active.
EditorState::offset_screen_top
Section titled “EditorState::offset_screen_top”pub fn offset_screen_top(&self, offset: usize) -> Option<Pixels>The window-space top of the row containing byte offset, from the last
layout — what a find bar needs to scroll a match into view. None before the
first paint.
fn find_in_source
Section titled “fn find_in_source”pub fn find_in_source(content: &str, query: &str) -> Vec<Range<usize>>Case-insensitive occurrences of query in content, as source byte ranges —
Unicode-aware (comparison happens on lowercased text through an index map back
to original offsets, so Grüße matches grüße). An empty query matches
nothing.
EditorState::copy_plain
Section titled “EditorState::copy_plain”pub fn copy_plain(&mut self, window: &mut Window, cx: &mut Context<Self>)Copy the selection as the raw markdown only — bypassing any
ClipboardWriter — for pasting literal source into
rich surfaces where Copy’s extra flavors would win. Same selection extension
and ordered-list renumbering as Copy; the context menu’s “Copy as Markdown”.
type ClipboardWriter
Section titled “type ClipboardWriter”pub type ClipboardWriter = Rc<dyn Fn(&str, &mut App)>;The Copy/Cut write hook — (markdown_text, app).
EditorState::row_layout
Section titled “EditorState::row_layout”pub fn row_layout(&self) -> Vec<(Pixels, Pixels)>Per logical line, from the last paint: its top offset within the editor and its first wrap-row’s height — enough for a host-drawn gutter (line numbers) to align with rows without re-deriving layout. Empty before the first paint. Rows collapsed by a heading fold show no vertical advance (the next row’s top equals theirs) — a gutter should skip those.
fn mermaid_sources
Section titled “fn mermaid_sources”pub fn mermaid_sources(content: &str) -> Vec<SharedString>The diagram source of every ```mermaid block in content, in document
order. Lets a host pre-render diagrams off-thread before the editor’s
mermaid provider is consulted, so the provider finds a ready bitmap instead of
returning None (raw source) on first paint. Free function — needs no
EditorState.
fn math_sources
Section titled “fn math_sources”pub fn math_sources(content: &str) -> Vec<SharedString>The LaTeX source of every display $$…$$ block in content (the text between
the fences), in document order — the pre-render counterpart of
mermaid_sources for the math provider.
fn inline_math_sources
Section titled “fn inline_math_sources”pub fn inline_math_sources(content: &str) -> Vec<SharedString>The LaTeX of every inline $…$ formula in content — the inner LaTeX,
without the $ delimiters — so a host can pre-render them into the same
LaTeX-keyed store its block math provider reads (inline rendering reuses block
rasters; see set_block_math_em).
Guarantees & edge cases — lines inside fenced code blocks are skipped
($…$ is literal there). $$ fences are not inline formulas.
struct EditorState
Section titled “struct EditorState”pub struct EditorState { /* private */ }The editor: the document text, caret/selection state, undo/redo history, and a
cached layout (the wrapped lines from the last paint) for hit-testing, IME,
and the geometry queries. Renders the WYSIWYG view when a
SyntaxStyle is installed, the raw view otherwise.
- Entity: create with
cx.new(|cx| EditorState::new(window, cx)); it implementsRender(render it as a child — it auto-grows to content height, no inner scrollbar) andFocusable. - Events:
EventEmitter<EditorEvent>— subscribe withcx.subscribe(&editor, …); seeEditorEvent. - Offsets: every offset/range in this API is a byte offset into
text. Mutating methods snap incoming offsets tocharboundaries, so a stale or mid-character offset can’t panic. - Threading: a GPUI entity — main thread only, like all GPUI UI.
- Layout-dependent queries (
bounds_for_offset,caret_screen_bounds) read the last paint’s layout and returnNonebefore the first paint.
Construction
Section titled “Construction”pub fn new(window: &mut Window, cx: &mut Context<Self>) -> SelfAn empty editor: no text, no placeholder, tab indent 4, no markdown style, no
providers. (The window parameter is currently unused — kept for the
conventional GPUI constructor shape.) Infallible.
with_text
Section titled “with_text”pub fn with_text(self, text: impl Into<String>) -> SelfBuilder: start with text, caret at offset 0. Does not touch undo history
(there is none yet).
with_placeholder
Section titled “with_placeholder”pub fn with_placeholder(self, text: impl Into<SharedString>) -> SelfBuilder: the dimmed placeholder shown while the document is empty.
Content
Section titled “Content”pub fn text(&self) -> &strThe whole document, borrowed. Newline-separated; every byte offset in this API indexes into it.
pub fn value(&self) -> SharedStringThe document as an owned SharedString (clones the content). Use
text when a borrow suffices.
set_text
Section titled “set_text”pub fn set_text(&mut self, text: impl Into<String>, cx: &mut Context<Self>)Replace the whole document — a programmatic load, not a user edit.
Guarantees & edge cases
- Caret resets to offset 0; any selection and IME marked text are dropped.
- Clears the undo/redo history — the load isn’t undoable to the prior
document. Use
replace_rangeto write back an edit that should be undoable. - Does not emit
EditorEvent::Changed(the host initiated the change, so echoing it back would loop a save-on-change host). - Existing diagnostics are not cleared — feed a fresh
set_diagnosticsafter loading.
replace_range
Section titled “replace_range”pub fn replace_range(&mut self, range: Range<usize>, text: &str, cx: &mut Context<Self>)Replace byte range with text as one recorded (undoable) edit, leaving
the caret after the inserted text. The programmatic-edit counterpart to
set_text: it preserves — and extends — the undo history, so a
host writing back a structural edit (e.g. a committed $$…$$ formula) lands
as a normal undo step instead of clobbering history.
Parameters
| Name | Type | Description |
|---|---|---|
range | Range<usize> | Byte range to replace. Clamped to the document; snapped to char boundaries (start down, end up), so a stale/shifted range can’t panic mid-UTF-8. |
text | &str | Replacement text (may be empty = deletion). |
Guarantees & edge cases
- One undo step; a following keystroke is not coalesced into it.
- Diagnostics are remapped (spans after the edit shift; overlapping spans drop).
- Does not emit
Changed— the host made the edit. - Caret lands at
range.start + text.len(); selection collapses.
Caret & geometry
Section titled “Caret & geometry”cursor
Section titled “cursor”pub fn cursor(&self) -> usizeThe caret’s byte offset (the moving end of any selection). For hosts that drive a menu/completion off the caret position.
set_cursor
Section titled “set_cursor”pub fn set_cursor(&mut self, offset: usize, cx: &mut Context<Self>)Place the caret at offset, collapsing any selection. Clamped to the document
length and snapped down to a char boundary, so a raw click offset is
safe to pass. Emits SelectionChanged. Only moves the
caret — call focus to actually receive keyboard input (e.g. when
entering edit mode from clicked rendered text).
pub fn focus(&self, window: &mut Window, cx: &mut Context<Self>)Focus the editor so it receives keyboard input (delegates to its
FocusHandle). The complement of set_cursor for click-to-edit flows.
bounds_for_offset
Section titled “bounds_for_offset”pub fn bounds_for_offset(&self, offset: usize) -> Option<Bounds<Pixels>>The window-space caret box at offset (zero-width; the row’s height) —
for anchoring a popup (slash menu, toolbar) at a document position.
Returns — None before the first paint, or if offset’s row isn’t in the
last paint’s layout. Reads cached layout only — cheap; safe to call per frame.
caret_screen_bounds
Section titled “caret_screen_bounds”pub fn caret_screen_bounds(&self) -> Option<Bounds<Pixels>>The caret’s own painted Y range in window space (anchored at the editor’s left
edge), or None before the first paint — for a host to scroll the caret into
view. Computed from the layout stored at the last paint, so it’s valid for
caret moves that don’t change the text (arrow keys, click).
last_edit_was_keystroke
Section titled “last_edit_was_keystroke”pub fn last_edit_was_keystroke(&self) -> booltrue only when the last content change was a single typed character or a
single-character backspace — not a programmatic or multi-char edit (table
ops, paste, replace_range, auto-replace). A host doing its
own auto-pairing gates on this so structural edits don’t trip it. See also
take_replaced_selection.
Diagnostics & spell-check
Section titled “Diagnostics & spell-check”set_diagnostics
Section titled “set_diagnostics”pub fn set_diagnostics(&mut self, diagnostics: Vec<Diagnostic>, cx: &mut Context<Self>)Replace the set of flagged spans, each painted with a red wavy underline. The
host computes them (e.g. with os-spellcheck) and
refreshes on its own schedule — typically per edit; detection is designed to
be the cheap half.
Guarantees & edge cases — between refreshes the editor keeps the spans valid across edits itself: spans after an edit shift by the size delta, spans overlapping the edited text are dropped (stale), so squiggles don’t flicker off on every keystroke. Out-of-bounds spans are ignored at paint.
on_suggest
Section titled “on_suggest”pub fn on_suggest(&mut self, provider: impl Fn(&str) -> Vec<String> + 'static)Install the replacement-suggestion provider, consulted only when the user
right-clicks a flagged word — never in the per-edit pass, because the OS
suggestion call can be slow (a synchronous XPC round-trip on macOS). It
receives the offending word and returns candidates best-first; the editor
shows them in its built-in popup and applies the chosen one as a normal
(undoable, Changed-emitting) edit.
Live Markdown styling
Section titled “Live Markdown styling”set_markdown_style
Section titled “set_markdown_style”pub fn set_markdown_style(&mut self, style: SyntaxStyle, cx: &mut Context<Self>)Turn on WYSIWYG (live-preview) markdown styling with the given color/font palette — call once at setup (or again to re-theme). Everything markdown then renders live: headings sized, inline styles applied, markers hidden and revealed only around the caret, lists/quotes/alerts/rules drawn, tables gridded. Without it the editor is the raw view: plain text, spell squiggles only. The block providers below are each independently optional on top of this.
clear_markdown_style
Section titled “clear_markdown_style”pub fn clear_markdown_style(&mut self, cx: &mut Context<Self>)Turn live-preview styling off at runtime — back to plain text (squiggles only), e.g. the host’s “WYSIWYG off” toggle. No-op if styling was already off. Installed providers stay; they’re dormant without a style.
Block widgets & providers
Section titled “Block widgets & providers”All providers are host-supplied closures: the host owns loading, caching,
and rendering; the editor calls the provider during paint (so it must be
fast — return None while something is still loading, and pre-render with the
*_sources helpers). Every block widget renders only
when the caret is elsewhere — the caret’s own row shows raw source for
editing (reveal-on-caret). Providers only take effect while a markdown style
is installed.
set_block_image_provider
Section titled “set_block_image_provider”pub fn set_block_image_provider( &mut self, provider: impl Fn(&str) -> Option<Arc<RenderImage>> + 'static,)Resolve a standalone  line’s src to a decoded image; the line then
renders as the image. None (still decoding / failed) → the line shows its
raw source. Mid-text images flow as small inline thumbnails through the same
provider; clicking one emits PreviewImage. A rendered
image’s corner grip drag-resizes it (writes {width=N} back to the source).
set_block_chip_provider
Section titled “set_block_chip_provider”pub fn set_block_chip_provider( &mut self, provider: impl Fn(&str) -> Option<SharedString> + 'static,)Classify an  reference as a file chip (e.g. a PDF) and supply
its display label. Such lines render as a clickable chip: left-click emits
OpenLink(src), right-click places the caret to edit. Consulted
before the image provider — None means “not a chip, try an image”.
set_embed_provider
Section titled “set_embed_provider”pub fn set_embed_provider( &mut self, provider: impl Fn(&str) -> Option<(AnyView, Pixels)> + 'static,)Resolve a standalone ![[target]] line (Obsidian transclusion) to the host
view that renders the embedded content, plus the row height to reserve.
The editor reserves that gap in its layout and paints the AnyView there as
an absolute overlay (skipped on the caret’s row, where the raw ![[…]] shows
for editing). The host owns resolution — fetch the target page, render it
(typically with gpui-markdown, whose
syntax::embed_targets / extract_block / extract_section slice #^id /
#Heading anchors) — and refreshing views when a source page changes; it
estimates and caps the height (long content scrolls inside the view).
None → a compact ⧉ target chip for an unresolved target.
set_block_mermaid_provider
Section titled “set_block_mermaid_provider”pub fn set_block_mermaid_provider( &mut self, provider: impl Fn(&str) -> Option<(Arc<RenderImage>, f32, f32)> + 'static,)Resolve a ```mermaid block’s source to a rendered diagram: the bitmap plus
its logical (display) width and height in px. The host supplies the
logical size because only it knows the raster’s pixel density (see the math
provider below). Pre-render with mermaid_sources;
None while rendering → the block shows its raw fenced source.
set_block_math_provider
Section titled “set_block_math_provider”pub fn set_block_math_provider( &mut self, provider: impl Fn(&str) -> Option<(Arc<RenderImage>, f32, f32)> + 'static,)Resolve a $$…$$ block’s LaTeX to a typeset equation: the bitmap plus its
logical (display) width and height in px. The host supplies the logical
size because it knows the density the raster was typeset at (e.g. a fixed 2×
DPR) — deriving it from texture pixels ÷ window scale factor renders 2× too
large on a 1× display. Pre-render with math_sources.
Editing is the host’s, via the math editing hooks and
the EditMath event.
set_block_math_em
Section titled “set_block_math_em”pub fn set_block_math_em(&mut self, em: f32)Declare the em (font size) the math provider rasterizes at. This turns on
inline $…$ rendering: each inline formula reuses the block raster for the
same LaTeX, scaled by text_em / em so it sits at text size, painted over a
reserved gap in the line. Pre-render inline sources with
inline_math_sources. em <= 0 turns inline
rendering back off.
set_code_highlighter
Section titled “set_code_highlighter”pub fn set_code_highlighter( &mut self, f: impl Fn(&str, &str) -> Vec<(Range<usize>, HighlightStyle)> + 'static,)Token colors for fenced code blocks in WYSIWYG: (language tag, block text) →
sorted, non-overlapping styled ranges (byte offsets into the block text).
Host-supplied (e.g. a tree-sitter highlighter) so the crate stays engine-free;
absent it, code renders uniformly in SyntaxStyle::code.
Math editing hooks
Section titled “Math editing hooks”The seat/commit protocol for hosting a structural (2-D) math editor — and,
via the same block seat, an in-place property editor. Flow: the editor emits
EditMath (or EditProperties) → the host
builds its editor view and seats it (set_editing_block /
set_editing_inline) → on commit the host unseats it (end_editing_*),
validates the returned range with a guard (is_math_block_range /
is_inline_math_range, re-finding via find_* if stale), and overwrites the
range with replace_range.
set_editing_block
Section titled “set_editing_block”pub fn set_editing_block( &mut self, range: Range<usize>, view: AnyView, height: Pixels, cx: &mut Context<Self>,)Begin an in-line structural edit of the block at range: the editor reserves
a gap of height at the block’s spot and paints view (the host’s editor)
there. Pass the block’s currently displayed height so the formula stays put
instead of jumping to a fixed size. The host focuses view itself. One block
seat at a time — a second call replaces the first.
end_editing_block
Section titled “end_editing_block”pub fn end_editing_block(&mut self, cx: &mut Context<Self>) -> Option<Range<usize>>End the block edit (the host committed or cancelled). Returns the seated
block’s byte range so the host can overwrite it — None if nothing was
seated. Validate with is_math_block_range before
splicing: another edit may have shifted offsets.
set_editing_inline
Section titled “set_editing_inline”pub fn set_editing_inline( &mut self, range: Range<usize>, view: AnyView, cx: &mut Context<Self>,)Begin a structural edit of the inline $…$ span at range (absolute bytes):
view is overlaid at the formula’s painted spot (no gap is reserved — the
formula’s own space is reused). The host focuses view.
end_editing_inline
Section titled “end_editing_inline”pub fn end_editing_inline(&mut self, cx: &mut Context<Self>) -> Option<Range<usize>>End the inline edit; returns the span’s byte range to overwrite (None if
nothing was seated). Validate with
is_inline_math_range.
is_math_block_range
Section titled “is_math_block_range”pub fn is_math_block_range(&self, range: &Range<usize>) -> boolWhether byte range (half-open) still starts a $$…$$ block — a commit guard
so a stale/shifted range can’t splice the block into the wrong place and
corrupt the document. false for out-of-bounds or non-boundary ranges (never
panics).
is_inline_math_range
Section titled “is_inline_math_range”pub fn is_inline_math_range(&self, range: &Range<usize>) -> boolWhether range still bounds an inline $…$ span: a $ at each end, content
between, no newline, and not a $$ fence. The inline commit guard. Never
panics on bad ranges.
find_math_block
Section titled “find_math_block”pub fn find_math_block(&self, source: &str, approx: usize) -> Option<Range<usize>>Re-find a $$…$$ block by its exact LaTeX source, returned as the byte
range covering both fences — the one nearest to the now-stale byte approx
if several blocks share the LaTeX. For re-targeting after a prior formula’s
commit shifted offsets. None if no block has that source.
find_inline_math
Section titled “find_inline_math”pub fn find_inline_math(&self, latex: &str, approx: usize) -> Option<Range<usize>>The inline counterpart of find_math_block: re-find a
$…$ span by its exact inner LaTeX (no delimiters), as the absolute byte
range (including the $s) nearest approx.
math_align
Section titled “math_align”pub fn math_align(&self, block_start: usize) -> MathAlignThe horizontal alignment of the $$…$$ block whose byte range starts at
block_start — from its <!-- math:ALIGN --> marker comment on the line
above, or MathAlign::Center (the default, stored as no marker). Lets the
host seed its math editor at the right justification.
math_marker_edit
Section titled “math_marker_edit”pub fn math_marker_edit(&self, block: Range<usize>, align: MathAlign) -> (Range<usize>, String)Compute the single edit that writes align’s marker for the $$ block at
byte range block: the (possibly marker-extended) range to replace, and the
marker prefix to prepend to the rewritten block text. The host appends the
committed block text to the prefix and applies one
replace_range — folding the marker into the commit edit
avoids a separate, range-shifting edit. Center → empty prefix (and the
returned range swallows any existing marker line, dropping it).
edit_math_at_caret
Section titled “edit_math_at_caret”pub fn edit_math_at_caret(&mut self, cx: &mut Context<Self>)If the caret sits inside a $$…$$ block, emit
EditMath for it (at_end: false, inline: false) — so a host
can turn a freshly inserted, empty math block (e.g. a /math snippet)
straight into a live editor instead of raw source. No-op outside a block.
property_block_at_caret
Section titled “property_block_at_caret”pub fn property_block_at_caret(&self) -> Option<(Range<usize>, SharedString)>The key:: value property block covering the caret’s line, as its absolute
byte range + source — None outside a block or when no markdown style is
installed (WYSIWYG-only, like edit_math_at_caret).
Lets a host open the property editor on a freshly inserted /property line
instead of leaving raw source.
exit_math
Section titled “exit_math”pub fn exit_math( &mut self, block: Range<usize>, after: bool, window: &mut Window, cx: &mut Context<Self>,)Seat the caret on the plain-text line just before (after = false) or
after (after = true) the math block, and focus the editor — the
keyboard counterpart to clicking away, for when the caret flows out of a
formula’s structural editor. Skips the hidden $$ fence lines (landing on one
would reveal raw source). Emits SelectionChanged.
Auto-replace
Section titled “Auto-replace”set_auto_replace
Section titled “set_auto_replace”pub fn set_auto_replace( &mut self, f: impl Fn(&str) -> Option<(Range<usize>, String)> + 'static,)Install the word-completion rewrite hook, consulted when a word-boundary
character (space, punctuation, Enter) completes a word. It receives the
just-finished line’s text up to the boundary and returns the slice range
to replace plus its replacement — e.g. wrapping a completed page title as
[[title]] — or None to leave the text alone.
Guarantees & edge cases
- The rewrite is its own undo step (one ⌫Z restores the plain word); the caret keeps its place after the boundary.
- Never consulted inside fenced code blocks (text there is verbatim), and only for single-character insertions — never pastes or IME commits.
- A returned range that is empty or out of the line’s bounds is ignored.
take_replaced_selection
Section titled “take_replaced_selection”pub fn take_replaced_selection(&mut self) -> Option<String>The text the most recent keystroke edit typed over (its selection), if
any — consumed on read (one read per edit; subsequent calls return None).
Lets a host’s auto-pair logic tell “opener typed over a selection” (wrap it)
apart from deletions with identical text diffs. Pair with
last_edit_was_keystroke.
Indentation
Section titled “Indentation”set_tab_indent
Section titled “set_tab_indent”pub fn set_tab_indent(&mut self, spaces: usize)Spaces inserted per Tab / list-nesting level (the Indent/Outdent actions).
Clamped to a minimum of 1. Default 4. The host keeps this in sync with its
list-indent setting so nesting is configurable.
Table editing
Section titled “Table editing”The editor renders GFM tables as a grid and edits inside the cells (arrows
walk cell-to-cell keeping the column; Enter drops to the cell below; the
built-in right-click menu offers all of the below). These methods let a host
drive the same structural edits from its own UI. Common contract: each is a
no-op when the caret isn’t in a table; each structural edit is one undo
step, keeps the caret in (or near) its cell, remaps diagnostics, and — unlike
the content setters — emits Changed (the editor itself
changed the text, so the host must hear about it).
caret_table_align
Section titled “caret_table_align”pub fn caret_table_align(&self) -> Option<CellAlign>The alignment of the table column the caret sits in — but only while the caret
is in the table’s header row (alignment is a per-column property, set once
from the header). None otherwise, so it doubles as “should I show the
alignment control?”.
set_caret_table_align
Section titled “set_caret_table_align”pub fn set_caret_table_align(&mut self, align: CellAlign, cx: &mut Context<Self>)Set the caret column’s alignment by rewriting the table’s |---| separator
row (:-- / :-: / --:); the caret stays put. No-op outside a table or on
the separator row itself.
insert_table_row
Section titled “insert_table_row”pub fn insert_table_row(&mut self, below: bool, cx: &mut Context<Self>)Insert an empty row above (false) / below (true) the caret’s row; the
caret moves into the new row’s first cell. From the header row a new row
always lands below the separator (the first body position).
delete_table_row
Section titled “delete_table_row”pub fn delete_table_row(&mut self, cx: &mut Context<Self>)Delete the caret’s row — body rows only (the header and separator stay; a no-op on them). The caret keeps its cell and in-cell offset, landing on the row that takes the deleted row’s place (or the header if no body rows remain).
insert_table_column
Section titled “insert_table_column”pub fn insert_table_column(&mut self, right: bool, cx: &mut Context<Self>)Insert an empty column left (false) / right (true) of the caret’s column —
a cell added to every row, the separator getting a default-left marker. The
caret stays in its cell.
delete_table_column
Section titled “delete_table_column”pub fn delete_table_column(&mut self, cx: &mut Context<Self>)Delete the caret’s column from every row; the caret stays near where the column was. No-op on the last remaining column (a table keeps ≥ 1 column).
delete_table
Section titled “delete_table”pub fn delete_table(&mut self, cx: &mut Context<Self>)Delete the whole table the caret is in — its grid lines plus an optional
<!-- table:STYLE --> marker line directly above — joining the surrounding
text. The caret lands where the table was.
enum EditorEvent
Section titled “enum EditorEvent”#[derive(Clone, Debug, PartialEq, Eq)]pub enum EditorEvent { /* 8 variants below */ }Everything the editor asks the host to do. Subscribe with
cx.subscribe(&editor, |host, editor, event: &EditorEvent, cx| …).
Changed
Section titled “Changed”The document text changed via a user edit: typing, delete, paste, IME,
applying a spell suggestion, toggling a checkbox or alert fold, an image
resize, or a table structural edit (including the host-driven
table methods). Not emitted for programmatic
set_text / replace_range — the host made
those, so echoing would loop a save-on-change host.
Host obligation: persist / re-derive whatever tracks the text (save, re-run spell-check, refresh pre-rendered math).
OpenLink(SharedString)
Section titled “OpenLink(SharedString)”A file chip, [text](url) link, or bare http(s):// URL was left-clicked;
the payload is the src/URL. A navigation hint — the text is untouched.
Host obligation: open it — http(s) externally, files via the host’s own resolution.
OpenWikiLink(SharedString)
Section titled “OpenWikiLink(SharedString)”A [[wiki-link]], #tag, wiki file chip, or property-panel pill was
left-clicked; the payload is the target page title. It may carry a
#Heading / #^id anchor (split with gpui_markdown::syntax’s
split_heading_anchor / split_block_anchor).
Host obligation: navigate to that page (and scroll to the anchor).
SelectionChanged
Section titled “SelectionChanged”The caret or selection moved without a text change (arrows, click,
set_cursor, exit_math).
Host obligation: update any caret-anchored affordance — e.g. a
table-alignment toolbar driven by
caret_table_align + bounds_for_offset.
EditMath { range, source, at_end, inline }
Section titled “EditMath { range, source, at_end, inline }”The caret entered a math formula (click, or arrowing into it).
| Field | Type | Meaning |
|---|---|---|
range | Range<usize> | The formula’s byte range — a $$…$$ block including both fences, or an inline span including both $s. |
source | SharedString | The LaTeX between the delimiters, to seed the host’s editor. |
at_end | bool | Seat the structural editor’s caret at the formula’s end (entered from below/right or by click) vs its start (from above/left). |
inline | bool | true for an inline $…$ span (seat with set_editing_inline, splice $…$ back); false for a $$…$$ block (full-width gap via set_editing_block). |
Host obligation: open a structural editor seeded from source, seat it,
and on commit overwrite range (guarded — see
Math editing hooks) via
replace_range.
MathMenu { source, position }
Section titled “MathMenu { source, position }”A rendered formula was right-clicked. source is its LaTeX; position the
window-space click point.
Host obligation: show a context menu (Copy LaTeX / Export / …) at
position.
EditProperties { range, source, at_end, row }
Section titled “EditProperties { range, source, at_end, row }”A key:: value property panel was clicked, arrowed, word-jumped, or edited
into (Enter inside the block, a backspace/delete join at its edge).
| Field | Type | Meaning |
|---|---|---|
range | Range<usize> | Byte range of the whole consecutive key:: value block. |
source | SharedString | The block’s text, to seed the host’s property editor. |
at_end | bool | true = entered from below (focus the last field, caret at the value’s end); false = from above (focus the first). |
row | Option<usize> | The property line’s index within the block, when the entry targeted a specific row (a click, Enter, a join) — the host focuses that row. None for plain arrow entry (at_end decides). |
Host obligation: seat an in-place property editor with
set_editing_block and overwrite range on commit —
the same seat/commit pattern as EditMath for a block.
PreviewImage(SharedString)
Section titled “PreviewImage(SharedString)”An inline (mid-text) image thumbnail was left-clicked; the payload is its
src. The text is untouched.
Host obligation: open a full-size preview.
struct SyntaxStyle
Section titled “struct SyntaxStyle”#[derive(Clone)]pub struct SyntaxStyle { /* 21 public fields below */ }Colors + monospace font + icon hooks for the live-preview styling, supplied by
the host (set_markdown_style) so the editor stays
theme- and asset-agnostic. No Default — the host builds it from its theme,
every field explicit. All fields are gpui::Hsla unless noted.
| Field | Type | Styles |
|---|---|---|
marker | Hsla | dimmed syntax markers (**, `, [, ](…), ^id, …) |
code | Hsla | inline `code` (and unhighlighted fenced-code) text |
code_bg | Hsla | inline-code background (also the table row-shade tint) |
link | Hsla | [text](url), [[wiki-links]], footnote/reference refs |
tag | Hsla | #tags |
quote | Hsla | blockquote text + left border (a muted tone) |
alert_note | Hsla | > [!NOTE] bar + title |
alert_tip | Hsla | > [!TIP] bar + title |
alert_important | Hsla | > [!IMPORTANT] bar + title |
alert_warning | Hsla | > [!WARNING] bar + title |
alert_caution | Hsla | > [!CAUTION] bar + title |
alert_icons | Option<AlertIcons> | SVG asset paths for the alert title icons; None = bold label only |
rule | Hsla | thematic break (---) divider |
mark_bg | Hsla | <mark> highlight background |
popover_bg | Hsla | built-in menu (table ops, spell suggestions) surface |
popover_border | Hsla | menu border |
popover_fg | Hsla | menu text |
popover_hover | Hsla | menu hovered-row background |
popover_divider | Hsla | menu group divider |
mono | gpui::Font | monospace font for inline code + code blocks |
property_icon | Option<PropertyIconFn> | property key → icon asset path for the property panel; None = no icons |
struct AlertIcons
Section titled “struct AlertIcons”#[derive(Clone)]pub struct AlertIcons { pub note: SharedString, pub tip: SharedString, pub important: SharedString, pub warning: SharedString, pub caution: SharedString,}Per-kind SVG asset paths for the GitHub-alert title icons, resolved through
the host’s AssetSource — the crate itself ships no assets. Installed via
SyntaxStyle::alert_icons.
struct Diagnostic
Section titled “struct Diagnostic”#[derive(Clone)]pub struct Diagnostic { pub range: Range<usize>, // byte range in the document}A flagged span to underline (red squiggle); &text[range] is the offending
word. Fed in via set_diagnostics; suggestions fetched
lazily via on_suggest.
enum CellAlign
Section titled “enum CellAlign”#[derive(Clone, Copy, PartialEq, Eq, Debug)]pub enum CellAlign { Left, Center, Right }A table column’s text alignment, for
caret_table_align /
set_caret_table_align.
enum MathAlign
Section titled “enum MathAlign”#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]pub enum MathAlign { Left, #[default] Center, Right }Horizontal alignment of a display $$…$$ block, chosen per block via a
<!-- math:left --> / <!-- math:right --> marker comment on the line
directly above it. Center is the default (stored as no marker, matching
LaTeX display math); standard Markdown viewers ignore the comment. Read with
math_align, written via
math_marker_edit.
type PropertyIconFn
Section titled “type PropertyIconFn”pub type PropertyIconFn = std::rc::Rc<dyn Fn(&str) -> Option<gpui::SharedString>>;Maps a property key (tags, status, …) to an icon asset path the host
serves, or None for no icon — host-provided (SyntaxStyle::property_icon)
so the crate makes no assumption about which assets exist. Rc, so one
resolver is cheaply shared across many editors.