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