Skip to main content

twig_sys/
lib.rs

1//! Low-level FFI bindings for Twig's C ABI, plus the build script that links
2//! the native `libtwig.a`.
3//!
4//! This crate is the `-sys` layer: it exposes the raw `extern "C"` functions,
5//! the `#[repr(C)]` type mirrors, and the status/format constants, and nothing
6//! else. The safe, ergonomic API lives in the `twig-doc` crate, which depends
7//! on this one. Direct use is `unsafe` and unstable across ABI-version bumps —
8//! see [`TWIG_ABI_VERSION`].
9#![allow(non_camel_case_types)]
10
11use std::os::raw::{c_char, c_int};
12
13/// The C ABI contract version this binding is written against; see
14/// `twig_abi_version`. Must match the value baked into the linked library
15/// (asserted at runtime by the `abi_version_matches` test in `lib.rs`).
16pub const TWIG_ABI_VERSION: u32 = 6;
17
18// Freeze the canonical 64-bit layout of every `#[repr(C)]` mirror below so it
19// can never silently drift from the Zig `extern struct` it shadows. These are
20// the same offsets asserted on the Zig side in `src/c_abi.zig`; a change on
21// either side that isn't matched on the other fails to compile. Gated on 64-bit
22// (the offsets are the LP64/LLP64 layout); 32-bit targets pack tighter but the
23// C ABI still keeps both languages in agreement.
24#[cfg(target_pointer_width = "64")]
25const _: () = {
26    use std::mem::offset_of;
27    use std::mem::size_of;
28
29    assert!(size_of::<TwigSpan>() == 16);
30    assert!(offset_of!(TwigSpan, start) == 0);
31    assert!(offset_of!(TwigSpan, end) == 8);
32
33    assert!(size_of::<TwigQueryMatch>() == 56);
34    assert!(offset_of!(TwigQueryMatch, node_id) == 0);
35    assert!(offset_of!(TwigQueryMatch, span) == 8);
36    assert!(offset_of!(TwigQueryMatch, content_span) == 24);
37    assert!(offset_of!(TwigQueryMatch, has_content_span) == 40);
38    assert!(offset_of!(TwigQueryMatch, kind) == 48);
39
40    assert!(size_of::<TwigChange>() == 32);
41    assert!(offset_of!(TwigChange, old_span) == 0);
42    assert!(offset_of!(TwigChange, new_span) == 16);
43
44    assert!(size_of::<TwigFlatNode>() == 168);
45    assert!(offset_of!(TwigFlatNode, id) == 0);
46    assert!(offset_of!(TwigFlatNode, parent) == 4);
47    assert!(offset_of!(TwigFlatNode, first_child) == 8);
48    assert!(offset_of!(TwigFlatNode, next_sibling) == 12);
49    assert!(offset_of!(TwigFlatNode, span) == 16);
50    assert!(offset_of!(TwigFlatNode, content_span) == 32);
51    assert!(offset_of!(TwigFlatNode, has_content_span) == 48);
52    assert!(offset_of!(TwigFlatNode, level) == 52);
53    assert!(offset_of!(TwigFlatNode, kind) == 56);
54    assert!(offset_of!(TwigFlatNode, text_ptr) == 64);
55    assert!(offset_of!(TwigFlatNode, text_len) == 72);
56    assert!(offset_of!(TwigFlatNode, destination_ptr) == 80);
57    assert!(offset_of!(TwigFlatNode, destination_len) == 88);
58    assert!(offset_of!(TwigFlatNode, head) == 96);
59    assert!(offset_of!(TwigFlatNode, alignment) == 100);
60    assert!(offset_of!(TwigFlatNode, name_ptr) == 104);
61    assert!(offset_of!(TwigFlatNode, name_len) == 112);
62    assert!(offset_of!(TwigFlatNode, attrs_ptr) == 120);
63    assert!(offset_of!(TwigFlatNode, attrs_len) == 128);
64    assert!(offset_of!(TwigFlatNode, directive_form) == 136);
65    // In what was `directive_form`'s tail padding: `size_of` is still 144 and
66    // every offset above is unchanged. See the ABI note 5 in twig.h.
67    assert!(offset_of!(TwigFlatNode, container_origin) == 140);
68    // Appended, so every offset above keeps its place; only `size_of` moves
69    // (144 -> 168). See `TWIG_ABI_VERSION` note 6.
70    assert!(offset_of!(TwigFlatNode, marker_span) == 144);
71    assert!(offset_of!(TwigFlatNode, has_marker_span) == 160);
72    // In what was `has_marker_span`'s tail padding: `size_of` stays 168.
73    assert!(offset_of!(TwigFlatNode, checked) == 164);
74
75    assert!(size_of::<TwigKeyVal>() == 32);
76    assert!(offset_of!(TwigKeyVal, key) == 0);
77    assert!(offset_of!(TwigKeyVal, key_len) == 8);
78    assert!(offset_of!(TwigKeyVal, value) == 16);
79    assert!(offset_of!(TwigKeyVal, value_len) == 24);
80};
81
82#[repr(transparent)]
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub struct TwigStatus(pub c_int);
85
86#[allow(dead_code)]
87impl TwigStatus {
88    pub const OK: c_int = 0;
89    pub const INVALID_ARGUMENT: c_int = 1;
90    pub const PARSE_ERROR: c_int = 2;
91    pub const OUT_OF_MEMORY: c_int = 3;
92    pub const UNSUPPORTED_FORMAT: c_int = 4;
93    pub const NOT_FOUND: c_int = 5;
94    pub const AMBIGUOUS: c_int = 6;
95    pub const NOT_EDITABLE: c_int = 7;
96    pub const EDIT_CONFLICT: c_int = 8;
97    pub const UNSAFE_METADATA: c_int = 9;
98    pub const INTERNAL_ERROR: c_int = 255;
99}
100
101#[repr(C)]
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103pub enum TwigFormat {
104    Djot = 1,
105    Markdown = 2,
106    Xml = 3,
107    Html = 4,
108    /// Parses, renders, serializes and edits. The link, image, footnote and
109    /// table gestures report `TWIG_STATUS_UNSUPPORTED_FORMAT` over an
110    /// AsciiDoc document (their AsciiDoc spellings have a shape the gesture
111    /// algorithms cannot write); every other gesture works. What the parser
112    /// leaves unmodelled survives as literal source text.
113    Asciidoc = 5,
114    /// Strict CommonMark, a dialect of `Markdown`: one parser under a
115    /// different preset, `md_flags` laid over it. Writes as Markdown.
116    Commonmark = 6,
117    /// GitHub-Flavored Markdown, a dialect of `Markdown` the same way.
118    Gfm = 7,
119    /// SVG, a dialect of `Xml`: one parser, one serializer, one spelling
120    /// table, under a code of its own. Writes as XML.
121    Svg = 8,
122}
123
124/// `TWIG_FORMAT_RUNTIME_BASE`: the first format code that names a language
125/// registered at runtime rather than compiled in. Reserved ahead of the
126/// registration entry points, which are a later minor; every [`TwigFormat`]
127/// member is below it, and a code in the range is refused with
128/// `TWIG_STATUS_UNSUPPORTED_FORMAT` until they exist.
129pub const TWIG_FORMAT_RUNTIME_BASE: c_int = 4096;
130const _: () = assert!((TwigFormat::Gfm as c_int) < TWIG_FORMAT_RUNTIME_BASE);
131
132/// Markdown extension flags for the `md_flags` bitmask of `twig_parse_ext` and
133/// `twig_editor_create_ext`.
134pub const TWIG_MD_DIRECTIVES: u32 = 1 << 0;
135pub const TWIG_MD_MATH: u32 = 1 << 1;
136pub const TWIG_MD_HTML_ELEMENTS: u32 = 1 << 2;
137pub const TWIG_MD_HIGHLIGHT: u32 = 1 << 3;
138pub const TWIG_MD_HIGHLIGHT_COLORS: u32 = 1 << 4;
139
140pub enum TwigDocument {}
141
142pub enum TwigEditor {}
143
144pub enum TwigBuilder {}
145
146/// C ABI mirror of Zig's `TwigKeyVal` — one `(key, value)` attribute pair. Used
147/// on the read path ([`TwigFlatNode::attrs_ptr`], borrowed) and the write path
148/// (`twig_builder_set_attrs`, copied). A NULL `value` is a bare attribute.
149#[repr(C)]
150#[derive(Clone, Copy, Debug)]
151pub struct TwigKeyVal {
152    pub key: *const u8,
153    pub key_len: usize,
154    pub value: *const u8,
155    pub value_len: usize,
156}
157
158/// C ABI mirror of Zig's `TwigSpan` — a byte range `[start, end)`.
159#[repr(C)]
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub struct TwigSpan {
162    pub start: usize,
163    pub end: usize,
164}
165
166/// C ABI mirror of Zig's `TwigQueryMatch` — one node returned by
167/// `twig_document_query`. `content_span` is only meaningful when
168/// `has_content_span` is non-zero. `kind` is a NUL-terminated node-kind name
169/// in static, library-owned storage (never freed).
170#[repr(C)]
171#[derive(Clone, Copy, Debug)]
172pub struct TwigQueryMatch {
173    pub node_id: u32,
174    pub span: TwigSpan,
175    pub content_span: TwigSpan,
176    pub has_content_span: c_int,
177    pub kind: *const c_char,
178}
179
180/// C ABI mirror of Zig's `TwigWarning` — one thing a conversion would silently
181/// lose. `path` is a slash-separated child-index trail from the analyzed root
182/// (empty for the root itself); `kind` is a NUL-terminated node-kind name in
183/// static, library-owned storage. Both borrow, with the lifetime of the
184/// `twig_document_diagnostics` output array they came from.
185#[repr(C)]
186#[derive(Clone, Copy, Debug)]
187pub struct TwigWarning {
188    pub fidelity: c_int,
189    pub path_ptr: *const u8,
190    pub path_len: usize,
191    pub kind: *const c_char,
192}
193
194/// [`TwigWarning::fidelity`] codes. `FAITHFUL` completes the space and is never
195/// the value of a reported warning — a faithful node is not a warning.
196#[allow(dead_code)]
197pub const TWIG_FIDELITY_FAITHFUL: c_int = 0;
198pub const TWIG_FIDELITY_DEGRADED: c_int = 1;
199pub const TWIG_FIDELITY_DROPPED: c_int = 2;
200pub const TWIG_FIDELITY_ATTRS_DEGRADED: c_int = 3;
201pub const TWIG_FIDELITY_ATTRS_DROPPED: c_int = 4;
202
203/// The sentinel `node_id` for "no such node" in a [`TwigFlatNode`] link field.
204pub const TWIG_NO_NODE: u32 = u32::MAX;
205
206/// C ABI mirror of Zig's `TwigChange` — the byte effect of an edit. `old_span`
207/// is the replaced range in the pre-edit source; `new_span` is the range the
208/// replacement occupies in the post-edit source (same start).
209#[repr(C)]
210#[derive(Clone, Copy, Debug)]
211pub struct TwigChange {
212    pub old_span: TwigSpan,
213    pub new_span: TwigSpan,
214}
215
216/// C ABI mirror of Zig's `TwigFlatNode` — one node of the editor's flat tree
217/// snapshot. Link fields (`parent`/`first_child`/`next_sibling`) are ids or
218/// [`TWIG_NO_NODE`]. `content_span` is meaningful only when `has_content_span`.
219/// `kind` is static, library-owned storage; `text`/`destination` pointers
220/// borrow the current parse's payloads (NULL when the kind carries none).
221/// `head`/`alignment` carry a `row`/`cell` payload, each `-1` for a kind that
222/// has none (see [`TWIG_HEAD_NONE`] / [`TWIG_ALIGN_NONE`]). `name_ptr` is a
223/// generic container's name — an HTML/XML tag or a directive's type — and NULL
224/// for every other kind; `directive_form` is a [`TWIG_DIRECTIVE_NONE`]-defaulted
225/// code for which of the three surface forms it took; `container_origin` is a
226/// [`TWIG_CONTAINER_ORIGIN_NONE`]-defaulted code for whether it was WRITTEN as a
227/// tag or a directive (the two questions are different — see the constants);
228/// `attrs_ptr` points at
229/// `attrs_len` [`TwigKeyVal`]s (NULL/0 when the node has no attributes). Both
230/// borrow the current parse's payloads with the same lifetime as `text_ptr`.
231#[repr(C)]
232#[derive(Clone, Copy, Debug)]
233pub struct TwigFlatNode {
234    pub id: u32,
235    pub parent: u32,
236    pub first_child: u32,
237    pub next_sibling: u32,
238    pub span: TwigSpan,
239    pub content_span: TwigSpan,
240    pub has_content_span: c_int,
241    pub level: u32,
242    pub kind: *const c_char,
243    pub text_ptr: *const u8,
244    pub text_len: usize,
245    pub destination_ptr: *const u8,
246    pub destination_len: usize,
247    pub head: c_int,
248    pub alignment: c_int,
249    pub name_ptr: *const u8,
250    pub name_len: usize,
251    pub attrs_ptr: *const TwigKeyVal,
252    pub attrs_len: usize,
253    pub directive_form: c_int,
254    pub container_origin: c_int,
255    pub marker_span: TwigSpan,
256    pub has_marker_span: c_int,
257    pub checked: c_int,
258}
259
260/// `TwigFlatNode::checked` for a node that is not a `task_list_item`.
261pub const TWIG_TASK_CHECKED_NONE: c_int = -1;
262
263/// `TwigFlatNode::head` for a node that is neither a `row` nor a `cell`.
264pub const TWIG_HEAD_NONE: c_int = -1;
265
266/// `TwigFlatNode::alignment` codes; `NONE` means the node isn't a `cell`.
267/// `NONE` is part of the ABI contract even though `Alignment::from_c` folds it
268/// into its catch-all, so spell it out rather than leaving the -1 a mystery.
269#[allow(dead_code)]
270pub const TWIG_ALIGN_NONE: c_int = -1;
271pub const TWIG_ALIGN_DEFAULT: c_int = 0;
272pub const TWIG_ALIGN_LEFT: c_int = 1;
273pub const TWIG_ALIGN_RIGHT: c_int = 2;
274pub const TWIG_ALIGN_CENTER: c_int = 3;
275
276/// `TwigFlatNode::directive_form` codes — the generic-directives proposal's
277/// three spellings: `:name[x]` (text), `::name{…}` (leaf), `:::name{…}` … `:::`
278/// (container). These double as `twig_builder_add_directive`'s `form` argument.
279/// `NONE` means the node isn't a `directive` at all; like [`TWIG_ALIGN_NONE`] it
280/// is a read-path-only code, never something you can build with.
281#[allow(dead_code)]
282pub const TWIG_DIRECTIVE_NONE: c_int = -1;
283pub const TWIG_DIRECTIVE_TEXT: c_int = 0;
284pub const TWIG_DIRECTIVE_LEAF: c_int = 1;
285pub const TWIG_DIRECTIVE_CONTAINER: c_int = 2;
286
287/// `TwigFlatNode::container_origin` codes — whether a generic container was
288/// WRITTEN as a tag or as a directive.
289///
290/// A different question from [`TWIG_DIRECTIVE_TEXT`] and friends, which say
291/// which of the three directive spellings a node's producer draws. HTML's
292/// parser gives `<div>` and `<span>` a `directive_form` (they are the two tags
293/// djot and Markdown have generic spellings for), so `directive_form` cannot
294/// separate an HTML `<div>` from a Markdown `:::div`: those two agree on kind,
295/// on name and on form, field for field. This is the code that separates them.
296///
297/// `NONE` means nothing recorded an origin — the node isn't a container, or no
298/// parser produced it.
299#[allow(dead_code)]
300pub const TWIG_CONTAINER_ORIGIN_NONE: c_int = -1;
301pub const TWIG_CONTAINER_ORIGIN_ELEMENT: c_int = 0;
302pub const TWIG_CONTAINER_ORIGIN_DIRECTIVE: c_int = 1;
303
304// `op` codes for `twig_editor_table_edit` — mirror of `TwigTableOp` in twig.h.
305pub const TWIG_TABLE_INSERT_ROW: c_int = 0;
306pub const TWIG_TABLE_DELETE_ROW: c_int = 1;
307pub const TWIG_TABLE_INSERT_COLUMN: c_int = 2;
308pub const TWIG_TABLE_DELETE_COLUMN: c_int = 3;
309pub const TWIG_TABLE_SET_ALIGNMENT: c_int = 4;
310pub const TWIG_TABLE_MOVE_ROW: c_int = 5;
311pub const TWIG_TABLE_MOVE_COLUMN: c_int = 6;
312
313unsafe extern "C" {
314    pub fn twig_abi_version() -> u32;
315    pub fn twig_version() -> u32;
316    pub fn twig_version_string() -> *const c_char;
317    pub fn twig_parse(
318        input: *const u8,
319        input_len: usize,
320        format: c_int,
321        out_doc: *mut *mut TwigDocument,
322    ) -> TwigStatus;
323    pub fn twig_parse_ext(
324        input: *const u8,
325        input_len: usize,
326        format: c_int,
327        md_flags: u32,
328        out_doc: *mut *mut TwigDocument,
329    ) -> TwigStatus;
330    pub fn twig_document_destroy(doc: *mut TwigDocument);
331    pub fn twig_document_render_html(
332        doc: *mut TwigDocument,
333        out_ptr: *mut *const u8,
334        out_len: *mut usize,
335    ) -> TwigStatus;
336    pub fn twig_document_serialize(
337        doc: *mut TwigDocument,
338        format: c_int,
339        out_ptr: *mut *const u8,
340        out_len: *mut usize,
341    ) -> TwigStatus;
342    pub fn twig_document_ast_json(
343        doc: *mut TwigDocument,
344        out_ptr: *mut *const u8,
345        out_len: *mut usize,
346    ) -> TwigStatus;
347    pub fn twig_document_query(
348        doc: *mut TwigDocument,
349        selector: *const u8,
350        selector_len: usize,
351        out_ptr: *mut *const TwigQueryMatch,
352        out_len: *mut usize,
353    ) -> TwigStatus;
354    pub fn twig_document_node_span(
355        doc: *mut TwigDocument,
356        node_id: u32,
357        out_span: *mut TwigSpan,
358    ) -> TwigStatus;
359    pub fn twig_document_node_content_span(
360        doc: *mut TwigDocument,
361        node_id: u32,
362        out_span: *mut TwigSpan,
363    ) -> TwigStatus;
364    pub fn twig_document_node_marker_span(
365        doc: *mut TwigDocument,
366        node_id: u32,
367        out_span: *mut TwigSpan,
368    ) -> TwigStatus;
369    pub fn twig_document_attrs_span(
370        doc: *mut TwigDocument,
371        node_id: u32,
372        out_span: *mut TwigSpan,
373    ) -> TwigStatus;
374    pub fn twig_document_line_prefix(
375        doc: *mut TwigDocument,
376        offset: usize,
377        out_span: *mut TwigSpan,
378    ) -> TwigStatus;
379    pub fn twig_document_continuation_prefix(
380        doc: *mut TwigDocument,
381        offset: usize,
382        out_ptr: *mut *const u8,
383        out_len: *mut usize,
384        out_columns: *mut usize,
385    ) -> TwigStatus;
386    pub fn twig_document_blank_line_prefix(
387        doc: *mut TwigDocument,
388        offset: usize,
389        out_ptr: *mut *const u8,
390        out_len: *mut usize,
391        out_columns: *mut usize,
392    ) -> TwigStatus;
393    pub fn twig_document_cell_colspan(
394        doc: *mut TwigDocument,
395        node_id: u32,
396        out_colspan: *mut u32,
397    ) -> TwigStatus;
398    pub fn twig_document_cell_rowspan(
399        doc: *mut TwigDocument,
400        node_id: u32,
401        out_rowspan: *mut u32,
402    ) -> TwigStatus;
403    pub fn twig_document_nodes(
404        doc: *mut TwigDocument,
405        out_ptr: *mut *const TwigFlatNode,
406        out_len: *mut usize,
407    ) -> TwigStatus;
408    pub fn twig_document_definitions(
409        doc: *mut TwigDocument,
410        out_ptr: *mut *const TwigQueryMatch,
411        out_len: *mut usize,
412    ) -> TwigStatus;
413    pub fn twig_document_diagnostics(
414        doc: *mut TwigDocument,
415        format: c_int,
416        out_ptr: *mut *const TwigWarning,
417        out_len: *mut usize,
418    ) -> TwigStatus;
419    pub fn twig_document_children(
420        doc: *mut TwigDocument,
421        node_id: u32,
422        out_ptr: *mut *const TwigQueryMatch,
423        out_len: *mut usize,
424    ) -> TwigStatus;
425    pub fn twig_document_subtree(
426        doc: *mut TwigDocument,
427        node_id: u32,
428        out_ptr: *mut *const TwigFlatNode,
429        out_len: *mut usize,
430    ) -> TwigStatus;
431    pub fn twig_document_node_at(
432        doc: *mut TwigDocument,
433        offset: usize,
434        out_match: *mut TwigQueryMatch,
435    ) -> TwigStatus;
436    pub fn twig_document_nodes_at(
437        doc: *mut TwigDocument,
438        offset: usize,
439        out_ptr: *mut *const TwigQueryMatch,
440        out_len: *mut usize,
441    ) -> TwigStatus;
442    pub fn twig_document_node_at_caret(
443        doc: *mut TwigDocument,
444        offset: usize,
445        out_match: *mut TwigQueryMatch,
446    ) -> TwigStatus;
447    pub fn twig_document_nodes_at_caret(
448        doc: *mut TwigDocument,
449        offset: usize,
450        out_ptr: *mut *const TwigQueryMatch,
451        out_len: *mut usize,
452    ) -> TwigStatus;
453
454    pub fn twig_editor_create(
455        input: *const u8,
456        input_len: usize,
457        format: c_int,
458        out_editor: *mut *mut TwigEditor,
459    ) -> TwigStatus;
460    pub fn twig_editor_create_ext(
461        input: *const u8,
462        input_len: usize,
463        format: c_int,
464        md_flags: u32,
465        out_editor: *mut *mut TwigEditor,
466    ) -> TwigStatus;
467    pub fn twig_editor_destroy(editor: *mut TwigEditor);
468    pub fn twig_editor_replace(
469        editor: *mut TwigEditor,
470        locator: *const u8,
471        locator_len: usize,
472        text: *const u8,
473        text_len: usize,
474    ) -> TwigStatus;
475    pub fn twig_editor_replace_content(
476        editor: *mut TwigEditor,
477        locator: *const u8,
478        locator_len: usize,
479        text: *const u8,
480        text_len: usize,
481    ) -> TwigStatus;
482    pub fn twig_editor_insert_before(
483        editor: *mut TwigEditor,
484        locator: *const u8,
485        locator_len: usize,
486        text: *const u8,
487        text_len: usize,
488    ) -> TwigStatus;
489    pub fn twig_editor_insert_after(
490        editor: *mut TwigEditor,
491        locator: *const u8,
492        locator_len: usize,
493        text: *const u8,
494        text_len: usize,
495    ) -> TwigStatus;
496    pub fn twig_editor_insert_child(
497        editor: *mut TwigEditor,
498        locator: *const u8,
499        locator_len: usize,
500        child_index: usize,
501        text: *const u8,
502        text_len: usize,
503    ) -> TwigStatus;
504    pub fn twig_editor_delete(
505        editor: *mut TwigEditor,
506        locator: *const u8,
507        locator_len: usize,
508    ) -> TwigStatus;
509    pub fn twig_editor_delete_smart(
510        editor: *mut TwigEditor,
511        locator: *const u8,
512        locator_len: usize,
513    ) -> TwigStatus;
514    pub fn twig_editor_unwrap(
515        editor: *mut TwigEditor,
516        locator: *const u8,
517        locator_len: usize,
518    ) -> TwigStatus;
519    pub fn twig_editor_move_before(
520        editor: *mut TwigEditor,
521        locator: *const u8,
522        locator_len: usize,
523        anchor: *const u8,
524        anchor_len: usize,
525    ) -> TwigStatus;
526    pub fn twig_editor_move_after(
527        editor: *mut TwigEditor,
528        locator: *const u8,
529        locator_len: usize,
530        anchor: *const u8,
531        anchor_len: usize,
532    ) -> TwigStatus;
533    pub fn twig_editor_filter(
534        editor: *mut TwigEditor,
535        drop: *const u8,
536        drop_len: usize,
537        keep: *const u8,
538        keep_len: usize,
539        unwrap_kept: c_int,
540    ) -> TwigStatus;
541    pub fn twig_editor_source(
542        editor: *mut TwigEditor,
543        out_ptr: *mut *const u8,
544        out_len: *mut usize,
545    ) -> TwigStatus;
546    pub fn twig_editor_document(
547        editor: *mut TwigEditor,
548        out_doc: *mut *mut TwigDocument,
549    ) -> TwigStatus;
550    pub fn twig_editor_ast_json(
551        editor: *mut TwigEditor,
552        out_ptr: *mut *const u8,
553        out_len: *mut usize,
554    ) -> TwigStatus;
555    pub fn twig_editor_query(
556        editor: *mut TwigEditor,
557        selector: *const u8,
558        selector_len: usize,
559        out_ptr: *mut *const TwigQueryMatch,
560        out_len: *mut usize,
561    ) -> TwigStatus;
562    pub fn twig_editor_edit_range(
563        editor: *mut TwigEditor,
564        start: usize,
565        end: usize,
566        text: *const u8,
567        text_len: usize,
568        out_change: *mut TwigChange,
569    ) -> TwigStatus;
570    pub fn twig_editor_last_change(
571        editor: *mut TwigEditor,
572        out_change: *mut TwigChange,
573    ) -> TwigStatus;
574    pub fn twig_editor_undo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
575    pub fn twig_editor_redo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
576    pub fn twig_editor_coalesce_last(editor: *mut TwigEditor) -> TwigStatus;
577    pub fn twig_editor_revision(editor: *mut TwigEditor) -> u64;
578    pub fn twig_editor_dirty_range(editor: *mut TwigEditor, out_span: *mut TwigSpan) -> TwigStatus;
579    pub fn twig_editor_clear_dirty(editor: *mut TwigEditor) -> TwigStatus;
580    pub fn twig_editor_set_caret_blob(
581        editor: *mut TwigEditor,
582        blob_ptr: *const u8,
583        blob_len: usize,
584    ) -> TwigStatus;
585    pub fn twig_editor_caret_blob(
586        editor: *mut TwigEditor,
587        out_ptr: *mut *const u8,
588        out_len: *mut usize,
589    ) -> TwigStatus;
590    pub fn twig_editor_nodes(
591        editor: *mut TwigEditor,
592        out_ptr: *mut *const TwigFlatNode,
593        out_len: *mut usize,
594    ) -> TwigStatus;
595    pub fn twig_editor_child_spans(
596        editor: *mut TwigEditor,
597        node_id: u32,
598        out_ptr: *mut *const TwigQueryMatch,
599        out_len: *mut usize,
600    ) -> TwigStatus;
601    pub fn twig_editor_subtree(
602        editor: *mut TwigEditor,
603        node_id: u32,
604        out_ptr: *mut *const TwigFlatNode,
605        out_len: *mut usize,
606    ) -> TwigStatus;
607    pub fn twig_editor_node_at(
608        editor: *mut TwigEditor,
609        offset: usize,
610        out_match: *mut TwigQueryMatch,
611    ) -> TwigStatus;
612    pub fn twig_editor_nodes_at(
613        editor: *mut TwigEditor,
614        offset: usize,
615        out_ptr: *mut *const TwigQueryMatch,
616        out_len: *mut usize,
617    ) -> TwigStatus;
618    pub fn twig_editor_wrap_range(
619        editor: *mut TwigEditor,
620        start: usize,
621        end: usize,
622        kind: c_int,
623        out_change: *mut TwigChange,
624    ) -> TwigStatus;
625    pub fn twig_editor_toggle_inline(
626        editor: *mut TwigEditor,
627        start: usize,
628        end: usize,
629        kind: c_int,
630        out_change: *mut TwigChange,
631    ) -> TwigStatus;
632    pub fn twig_editor_set_block(
633        editor: *mut TwigEditor,
634        offset: usize,
635        block_kind: c_int,
636        level: u32,
637        out_change: *mut TwigChange,
638    ) -> TwigStatus;
639    pub fn twig_editor_toggle_block_container(
640        editor: *mut TwigEditor,
641        start: usize,
642        end: usize,
643        container_kind: c_int,
644        out_change: *mut TwigChange,
645    ) -> TwigStatus;
646    pub fn twig_editor_renumber_ordered_lists(
647        editor: *mut TwigEditor,
648        offset: usize,
649        out_change: *mut TwigChange,
650    ) -> TwigStatus;
651    /// Whether `format` can spell `gesture` — the toolbar's gray-out question,
652    /// asked without a document. `kind` is read in that gesture's own kind
653    /// space (a `TWIG_GESTURE_*` doc comment in `twig.h` lists which), and must
654    /// be 0 for a gesture that takes none.
655    pub fn twig_format_supports(
656        format: c_int,
657        gesture: c_int,
658        kind: c_int,
659        out_supported: *mut c_int,
660    ) -> TwigStatus;
661    /// `twig_format_supports` for a document parsed with `md_flags` — the
662    /// question asked of the table the editor actually holds. A Markdown
663    /// extension can widen what may be authored: `==x==` is text under default
664    /// options and a `mark` under `TWIG_MD_HIGHLIGHT`, and
665    /// `TWIG_GESTURE_SET_MARK_COLOR` needs `TWIG_MD_HIGHLIGHT_COLORS` on top.
666    pub fn twig_format_supports_ext(
667        format: c_int,
668        md_flags: u32,
669        gesture: c_int,
670        kind: c_int,
671        out_supported: *mut c_int,
672    ) -> TwigStatus;
673    /// Whether `format` can be authored into at all — 0 for a parse-only
674    /// format. A weaker claim than it looks; see `twig_format_supports`.
675    pub fn twig_format_is_authorable(format: c_int, out_authorable: *mut c_int) -> TwigStatus;
676    pub fn twig_editor_table_edit(
677        editor: *mut TwigEditor,
678        offset: usize,
679        op: c_int,
680        arg: c_int,
681        out_change: *mut TwigChange,
682    ) -> TwigStatus;
683    pub fn twig_editor_insert_table(
684        editor: *mut TwigEditor,
685        offset: usize,
686        rows: usize,
687        cols: usize,
688        out_change: *mut TwigChange,
689    ) -> TwigStatus;
690    pub fn twig_editor_insert_directive(
691        editor: *mut TwigEditor,
692        offset: usize,
693        name_ptr: *const u8,
694        name_len: usize,
695        label_ptr: *const u8,
696        label_len: usize,
697        attrs_ptr: *const TwigKeyVal,
698        attrs_len: usize,
699        out_change: *mut TwigChange,
700    ) -> TwigStatus;
701    pub fn twig_editor_set_block_attrs(
702        editor: *mut TwigEditor,
703        offset: usize,
704        attrs_ptr: *const TwigKeyVal,
705        attrs_len: usize,
706        out_change: *mut TwigChange,
707    ) -> TwigStatus;
708    pub fn twig_editor_wrap_range_attrs(
709        editor: *mut TwigEditor,
710        start: usize,
711        end: usize,
712        attrs_ptr: *const TwigKeyVal,
713        attrs_len: usize,
714        out_change: *mut TwigChange,
715    ) -> TwigStatus;
716    pub fn twig_editor_set_node_attrs(
717        editor: *mut TwigEditor,
718        node_id: u32,
719        attrs_ptr: *const TwigKeyVal,
720        attrs_len: usize,
721        out_change: *mut TwigChange,
722    ) -> TwigStatus;
723    pub fn twig_editor_insert_link(
724        editor: *mut TwigEditor,
725        start: usize,
726        end: usize,
727        destination: *const u8,
728        destination_len: usize,
729        out_change: *mut TwigChange,
730    ) -> TwigStatus;
731    pub fn twig_editor_insert_image(
732        editor: *mut TwigEditor,
733        start: usize,
734        end: usize,
735        destination: *const u8,
736        destination_len: usize,
737        out_change: *mut TwigChange,
738    ) -> TwigStatus;
739
740    pub fn twig_editor_insert_literal(
741        editor: *mut TwigEditor,
742        offset: usize,
743        text: *const u8,
744        text_len: usize,
745        out_change: *mut TwigChange,
746    ) -> TwigStatus;
747
748    pub fn twig_editor_insert_line_break(
749        editor: *mut TwigEditor,
750        offset: usize,
751        out_change: *mut TwigChange,
752    ) -> TwigStatus;
753
754    pub fn twig_editor_insert_thematic_break(
755        editor: *mut TwigEditor,
756        offset: usize,
757        out_change: *mut TwigChange,
758    ) -> TwigStatus;
759
760    pub fn twig_editor_split_block(
761        editor: *mut TwigEditor,
762        offset: usize,
763        out_change: *mut TwigChange,
764    ) -> TwigStatus;
765
766    pub fn twig_editor_join_blocks(
767        editor: *mut TwigEditor,
768        offset: usize,
769        out_change: *mut TwigChange,
770    ) -> TwigStatus;
771    // `language` is OPTIONAL, carried as this ABI's (ptr, len, has_*) triple:
772    // has_language == 0 leaves the fence bare, and "absent" is a different
773    // request from "present but empty".
774    pub fn twig_editor_toggle_code_block(
775        editor: *mut TwigEditor,
776        start: usize,
777        end: usize,
778        language: *const u8,
779        language_len: usize,
780        has_language: c_int,
781        out_change: *mut TwigChange,
782    ) -> TwigStatus;
783    pub fn twig_editor_set_code_language(
784        editor: *mut TwigEditor,
785        offset: usize,
786        language: *const u8,
787        language_len: usize,
788        has_language: c_int,
789        out_change: *mut TwigChange,
790    ) -> TwigStatus;
791    /// Set — or clear, with `has_color == 0` — the colour of the highlight the
792    /// caret at `offset` is inside. Markdown with `TWIG_MD_HIGHLIGHT_COLORS`
793    /// only; the name is a `data-color` value (`red`, `blue`, …).
794    pub fn twig_editor_set_mark_color(
795        editor: *mut TwigEditor,
796        offset: usize,
797        color: *const u8,
798        color_len: usize,
799        has_color: c_int,
800        out_change: *mut TwigChange,
801    ) -> TwigStatus;
802    pub fn twig_editor_toggle_task_item(
803        editor: *mut TwigEditor,
804        offset: usize,
805        out_change: *mut TwigChange,
806    ) -> TwigStatus;
807    pub fn twig_editor_set_task_checked(
808        editor: *mut TwigEditor,
809        offset: usize,
810        checked: c_int,
811        out_change: *mut TwigChange,
812    ) -> TwigStatus;
813    pub fn twig_editor_toggle_task_checked(
814        editor: *mut TwigEditor,
815        offset: usize,
816        out_change: *mut TwigChange,
817    ) -> TwigStatus;
818    pub fn twig_editor_insert_footnote(
819        editor: *mut TwigEditor,
820        offset: usize,
821        label: *const u8,
822        label_len: usize,
823        out_change: *mut TwigChange,
824    ) -> TwigStatus;
825
826    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
827    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
828    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32)
829    -> TwigStatus;
830    pub fn twig_builder_add_text(
831        builder: *mut TwigBuilder,
832        kind: c_int,
833        text: *const u8,
834        text_len: usize,
835        out_id: *mut u32,
836    ) -> TwigStatus;
837    pub fn twig_builder_add_heading(
838        builder: *mut TwigBuilder,
839        level: u32,
840        out_id: *mut u32,
841    ) -> TwigStatus;
842    pub fn twig_builder_add_code_block(
843        builder: *mut TwigBuilder,
844        lang: *const u8,
845        lang_len: usize,
846        has_lang: c_int,
847        text: *const u8,
848        text_len: usize,
849        out_id: *mut u32,
850    ) -> TwigStatus;
851    pub fn twig_builder_add_raw_block(
852        builder: *mut TwigBuilder,
853        format: *const u8,
854        format_len: usize,
855        text: *const u8,
856        text_len: usize,
857        out_id: *mut u32,
858    ) -> TwigStatus;
859    pub fn twig_builder_add_metadata(
860        builder: *mut TwigBuilder,
861        lang: *const u8,
862        lang_len: usize,
863        text: *const u8,
864        text_len: usize,
865        out_id: *mut u32,
866    ) -> TwigStatus;
867    pub fn twig_builder_add_raw_inline(
868        builder: *mut TwigBuilder,
869        format: *const u8,
870        format_len: usize,
871        text: *const u8,
872        text_len: usize,
873        out_id: *mut u32,
874    ) -> TwigStatus;
875    pub fn twig_builder_add_smart_punctuation(
876        builder: *mut TwigBuilder,
877        punct_kind: c_int,
878        text: *const u8,
879        text_len: usize,
880        out_id: *mut u32,
881    ) -> TwigStatus;
882    pub fn twig_builder_add_link(
883        builder: *mut TwigBuilder,
884        destination: *const u8,
885        destination_len: usize,
886        has_destination: c_int,
887        reference: *const u8,
888        reference_len: usize,
889        has_reference: c_int,
890        out_id: *mut u32,
891    ) -> TwigStatus;
892    pub fn twig_builder_add_image(
893        builder: *mut TwigBuilder,
894        destination: *const u8,
895        destination_len: usize,
896        has_destination: c_int,
897        reference: *const u8,
898        reference_len: usize,
899        has_reference: c_int,
900        out_id: *mut u32,
901    ) -> TwigStatus;
902    pub fn twig_builder_add_directive(
903        builder: *mut TwigBuilder,
904        form: c_int,
905        name: *const u8,
906        name_len: usize,
907        out_id: *mut u32,
908    ) -> TwigStatus;
909    pub fn twig_builder_add_element(
910        builder: *mut TwigBuilder,
911        name: *const u8,
912        name_len: usize,
913        out_id: *mut u32,
914    ) -> TwigStatus;
915    pub fn twig_builder_add_processing_instruction(
916        builder: *mut TwigBuilder,
917        target: *const u8,
918        target_len: usize,
919        data: *const u8,
920        data_len: usize,
921        out_id: *mut u32,
922    ) -> TwigStatus;
923    pub fn twig_builder_add_footnote(
924        builder: *mut TwigBuilder,
925        label: *const u8,
926        label_len: usize,
927        out_id: *mut u32,
928    ) -> TwigStatus;
929    pub fn twig_builder_add_citation(
930        builder: *mut TwigBuilder,
931        label: *const u8,
932        label_len: usize,
933        out_id: *mut u32,
934    ) -> TwigStatus;
935    pub fn twig_builder_add_substitution(
936        builder: *mut TwigBuilder,
937        label: *const u8,
938        label_len: usize,
939        out_id: *mut u32,
940    ) -> TwigStatus;
941    pub fn twig_builder_add_reference(
942        builder: *mut TwigBuilder,
943        label: *const u8,
944        label_len: usize,
945        destination: *const u8,
946        destination_len: usize,
947        out_id: *mut u32,
948    ) -> TwigStatus;
949    pub fn twig_builder_add_bullet_list(
950        builder: *mut TwigBuilder,
951        style: c_int,
952        tight: c_int,
953        out_id: *mut u32,
954    ) -> TwigStatus;
955    pub fn twig_builder_add_ordered_list(
956        builder: *mut TwigBuilder,
957        numbering: c_int,
958        delim: c_int,
959        tight: c_int,
960        start: u32,
961        has_start: c_int,
962        out_id: *mut u32,
963    ) -> TwigStatus;
964    pub fn twig_builder_add_task_list(
965        builder: *mut TwigBuilder,
966        tight: c_int,
967        out_id: *mut u32,
968    ) -> TwigStatus;
969    pub fn twig_builder_add_task_list_item(
970        builder: *mut TwigBuilder,
971        checked: c_int,
972        out_id: *mut u32,
973    ) -> TwigStatus;
974    pub fn twig_builder_add_row(
975        builder: *mut TwigBuilder,
976        head: c_int,
977        out_id: *mut u32,
978    ) -> TwigStatus;
979    pub fn twig_builder_add_cell(
980        builder: *mut TwigBuilder,
981        head: c_int,
982        alignment: c_int,
983        out_id: *mut u32,
984    ) -> TwigStatus;
985    pub fn twig_builder_add_cell_spanning(
986        builder: *mut TwigBuilder,
987        head: c_int,
988        alignment: c_int,
989        colspan: u32,
990        rowspan: u32,
991        out_id: *mut u32,
992    ) -> TwigStatus;
993    pub fn twig_builder_set_children(
994        builder: *mut TwigBuilder,
995        parent: u32,
996        ids: *const u32,
997        ids_len: usize,
998    ) -> TwigStatus;
999    pub fn twig_builder_set_attrs(
1000        builder: *mut TwigBuilder,
1001        id: u32,
1002        kvs: *const TwigKeyVal,
1003        kvs_len: usize,
1004    ) -> TwigStatus;
1005    pub fn twig_builder_render_html(
1006        builder: *mut TwigBuilder,
1007        root: u32,
1008        out_ptr: *mut *const u8,
1009        out_len: *mut usize,
1010    ) -> TwigStatus;
1011    pub fn twig_builder_serialize(
1012        builder: *mut TwigBuilder,
1013        root: u32,
1014        format: c_int,
1015        out_ptr: *mut *const u8,
1016        out_len: *mut usize,
1017    ) -> TwigStatus;
1018    pub fn twig_builder_ast_json(
1019        builder: *mut TwigBuilder,
1020        root: u32,
1021        out_ptr: *mut *const u8,
1022        out_len: *mut usize,
1023    ) -> TwigStatus;
1024    pub fn twig_builder_query(
1025        builder: *mut TwigBuilder,
1026        root: u32,
1027        selector: *const u8,
1028        selector_len: usize,
1029        out_ptr: *mut *const TwigQueryMatch,
1030        out_len: *mut usize,
1031    ) -> TwigStatus;
1032}