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 and renders, but does not serialize: `twig_document_serialize`
109    /// with this code reports `TWIG_STATUS_UNSUPPORTED_FORMAT`, and no editing
110    /// gesture applies to an AsciiDoc document. The parser also covers a SLICE
111    /// of the language rather than all of it; what it doesn't implement
112    /// survives as literal source text.
113    Asciidoc = 5,
114}
115
116/// Markdown extension flags for the `md_flags` bitmask of `twig_parse_ext` and
117/// `twig_editor_create_ext`.
118pub const TWIG_MD_DIRECTIVES: u32 = 1 << 0;
119pub const TWIG_MD_MATH: u32 = 1 << 1;
120pub const TWIG_MD_HTML_ELEMENTS: u32 = 1 << 2;
121
122pub enum TwigDocument {}
123
124pub enum TwigEditor {}
125
126pub enum TwigBuilder {}
127
128/// C ABI mirror of Zig's `TwigKeyVal` — one `(key, value)` attribute pair. Used
129/// on the read path ([`TwigFlatNode::attrs_ptr`], borrowed) and the write path
130/// (`twig_builder_set_attrs`, copied). A NULL `value` is a bare attribute.
131#[repr(C)]
132#[derive(Clone, Copy, Debug)]
133pub struct TwigKeyVal {
134    pub key: *const u8,
135    pub key_len: usize,
136    pub value: *const u8,
137    pub value_len: usize,
138}
139
140/// C ABI mirror of Zig's `TwigSpan` — a byte range `[start, end)`.
141#[repr(C)]
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143pub struct TwigSpan {
144    pub start: usize,
145    pub end: usize,
146}
147
148/// C ABI mirror of Zig's `TwigQueryMatch` — one node returned by
149/// `twig_document_query`. `content_span` is only meaningful when
150/// `has_content_span` is non-zero. `kind` is a NUL-terminated node-kind name
151/// in static, library-owned storage (never freed).
152#[repr(C)]
153#[derive(Clone, Copy, Debug)]
154pub struct TwigQueryMatch {
155    pub node_id: u32,
156    pub span: TwigSpan,
157    pub content_span: TwigSpan,
158    pub has_content_span: c_int,
159    pub kind: *const c_char,
160}
161
162/// C ABI mirror of Zig's `TwigWarning` — one thing a conversion would silently
163/// lose. `path` is a slash-separated child-index trail from the analyzed root
164/// (empty for the root itself); `kind` is a NUL-terminated node-kind name in
165/// static, library-owned storage. Both borrow, with the lifetime of the
166/// `twig_document_diagnostics` output array they came from.
167#[repr(C)]
168#[derive(Clone, Copy, Debug)]
169pub struct TwigWarning {
170    pub fidelity: c_int,
171    pub path_ptr: *const u8,
172    pub path_len: usize,
173    pub kind: *const c_char,
174}
175
176/// [`TwigWarning::fidelity`] codes. `FAITHFUL` completes the space and is never
177/// the value of a reported warning — a faithful node is not a warning.
178#[allow(dead_code)]
179pub const TWIG_FIDELITY_FAITHFUL: c_int = 0;
180pub const TWIG_FIDELITY_DEGRADED: c_int = 1;
181pub const TWIG_FIDELITY_DROPPED: c_int = 2;
182
183/// The sentinel `node_id` for "no such node" in a [`TwigFlatNode`] link field.
184pub const TWIG_NO_NODE: u32 = u32::MAX;
185
186/// C ABI mirror of Zig's `TwigChange` — the byte effect of an edit. `old_span`
187/// is the replaced range in the pre-edit source; `new_span` is the range the
188/// replacement occupies in the post-edit source (same start).
189#[repr(C)]
190#[derive(Clone, Copy, Debug)]
191pub struct TwigChange {
192    pub old_span: TwigSpan,
193    pub new_span: TwigSpan,
194}
195
196/// C ABI mirror of Zig's `TwigFlatNode` — one node of the editor's flat tree
197/// snapshot. Link fields (`parent`/`first_child`/`next_sibling`) are ids or
198/// [`TWIG_NO_NODE`]. `content_span` is meaningful only when `has_content_span`.
199/// `kind` is static, library-owned storage; `text`/`destination` pointers
200/// borrow the current parse's payloads (NULL when the kind carries none).
201/// `head`/`alignment` carry a `row`/`cell` payload, each `-1` for a kind that
202/// has none (see [`TWIG_HEAD_NONE`] / [`TWIG_ALIGN_NONE`]). `name_ptr` is a
203/// generic container's name — an HTML/XML tag or a directive's type — and NULL
204/// for every other kind; `directive_form` is a [`TWIG_DIRECTIVE_NONE`]-defaulted
205/// code for which of the three surface forms it took; `container_origin` is a
206/// [`TWIG_CONTAINER_ORIGIN_NONE`]-defaulted code for whether it was WRITTEN as a
207/// tag or a directive (the two questions are different — see the constants);
208/// `attrs_ptr` points at
209/// `attrs_len` [`TwigKeyVal`]s (NULL/0 when the node has no attributes). Both
210/// borrow the current parse's payloads with the same lifetime as `text_ptr`.
211#[repr(C)]
212#[derive(Clone, Copy, Debug)]
213pub struct TwigFlatNode {
214    pub id: u32,
215    pub parent: u32,
216    pub first_child: u32,
217    pub next_sibling: u32,
218    pub span: TwigSpan,
219    pub content_span: TwigSpan,
220    pub has_content_span: c_int,
221    pub level: u32,
222    pub kind: *const c_char,
223    pub text_ptr: *const u8,
224    pub text_len: usize,
225    pub destination_ptr: *const u8,
226    pub destination_len: usize,
227    pub head: c_int,
228    pub alignment: c_int,
229    pub name_ptr: *const u8,
230    pub name_len: usize,
231    pub attrs_ptr: *const TwigKeyVal,
232    pub attrs_len: usize,
233    pub directive_form: c_int,
234    pub container_origin: c_int,
235    pub marker_span: TwigSpan,
236    pub has_marker_span: c_int,
237    pub checked: c_int,
238}
239
240/// `TwigFlatNode::checked` for a node that is not a `task_list_item`.
241pub const TWIG_TASK_CHECKED_NONE: c_int = -1;
242
243/// `TwigFlatNode::head` for a node that is neither a `row` nor a `cell`.
244pub const TWIG_HEAD_NONE: c_int = -1;
245
246/// `TwigFlatNode::alignment` codes; `NONE` means the node isn't a `cell`.
247/// `NONE` is part of the ABI contract even though `Alignment::from_c` folds it
248/// into its catch-all, so spell it out rather than leaving the -1 a mystery.
249#[allow(dead_code)]
250pub const TWIG_ALIGN_NONE: c_int = -1;
251pub const TWIG_ALIGN_DEFAULT: c_int = 0;
252pub const TWIG_ALIGN_LEFT: c_int = 1;
253pub const TWIG_ALIGN_RIGHT: c_int = 2;
254pub const TWIG_ALIGN_CENTER: c_int = 3;
255
256/// `TwigFlatNode::directive_form` codes — the generic-directives proposal's
257/// three spellings: `:name[x]` (text), `::name{…}` (leaf), `:::name{…}` … `:::`
258/// (container). These double as `twig_builder_add_directive`'s `form` argument.
259/// `NONE` means the node isn't a `directive` at all; like [`TWIG_ALIGN_NONE`] it
260/// is a read-path-only code, never something you can build with.
261#[allow(dead_code)]
262pub const TWIG_DIRECTIVE_NONE: c_int = -1;
263pub const TWIG_DIRECTIVE_TEXT: c_int = 0;
264pub const TWIG_DIRECTIVE_LEAF: c_int = 1;
265pub const TWIG_DIRECTIVE_CONTAINER: c_int = 2;
266
267/// `TwigFlatNode::container_origin` codes — whether a generic container was
268/// WRITTEN as a tag or as a directive.
269///
270/// A different question from [`TWIG_DIRECTIVE_TEXT`] and friends, which say
271/// which of the three directive spellings a node's producer draws. HTML's
272/// parser gives `<div>` and `<span>` a `directive_form` (they are the two tags
273/// djot and Markdown have generic spellings for), so `directive_form` cannot
274/// separate an HTML `<div>` from a Markdown `:::div`: those two agree on kind,
275/// on name and on form, field for field. This is the code that separates them.
276///
277/// `NONE` means nothing recorded an origin — the node isn't a container, or no
278/// parser produced it.
279#[allow(dead_code)]
280pub const TWIG_CONTAINER_ORIGIN_NONE: c_int = -1;
281pub const TWIG_CONTAINER_ORIGIN_ELEMENT: c_int = 0;
282pub const TWIG_CONTAINER_ORIGIN_DIRECTIVE: c_int = 1;
283
284// `op` codes for `twig_editor_table_edit` — mirror of `TwigTableOp` in twig.h.
285pub const TWIG_TABLE_INSERT_ROW: c_int = 0;
286pub const TWIG_TABLE_DELETE_ROW: c_int = 1;
287pub const TWIG_TABLE_INSERT_COLUMN: c_int = 2;
288pub const TWIG_TABLE_DELETE_COLUMN: c_int = 3;
289pub const TWIG_TABLE_SET_ALIGNMENT: c_int = 4;
290pub const TWIG_TABLE_MOVE_ROW: c_int = 5;
291pub const TWIG_TABLE_MOVE_COLUMN: c_int = 6;
292
293unsafe extern "C" {
294    pub fn twig_abi_version() -> u32;
295    pub fn twig_version() -> u32;
296    pub fn twig_version_string() -> *const c_char;
297    pub fn twig_parse(
298        input: *const u8,
299        input_len: usize,
300        format: c_int,
301        out_doc: *mut *mut TwigDocument,
302    ) -> TwigStatus;
303    pub fn twig_parse_ext(
304        input: *const u8,
305        input_len: usize,
306        format: c_int,
307        md_flags: u32,
308        out_doc: *mut *mut TwigDocument,
309    ) -> TwigStatus;
310    pub fn twig_document_destroy(doc: *mut TwigDocument);
311    pub fn twig_document_render_html(
312        doc: *mut TwigDocument,
313        out_ptr: *mut *const u8,
314        out_len: *mut usize,
315    ) -> TwigStatus;
316    pub fn twig_document_serialize(
317        doc: *mut TwigDocument,
318        format: c_int,
319        out_ptr: *mut *const u8,
320        out_len: *mut usize,
321    ) -> TwigStatus;
322    pub fn twig_document_ast_json(
323        doc: *mut TwigDocument,
324        out_ptr: *mut *const u8,
325        out_len: *mut usize,
326    ) -> TwigStatus;
327    pub fn twig_document_query(
328        doc: *mut TwigDocument,
329        selector: *const u8,
330        selector_len: usize,
331        out_ptr: *mut *const TwigQueryMatch,
332        out_len: *mut usize,
333    ) -> TwigStatus;
334    pub fn twig_document_node_span(
335        doc: *mut TwigDocument,
336        node_id: u32,
337        out_span: *mut TwigSpan,
338    ) -> TwigStatus;
339    pub fn twig_document_node_content_span(
340        doc: *mut TwigDocument,
341        node_id: u32,
342        out_span: *mut TwigSpan,
343    ) -> TwigStatus;
344    pub fn twig_document_node_marker_span(
345        doc: *mut TwigDocument,
346        node_id: u32,
347        out_span: *mut TwigSpan,
348    ) -> TwigStatus;
349    pub fn twig_document_line_prefix(
350        doc: *mut TwigDocument,
351        offset: usize,
352        out_span: *mut TwigSpan,
353    ) -> TwigStatus;
354    pub fn twig_document_continuation_prefix(
355        doc: *mut TwigDocument,
356        offset: usize,
357        out_ptr: *mut *const u8,
358        out_len: *mut usize,
359        out_columns: *mut usize,
360    ) -> TwigStatus;
361    pub fn twig_document_blank_line_prefix(
362        doc: *mut TwigDocument,
363        offset: usize,
364        out_ptr: *mut *const u8,
365        out_len: *mut usize,
366        out_columns: *mut usize,
367    ) -> TwigStatus;
368    pub fn twig_document_cell_colspan(
369        doc: *mut TwigDocument,
370        node_id: u32,
371        out_colspan: *mut u32,
372    ) -> TwigStatus;
373    pub fn twig_document_cell_rowspan(
374        doc: *mut TwigDocument,
375        node_id: u32,
376        out_rowspan: *mut u32,
377    ) -> TwigStatus;
378    pub fn twig_document_nodes(
379        doc: *mut TwigDocument,
380        out_ptr: *mut *const TwigFlatNode,
381        out_len: *mut usize,
382    ) -> TwigStatus;
383    pub fn twig_document_definitions(
384        doc: *mut TwigDocument,
385        out_ptr: *mut *const TwigQueryMatch,
386        out_len: *mut usize,
387    ) -> TwigStatus;
388    pub fn twig_document_diagnostics(
389        doc: *mut TwigDocument,
390        format: c_int,
391        out_ptr: *mut *const TwigWarning,
392        out_len: *mut usize,
393    ) -> TwigStatus;
394    pub fn twig_document_children(
395        doc: *mut TwigDocument,
396        node_id: u32,
397        out_ptr: *mut *const TwigQueryMatch,
398        out_len: *mut usize,
399    ) -> TwigStatus;
400    pub fn twig_document_subtree(
401        doc: *mut TwigDocument,
402        node_id: u32,
403        out_ptr: *mut *const TwigFlatNode,
404        out_len: *mut usize,
405    ) -> TwigStatus;
406    pub fn twig_document_node_at(
407        doc: *mut TwigDocument,
408        offset: usize,
409        out_match: *mut TwigQueryMatch,
410    ) -> TwigStatus;
411    pub fn twig_document_nodes_at(
412        doc: *mut TwigDocument,
413        offset: usize,
414        out_ptr: *mut *const TwigQueryMatch,
415        out_len: *mut usize,
416    ) -> TwigStatus;
417    pub fn twig_document_node_at_caret(
418        doc: *mut TwigDocument,
419        offset: usize,
420        out_match: *mut TwigQueryMatch,
421    ) -> TwigStatus;
422    pub fn twig_document_nodes_at_caret(
423        doc: *mut TwigDocument,
424        offset: usize,
425        out_ptr: *mut *const TwigQueryMatch,
426        out_len: *mut usize,
427    ) -> TwigStatus;
428
429    pub fn twig_editor_create(
430        input: *const u8,
431        input_len: usize,
432        format: c_int,
433        out_editor: *mut *mut TwigEditor,
434    ) -> TwigStatus;
435    pub fn twig_editor_create_ext(
436        input: *const u8,
437        input_len: usize,
438        format: c_int,
439        md_flags: u32,
440        out_editor: *mut *mut TwigEditor,
441    ) -> TwigStatus;
442    pub fn twig_editor_destroy(editor: *mut TwigEditor);
443    pub fn twig_editor_replace(
444        editor: *mut TwigEditor,
445        locator: *const u8,
446        locator_len: usize,
447        text: *const u8,
448        text_len: usize,
449    ) -> TwigStatus;
450    pub fn twig_editor_replace_content(
451        editor: *mut TwigEditor,
452        locator: *const u8,
453        locator_len: usize,
454        text: *const u8,
455        text_len: usize,
456    ) -> TwigStatus;
457    pub fn twig_editor_insert_before(
458        editor: *mut TwigEditor,
459        locator: *const u8,
460        locator_len: usize,
461        text: *const u8,
462        text_len: usize,
463    ) -> TwigStatus;
464    pub fn twig_editor_insert_after(
465        editor: *mut TwigEditor,
466        locator: *const u8,
467        locator_len: usize,
468        text: *const u8,
469        text_len: usize,
470    ) -> TwigStatus;
471    pub fn twig_editor_insert_child(
472        editor: *mut TwigEditor,
473        locator: *const u8,
474        locator_len: usize,
475        child_index: usize,
476        text: *const u8,
477        text_len: usize,
478    ) -> TwigStatus;
479    pub fn twig_editor_delete(
480        editor: *mut TwigEditor,
481        locator: *const u8,
482        locator_len: usize,
483    ) -> TwigStatus;
484    pub fn twig_editor_delete_smart(
485        editor: *mut TwigEditor,
486        locator: *const u8,
487        locator_len: usize,
488    ) -> TwigStatus;
489    pub fn twig_editor_unwrap(
490        editor: *mut TwigEditor,
491        locator: *const u8,
492        locator_len: usize,
493    ) -> TwigStatus;
494    pub fn twig_editor_filter(
495        editor: *mut TwigEditor,
496        drop: *const u8,
497        drop_len: usize,
498        keep: *const u8,
499        keep_len: usize,
500        unwrap_kept: c_int,
501    ) -> TwigStatus;
502    pub fn twig_editor_source(
503        editor: *mut TwigEditor,
504        out_ptr: *mut *const u8,
505        out_len: *mut usize,
506    ) -> TwigStatus;
507    pub fn twig_editor_document(
508        editor: *mut TwigEditor,
509        out_doc: *mut *mut TwigDocument,
510    ) -> TwigStatus;
511    pub fn twig_editor_ast_json(
512        editor: *mut TwigEditor,
513        out_ptr: *mut *const u8,
514        out_len: *mut usize,
515    ) -> TwigStatus;
516    pub fn twig_editor_query(
517        editor: *mut TwigEditor,
518        selector: *const u8,
519        selector_len: usize,
520        out_ptr: *mut *const TwigQueryMatch,
521        out_len: *mut usize,
522    ) -> TwigStatus;
523    pub fn twig_editor_edit_range(
524        editor: *mut TwigEditor,
525        start: usize,
526        end: usize,
527        text: *const u8,
528        text_len: usize,
529        out_change: *mut TwigChange,
530    ) -> TwigStatus;
531    pub fn twig_editor_last_change(
532        editor: *mut TwigEditor,
533        out_change: *mut TwigChange,
534    ) -> TwigStatus;
535    pub fn twig_editor_undo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
536    pub fn twig_editor_redo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
537    pub fn twig_editor_coalesce_last(editor: *mut TwigEditor) -> TwigStatus;
538    pub fn twig_editor_revision(editor: *mut TwigEditor) -> u64;
539    pub fn twig_editor_dirty_range(editor: *mut TwigEditor, out_span: *mut TwigSpan) -> TwigStatus;
540    pub fn twig_editor_clear_dirty(editor: *mut TwigEditor) -> TwigStatus;
541    pub fn twig_editor_set_caret_blob(
542        editor: *mut TwigEditor,
543        blob_ptr: *const u8,
544        blob_len: usize,
545    ) -> TwigStatus;
546    pub fn twig_editor_caret_blob(
547        editor: *mut TwigEditor,
548        out_ptr: *mut *const u8,
549        out_len: *mut usize,
550    ) -> TwigStatus;
551    pub fn twig_editor_nodes(
552        editor: *mut TwigEditor,
553        out_ptr: *mut *const TwigFlatNode,
554        out_len: *mut usize,
555    ) -> TwigStatus;
556    pub fn twig_editor_child_spans(
557        editor: *mut TwigEditor,
558        node_id: u32,
559        out_ptr: *mut *const TwigQueryMatch,
560        out_len: *mut usize,
561    ) -> TwigStatus;
562    pub fn twig_editor_subtree(
563        editor: *mut TwigEditor,
564        node_id: u32,
565        out_ptr: *mut *const TwigFlatNode,
566        out_len: *mut usize,
567    ) -> TwigStatus;
568    pub fn twig_editor_node_at(
569        editor: *mut TwigEditor,
570        offset: usize,
571        out_match: *mut TwigQueryMatch,
572    ) -> TwigStatus;
573    pub fn twig_editor_nodes_at(
574        editor: *mut TwigEditor,
575        offset: usize,
576        out_ptr: *mut *const TwigQueryMatch,
577        out_len: *mut usize,
578    ) -> TwigStatus;
579    pub fn twig_editor_wrap_range(
580        editor: *mut TwigEditor,
581        start: usize,
582        end: usize,
583        kind: c_int,
584        out_change: *mut TwigChange,
585    ) -> TwigStatus;
586    pub fn twig_editor_toggle_inline(
587        editor: *mut TwigEditor,
588        start: usize,
589        end: usize,
590        kind: c_int,
591        out_change: *mut TwigChange,
592    ) -> TwigStatus;
593    pub fn twig_editor_set_block(
594        editor: *mut TwigEditor,
595        offset: usize,
596        block_kind: c_int,
597        level: u32,
598        out_change: *mut TwigChange,
599    ) -> TwigStatus;
600    pub fn twig_editor_toggle_block_container(
601        editor: *mut TwigEditor,
602        start: usize,
603        end: usize,
604        container_kind: c_int,
605        out_change: *mut TwigChange,
606    ) -> TwigStatus;
607    pub fn twig_editor_renumber_ordered_lists(
608        editor: *mut TwigEditor,
609        offset: usize,
610        out_change: *mut TwigChange,
611    ) -> TwigStatus;
612    /// Whether `format` can spell `gesture` — the toolbar's gray-out question,
613    /// asked without a document. `kind` is read in that gesture's own kind
614    /// space (a `TWIG_GESTURE_*` doc comment in `twig.h` lists which), and must
615    /// be 0 for a gesture that takes none.
616    pub fn twig_format_supports(
617        format: c_int,
618        gesture: c_int,
619        kind: c_int,
620        out_supported: *mut c_int,
621    ) -> TwigStatus;
622    /// Whether `format` can be authored into at all — 0 for a parse-only
623    /// format. A weaker claim than it looks; see `twig_format_supports`.
624    pub fn twig_format_is_authorable(format: c_int, out_authorable: *mut c_int) -> TwigStatus;
625    pub fn twig_editor_table_edit(
626        editor: *mut TwigEditor,
627        offset: usize,
628        op: c_int,
629        arg: c_int,
630        out_change: *mut TwigChange,
631    ) -> TwigStatus;
632    pub fn twig_editor_insert_link(
633        editor: *mut TwigEditor,
634        start: usize,
635        end: usize,
636        destination: *const u8,
637        destination_len: usize,
638        out_change: *mut TwigChange,
639    ) -> TwigStatus;
640    pub fn twig_editor_insert_image(
641        editor: *mut TwigEditor,
642        start: usize,
643        end: usize,
644        destination: *const u8,
645        destination_len: usize,
646        out_change: *mut TwigChange,
647    ) -> TwigStatus;
648
649    pub fn twig_editor_insert_literal(
650        editor: *mut TwigEditor,
651        offset: usize,
652        text: *const u8,
653        text_len: usize,
654        out_change: *mut TwigChange,
655    ) -> TwigStatus;
656
657    pub fn twig_editor_insert_line_break(
658        editor: *mut TwigEditor,
659        offset: usize,
660        out_change: *mut TwigChange,
661    ) -> TwigStatus;
662
663    pub fn twig_editor_insert_thematic_break(
664        editor: *mut TwigEditor,
665        offset: usize,
666        out_change: *mut TwigChange,
667    ) -> TwigStatus;
668
669    pub fn twig_editor_split_block(
670        editor: *mut TwigEditor,
671        offset: usize,
672        out_change: *mut TwigChange,
673    ) -> TwigStatus;
674    // `language` is OPTIONAL, carried as this ABI's (ptr, len, has_*) triple:
675    // has_language == 0 leaves the fence bare, and "absent" is a different
676    // request from "present but empty".
677    pub fn twig_editor_toggle_code_block(
678        editor: *mut TwigEditor,
679        start: usize,
680        end: usize,
681        language: *const u8,
682        language_len: usize,
683        has_language: c_int,
684        out_change: *mut TwigChange,
685    ) -> TwigStatus;
686    pub fn twig_editor_set_code_language(
687        editor: *mut TwigEditor,
688        offset: usize,
689        language: *const u8,
690        language_len: usize,
691        has_language: c_int,
692        out_change: *mut TwigChange,
693    ) -> TwigStatus;
694    pub fn twig_editor_toggle_task_item(
695        editor: *mut TwigEditor,
696        offset: usize,
697        out_change: *mut TwigChange,
698    ) -> TwigStatus;
699    pub fn twig_editor_set_task_checked(
700        editor: *mut TwigEditor,
701        offset: usize,
702        checked: c_int,
703        out_change: *mut TwigChange,
704    ) -> TwigStatus;
705    pub fn twig_editor_toggle_task_checked(
706        editor: *mut TwigEditor,
707        offset: usize,
708        out_change: *mut TwigChange,
709    ) -> TwigStatus;
710    pub fn twig_editor_insert_footnote(
711        editor: *mut TwigEditor,
712        offset: usize,
713        label: *const u8,
714        label_len: usize,
715        out_change: *mut TwigChange,
716    ) -> TwigStatus;
717
718    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
719    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
720    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32)
721    -> TwigStatus;
722    pub fn twig_builder_add_text(
723        builder: *mut TwigBuilder,
724        kind: c_int,
725        text: *const u8,
726        text_len: usize,
727        out_id: *mut u32,
728    ) -> TwigStatus;
729    pub fn twig_builder_add_heading(
730        builder: *mut TwigBuilder,
731        level: u32,
732        out_id: *mut u32,
733    ) -> TwigStatus;
734    pub fn twig_builder_add_code_block(
735        builder: *mut TwigBuilder,
736        lang: *const u8,
737        lang_len: usize,
738        has_lang: c_int,
739        text: *const u8,
740        text_len: usize,
741        out_id: *mut u32,
742    ) -> TwigStatus;
743    pub fn twig_builder_add_raw_block(
744        builder: *mut TwigBuilder,
745        format: *const u8,
746        format_len: usize,
747        text: *const u8,
748        text_len: usize,
749        out_id: *mut u32,
750    ) -> TwigStatus;
751    pub fn twig_builder_add_metadata(
752        builder: *mut TwigBuilder,
753        lang: *const u8,
754        lang_len: usize,
755        text: *const u8,
756        text_len: usize,
757        out_id: *mut u32,
758    ) -> TwigStatus;
759    pub fn twig_builder_add_raw_inline(
760        builder: *mut TwigBuilder,
761        format: *const u8,
762        format_len: usize,
763        text: *const u8,
764        text_len: usize,
765        out_id: *mut u32,
766    ) -> TwigStatus;
767    pub fn twig_builder_add_smart_punctuation(
768        builder: *mut TwigBuilder,
769        punct_kind: c_int,
770        text: *const u8,
771        text_len: usize,
772        out_id: *mut u32,
773    ) -> TwigStatus;
774    pub fn twig_builder_add_link(
775        builder: *mut TwigBuilder,
776        destination: *const u8,
777        destination_len: usize,
778        has_destination: c_int,
779        reference: *const u8,
780        reference_len: usize,
781        has_reference: c_int,
782        out_id: *mut u32,
783    ) -> TwigStatus;
784    pub fn twig_builder_add_image(
785        builder: *mut TwigBuilder,
786        destination: *const u8,
787        destination_len: usize,
788        has_destination: c_int,
789        reference: *const u8,
790        reference_len: usize,
791        has_reference: c_int,
792        out_id: *mut u32,
793    ) -> TwigStatus;
794    pub fn twig_builder_add_directive(
795        builder: *mut TwigBuilder,
796        form: c_int,
797        name: *const u8,
798        name_len: usize,
799        out_id: *mut u32,
800    ) -> TwigStatus;
801    pub fn twig_builder_add_element(
802        builder: *mut TwigBuilder,
803        name: *const u8,
804        name_len: usize,
805        out_id: *mut u32,
806    ) -> TwigStatus;
807    pub fn twig_builder_add_processing_instruction(
808        builder: *mut TwigBuilder,
809        target: *const u8,
810        target_len: usize,
811        data: *const u8,
812        data_len: usize,
813        out_id: *mut u32,
814    ) -> TwigStatus;
815    pub fn twig_builder_add_footnote(
816        builder: *mut TwigBuilder,
817        label: *const u8,
818        label_len: usize,
819        out_id: *mut u32,
820    ) -> TwigStatus;
821    pub fn twig_builder_add_citation(
822        builder: *mut TwigBuilder,
823        label: *const u8,
824        label_len: usize,
825        out_id: *mut u32,
826    ) -> TwigStatus;
827    pub fn twig_builder_add_substitution(
828        builder: *mut TwigBuilder,
829        label: *const u8,
830        label_len: usize,
831        out_id: *mut u32,
832    ) -> TwigStatus;
833    pub fn twig_builder_add_reference(
834        builder: *mut TwigBuilder,
835        label: *const u8,
836        label_len: usize,
837        destination: *const u8,
838        destination_len: usize,
839        out_id: *mut u32,
840    ) -> TwigStatus;
841    pub fn twig_builder_add_bullet_list(
842        builder: *mut TwigBuilder,
843        style: c_int,
844        tight: c_int,
845        out_id: *mut u32,
846    ) -> TwigStatus;
847    pub fn twig_builder_add_ordered_list(
848        builder: *mut TwigBuilder,
849        numbering: c_int,
850        delim: c_int,
851        tight: c_int,
852        start: u32,
853        has_start: c_int,
854        out_id: *mut u32,
855    ) -> TwigStatus;
856    pub fn twig_builder_add_task_list(
857        builder: *mut TwigBuilder,
858        tight: c_int,
859        out_id: *mut u32,
860    ) -> TwigStatus;
861    pub fn twig_builder_add_task_list_item(
862        builder: *mut TwigBuilder,
863        checked: c_int,
864        out_id: *mut u32,
865    ) -> TwigStatus;
866    pub fn twig_builder_add_row(
867        builder: *mut TwigBuilder,
868        head: c_int,
869        out_id: *mut u32,
870    ) -> TwigStatus;
871    pub fn twig_builder_add_cell(
872        builder: *mut TwigBuilder,
873        head: c_int,
874        alignment: c_int,
875        out_id: *mut u32,
876    ) -> TwigStatus;
877    pub fn twig_builder_add_cell_spanning(
878        builder: *mut TwigBuilder,
879        head: c_int,
880        alignment: c_int,
881        colspan: u32,
882        rowspan: u32,
883        out_id: *mut u32,
884    ) -> TwigStatus;
885    pub fn twig_builder_set_children(
886        builder: *mut TwigBuilder,
887        parent: u32,
888        ids: *const u32,
889        ids_len: usize,
890    ) -> TwigStatus;
891    pub fn twig_builder_set_attrs(
892        builder: *mut TwigBuilder,
893        id: u32,
894        kvs: *const TwigKeyVal,
895        kvs_len: usize,
896    ) -> TwigStatus;
897    pub fn twig_builder_render_html(
898        builder: *mut TwigBuilder,
899        root: u32,
900        out_ptr: *mut *const u8,
901        out_len: *mut usize,
902    ) -> TwigStatus;
903    pub fn twig_builder_serialize(
904        builder: *mut TwigBuilder,
905        root: u32,
906        format: c_int,
907        out_ptr: *mut *const u8,
908        out_len: *mut usize,
909    ) -> TwigStatus;
910    pub fn twig_builder_ast_json(
911        builder: *mut TwigBuilder,
912        root: u32,
913        out_ptr: *mut *const u8,
914        out_len: *mut usize,
915    ) -> TwigStatus;
916    pub fn twig_builder_query(
917        builder: *mut TwigBuilder,
918        root: u32,
919        selector: *const u8,
920        selector_len: usize,
921        out_ptr: *mut *const TwigQueryMatch,
922        out_len: *mut usize,
923    ) -> TwigStatus;
924}