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    pub fn twig_editor_table_edit(
613        editor: *mut TwigEditor,
614        offset: usize,
615        op: c_int,
616        arg: c_int,
617        out_change: *mut TwigChange,
618    ) -> TwigStatus;
619    pub fn twig_editor_insert_link(
620        editor: *mut TwigEditor,
621        start: usize,
622        end: usize,
623        destination: *const u8,
624        destination_len: usize,
625        out_change: *mut TwigChange,
626    ) -> TwigStatus;
627    pub fn twig_editor_insert_image(
628        editor: *mut TwigEditor,
629        start: usize,
630        end: usize,
631        destination: *const u8,
632        destination_len: usize,
633        out_change: *mut TwigChange,
634    ) -> TwigStatus;
635
636    pub fn twig_editor_insert_literal(
637        editor: *mut TwigEditor,
638        offset: usize,
639        text: *const u8,
640        text_len: usize,
641        out_change: *mut TwigChange,
642    ) -> TwigStatus;
643
644    pub fn twig_editor_insert_line_break(
645        editor: *mut TwigEditor,
646        offset: usize,
647        out_change: *mut TwigChange,
648    ) -> TwigStatus;
649
650    pub fn twig_editor_insert_thematic_break(
651        editor: *mut TwigEditor,
652        offset: usize,
653        out_change: *mut TwigChange,
654    ) -> TwigStatus;
655
656    pub fn twig_editor_split_block(
657        editor: *mut TwigEditor,
658        offset: usize,
659        out_change: *mut TwigChange,
660    ) -> TwigStatus;
661    // `language` is OPTIONAL, carried as this ABI's (ptr, len, has_*) triple:
662    // has_language == 0 leaves the fence bare, and "absent" is a different
663    // request from "present but empty".
664    pub fn twig_editor_toggle_code_block(
665        editor: *mut TwigEditor,
666        start: usize,
667        end: usize,
668        language: *const u8,
669        language_len: usize,
670        has_language: c_int,
671        out_change: *mut TwigChange,
672    ) -> TwigStatus;
673    pub fn twig_editor_set_code_language(
674        editor: *mut TwigEditor,
675        offset: usize,
676        language: *const u8,
677        language_len: usize,
678        has_language: c_int,
679        out_change: *mut TwigChange,
680    ) -> TwigStatus;
681    pub fn twig_editor_toggle_task_item(
682        editor: *mut TwigEditor,
683        offset: usize,
684        out_change: *mut TwigChange,
685    ) -> TwigStatus;
686    pub fn twig_editor_set_task_checked(
687        editor: *mut TwigEditor,
688        offset: usize,
689        checked: c_int,
690        out_change: *mut TwigChange,
691    ) -> TwigStatus;
692    pub fn twig_editor_toggle_task_checked(
693        editor: *mut TwigEditor,
694        offset: usize,
695        out_change: *mut TwigChange,
696    ) -> TwigStatus;
697    pub fn twig_editor_insert_footnote(
698        editor: *mut TwigEditor,
699        offset: usize,
700        label: *const u8,
701        label_len: usize,
702        out_change: *mut TwigChange,
703    ) -> TwigStatus;
704
705    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
706    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
707    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32)
708    -> TwigStatus;
709    pub fn twig_builder_add_text(
710        builder: *mut TwigBuilder,
711        kind: c_int,
712        text: *const u8,
713        text_len: usize,
714        out_id: *mut u32,
715    ) -> TwigStatus;
716    pub fn twig_builder_add_heading(
717        builder: *mut TwigBuilder,
718        level: u32,
719        out_id: *mut u32,
720    ) -> TwigStatus;
721    pub fn twig_builder_add_code_block(
722        builder: *mut TwigBuilder,
723        lang: *const u8,
724        lang_len: usize,
725        has_lang: c_int,
726        text: *const u8,
727        text_len: usize,
728        out_id: *mut u32,
729    ) -> TwigStatus;
730    pub fn twig_builder_add_raw_block(
731        builder: *mut TwigBuilder,
732        format: *const u8,
733        format_len: usize,
734        text: *const u8,
735        text_len: usize,
736        out_id: *mut u32,
737    ) -> TwigStatus;
738    pub fn twig_builder_add_metadata(
739        builder: *mut TwigBuilder,
740        lang: *const u8,
741        lang_len: usize,
742        text: *const u8,
743        text_len: usize,
744        out_id: *mut u32,
745    ) -> TwigStatus;
746    pub fn twig_builder_add_raw_inline(
747        builder: *mut TwigBuilder,
748        format: *const u8,
749        format_len: usize,
750        text: *const u8,
751        text_len: usize,
752        out_id: *mut u32,
753    ) -> TwigStatus;
754    pub fn twig_builder_add_smart_punctuation(
755        builder: *mut TwigBuilder,
756        punct_kind: c_int,
757        text: *const u8,
758        text_len: usize,
759        out_id: *mut u32,
760    ) -> TwigStatus;
761    pub fn twig_builder_add_link(
762        builder: *mut TwigBuilder,
763        destination: *const u8,
764        destination_len: usize,
765        has_destination: c_int,
766        reference: *const u8,
767        reference_len: usize,
768        has_reference: c_int,
769        out_id: *mut u32,
770    ) -> TwigStatus;
771    pub fn twig_builder_add_image(
772        builder: *mut TwigBuilder,
773        destination: *const u8,
774        destination_len: usize,
775        has_destination: c_int,
776        reference: *const u8,
777        reference_len: usize,
778        has_reference: c_int,
779        out_id: *mut u32,
780    ) -> TwigStatus;
781    pub fn twig_builder_add_directive(
782        builder: *mut TwigBuilder,
783        form: c_int,
784        name: *const u8,
785        name_len: usize,
786        out_id: *mut u32,
787    ) -> TwigStatus;
788    pub fn twig_builder_add_element(
789        builder: *mut TwigBuilder,
790        name: *const u8,
791        name_len: usize,
792        out_id: *mut u32,
793    ) -> TwigStatus;
794    pub fn twig_builder_add_processing_instruction(
795        builder: *mut TwigBuilder,
796        target: *const u8,
797        target_len: usize,
798        data: *const u8,
799        data_len: usize,
800        out_id: *mut u32,
801    ) -> TwigStatus;
802    pub fn twig_builder_add_footnote(
803        builder: *mut TwigBuilder,
804        label: *const u8,
805        label_len: usize,
806        out_id: *mut u32,
807    ) -> TwigStatus;
808    pub fn twig_builder_add_citation(
809        builder: *mut TwigBuilder,
810        label: *const u8,
811        label_len: usize,
812        out_id: *mut u32,
813    ) -> TwigStatus;
814    pub fn twig_builder_add_substitution(
815        builder: *mut TwigBuilder,
816        label: *const u8,
817        label_len: usize,
818        out_id: *mut u32,
819    ) -> TwigStatus;
820    pub fn twig_builder_add_reference(
821        builder: *mut TwigBuilder,
822        label: *const u8,
823        label_len: usize,
824        destination: *const u8,
825        destination_len: usize,
826        out_id: *mut u32,
827    ) -> TwigStatus;
828    pub fn twig_builder_add_bullet_list(
829        builder: *mut TwigBuilder,
830        style: c_int,
831        tight: c_int,
832        out_id: *mut u32,
833    ) -> TwigStatus;
834    pub fn twig_builder_add_ordered_list(
835        builder: *mut TwigBuilder,
836        numbering: c_int,
837        delim: c_int,
838        tight: c_int,
839        start: u32,
840        has_start: c_int,
841        out_id: *mut u32,
842    ) -> TwigStatus;
843    pub fn twig_builder_add_task_list(
844        builder: *mut TwigBuilder,
845        tight: c_int,
846        out_id: *mut u32,
847    ) -> TwigStatus;
848    pub fn twig_builder_add_task_list_item(
849        builder: *mut TwigBuilder,
850        checked: c_int,
851        out_id: *mut u32,
852    ) -> TwigStatus;
853    pub fn twig_builder_add_row(
854        builder: *mut TwigBuilder,
855        head: c_int,
856        out_id: *mut u32,
857    ) -> TwigStatus;
858    pub fn twig_builder_add_cell(
859        builder: *mut TwigBuilder,
860        head: c_int,
861        alignment: c_int,
862        out_id: *mut u32,
863    ) -> TwigStatus;
864    pub fn twig_builder_add_cell_spanning(
865        builder: *mut TwigBuilder,
866        head: c_int,
867        alignment: c_int,
868        colspan: u32,
869        rowspan: u32,
870        out_id: *mut u32,
871    ) -> TwigStatus;
872    pub fn twig_builder_set_children(
873        builder: *mut TwigBuilder,
874        parent: u32,
875        ids: *const u32,
876        ids_len: usize,
877    ) -> TwigStatus;
878    pub fn twig_builder_set_attrs(
879        builder: *mut TwigBuilder,
880        id: u32,
881        kvs: *const TwigKeyVal,
882        kvs_len: usize,
883    ) -> TwigStatus;
884    pub fn twig_builder_render_html(
885        builder: *mut TwigBuilder,
886        root: u32,
887        out_ptr: *mut *const u8,
888        out_len: *mut usize,
889    ) -> TwigStatus;
890    pub fn twig_builder_serialize(
891        builder: *mut TwigBuilder,
892        root: u32,
893        format: c_int,
894        out_ptr: *mut *const u8,
895        out_len: *mut usize,
896    ) -> TwigStatus;
897    pub fn twig_builder_ast_json(
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_query(
904        builder: *mut TwigBuilder,
905        root: u32,
906        selector: *const u8,
907        selector_len: usize,
908        out_ptr: *mut *const TwigQueryMatch,
909        out_len: *mut usize,
910    ) -> TwigStatus;
911}