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_attrs_span(
350        doc: *mut TwigDocument,
351        node_id: u32,
352        out_span: *mut TwigSpan,
353    ) -> TwigStatus;
354    pub fn twig_document_line_prefix(
355        doc: *mut TwigDocument,
356        offset: usize,
357        out_span: *mut TwigSpan,
358    ) -> TwigStatus;
359    pub fn twig_document_continuation_prefix(
360        doc: *mut TwigDocument,
361        offset: usize,
362        out_ptr: *mut *const u8,
363        out_len: *mut usize,
364        out_columns: *mut usize,
365    ) -> TwigStatus;
366    pub fn twig_document_blank_line_prefix(
367        doc: *mut TwigDocument,
368        offset: usize,
369        out_ptr: *mut *const u8,
370        out_len: *mut usize,
371        out_columns: *mut usize,
372    ) -> TwigStatus;
373    pub fn twig_document_cell_colspan(
374        doc: *mut TwigDocument,
375        node_id: u32,
376        out_colspan: *mut u32,
377    ) -> TwigStatus;
378    pub fn twig_document_cell_rowspan(
379        doc: *mut TwigDocument,
380        node_id: u32,
381        out_rowspan: *mut u32,
382    ) -> TwigStatus;
383    pub fn twig_document_nodes(
384        doc: *mut TwigDocument,
385        out_ptr: *mut *const TwigFlatNode,
386        out_len: *mut usize,
387    ) -> TwigStatus;
388    pub fn twig_document_definitions(
389        doc: *mut TwigDocument,
390        out_ptr: *mut *const TwigQueryMatch,
391        out_len: *mut usize,
392    ) -> TwigStatus;
393    pub fn twig_document_diagnostics(
394        doc: *mut TwigDocument,
395        format: c_int,
396        out_ptr: *mut *const TwigWarning,
397        out_len: *mut usize,
398    ) -> TwigStatus;
399    pub fn twig_document_children(
400        doc: *mut TwigDocument,
401        node_id: u32,
402        out_ptr: *mut *const TwigQueryMatch,
403        out_len: *mut usize,
404    ) -> TwigStatus;
405    pub fn twig_document_subtree(
406        doc: *mut TwigDocument,
407        node_id: u32,
408        out_ptr: *mut *const TwigFlatNode,
409        out_len: *mut usize,
410    ) -> TwigStatus;
411    pub fn twig_document_node_at(
412        doc: *mut TwigDocument,
413        offset: usize,
414        out_match: *mut TwigQueryMatch,
415    ) -> TwigStatus;
416    pub fn twig_document_nodes_at(
417        doc: *mut TwigDocument,
418        offset: usize,
419        out_ptr: *mut *const TwigQueryMatch,
420        out_len: *mut usize,
421    ) -> TwigStatus;
422    pub fn twig_document_node_at_caret(
423        doc: *mut TwigDocument,
424        offset: usize,
425        out_match: *mut TwigQueryMatch,
426    ) -> TwigStatus;
427    pub fn twig_document_nodes_at_caret(
428        doc: *mut TwigDocument,
429        offset: usize,
430        out_ptr: *mut *const TwigQueryMatch,
431        out_len: *mut usize,
432    ) -> TwigStatus;
433
434    pub fn twig_editor_create(
435        input: *const u8,
436        input_len: usize,
437        format: c_int,
438        out_editor: *mut *mut TwigEditor,
439    ) -> TwigStatus;
440    pub fn twig_editor_create_ext(
441        input: *const u8,
442        input_len: usize,
443        format: c_int,
444        md_flags: u32,
445        out_editor: *mut *mut TwigEditor,
446    ) -> TwigStatus;
447    pub fn twig_editor_destroy(editor: *mut TwigEditor);
448    pub fn twig_editor_replace(
449        editor: *mut TwigEditor,
450        locator: *const u8,
451        locator_len: usize,
452        text: *const u8,
453        text_len: usize,
454    ) -> TwigStatus;
455    pub fn twig_editor_replace_content(
456        editor: *mut TwigEditor,
457        locator: *const u8,
458        locator_len: usize,
459        text: *const u8,
460        text_len: usize,
461    ) -> TwigStatus;
462    pub fn twig_editor_insert_before(
463        editor: *mut TwigEditor,
464        locator: *const u8,
465        locator_len: usize,
466        text: *const u8,
467        text_len: usize,
468    ) -> TwigStatus;
469    pub fn twig_editor_insert_after(
470        editor: *mut TwigEditor,
471        locator: *const u8,
472        locator_len: usize,
473        text: *const u8,
474        text_len: usize,
475    ) -> TwigStatus;
476    pub fn twig_editor_insert_child(
477        editor: *mut TwigEditor,
478        locator: *const u8,
479        locator_len: usize,
480        child_index: usize,
481        text: *const u8,
482        text_len: usize,
483    ) -> TwigStatus;
484    pub fn twig_editor_delete(
485        editor: *mut TwigEditor,
486        locator: *const u8,
487        locator_len: usize,
488    ) -> TwigStatus;
489    pub fn twig_editor_delete_smart(
490        editor: *mut TwigEditor,
491        locator: *const u8,
492        locator_len: usize,
493    ) -> TwigStatus;
494    pub fn twig_editor_unwrap(
495        editor: *mut TwigEditor,
496        locator: *const u8,
497        locator_len: usize,
498    ) -> TwigStatus;
499    pub fn twig_editor_filter(
500        editor: *mut TwigEditor,
501        drop: *const u8,
502        drop_len: usize,
503        keep: *const u8,
504        keep_len: usize,
505        unwrap_kept: c_int,
506    ) -> TwigStatus;
507    pub fn twig_editor_source(
508        editor: *mut TwigEditor,
509        out_ptr: *mut *const u8,
510        out_len: *mut usize,
511    ) -> TwigStatus;
512    pub fn twig_editor_document(
513        editor: *mut TwigEditor,
514        out_doc: *mut *mut TwigDocument,
515    ) -> TwigStatus;
516    pub fn twig_editor_ast_json(
517        editor: *mut TwigEditor,
518        out_ptr: *mut *const u8,
519        out_len: *mut usize,
520    ) -> TwigStatus;
521    pub fn twig_editor_query(
522        editor: *mut TwigEditor,
523        selector: *const u8,
524        selector_len: usize,
525        out_ptr: *mut *const TwigQueryMatch,
526        out_len: *mut usize,
527    ) -> TwigStatus;
528    pub fn twig_editor_edit_range(
529        editor: *mut TwigEditor,
530        start: usize,
531        end: usize,
532        text: *const u8,
533        text_len: usize,
534        out_change: *mut TwigChange,
535    ) -> TwigStatus;
536    pub fn twig_editor_last_change(
537        editor: *mut TwigEditor,
538        out_change: *mut TwigChange,
539    ) -> TwigStatus;
540    pub fn twig_editor_undo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
541    pub fn twig_editor_redo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
542    pub fn twig_editor_coalesce_last(editor: *mut TwigEditor) -> TwigStatus;
543    pub fn twig_editor_revision(editor: *mut TwigEditor) -> u64;
544    pub fn twig_editor_dirty_range(editor: *mut TwigEditor, out_span: *mut TwigSpan) -> TwigStatus;
545    pub fn twig_editor_clear_dirty(editor: *mut TwigEditor) -> TwigStatus;
546    pub fn twig_editor_set_caret_blob(
547        editor: *mut TwigEditor,
548        blob_ptr: *const u8,
549        blob_len: usize,
550    ) -> TwigStatus;
551    pub fn twig_editor_caret_blob(
552        editor: *mut TwigEditor,
553        out_ptr: *mut *const u8,
554        out_len: *mut usize,
555    ) -> TwigStatus;
556    pub fn twig_editor_nodes(
557        editor: *mut TwigEditor,
558        out_ptr: *mut *const TwigFlatNode,
559        out_len: *mut usize,
560    ) -> TwigStatus;
561    pub fn twig_editor_child_spans(
562        editor: *mut TwigEditor,
563        node_id: u32,
564        out_ptr: *mut *const TwigQueryMatch,
565        out_len: *mut usize,
566    ) -> TwigStatus;
567    pub fn twig_editor_subtree(
568        editor: *mut TwigEditor,
569        node_id: u32,
570        out_ptr: *mut *const TwigFlatNode,
571        out_len: *mut usize,
572    ) -> TwigStatus;
573    pub fn twig_editor_node_at(
574        editor: *mut TwigEditor,
575        offset: usize,
576        out_match: *mut TwigQueryMatch,
577    ) -> TwigStatus;
578    pub fn twig_editor_nodes_at(
579        editor: *mut TwigEditor,
580        offset: usize,
581        out_ptr: *mut *const TwigQueryMatch,
582        out_len: *mut usize,
583    ) -> TwigStatus;
584    pub fn twig_editor_wrap_range(
585        editor: *mut TwigEditor,
586        start: usize,
587        end: usize,
588        kind: c_int,
589        out_change: *mut TwigChange,
590    ) -> TwigStatus;
591    pub fn twig_editor_toggle_inline(
592        editor: *mut TwigEditor,
593        start: usize,
594        end: usize,
595        kind: c_int,
596        out_change: *mut TwigChange,
597    ) -> TwigStatus;
598    pub fn twig_editor_set_block(
599        editor: *mut TwigEditor,
600        offset: usize,
601        block_kind: c_int,
602        level: u32,
603        out_change: *mut TwigChange,
604    ) -> TwigStatus;
605    pub fn twig_editor_toggle_block_container(
606        editor: *mut TwigEditor,
607        start: usize,
608        end: usize,
609        container_kind: c_int,
610        out_change: *mut TwigChange,
611    ) -> TwigStatus;
612    pub fn twig_editor_renumber_ordered_lists(
613        editor: *mut TwigEditor,
614        offset: usize,
615        out_change: *mut TwigChange,
616    ) -> TwigStatus;
617    /// Whether `format` can spell `gesture` — the toolbar's gray-out question,
618    /// asked without a document. `kind` is read in that gesture's own kind
619    /// space (a `TWIG_GESTURE_*` doc comment in `twig.h` lists which), and must
620    /// be 0 for a gesture that takes none.
621    pub fn twig_format_supports(
622        format: c_int,
623        gesture: c_int,
624        kind: c_int,
625        out_supported: *mut c_int,
626    ) -> TwigStatus;
627    /// Whether `format` can be authored into at all — 0 for a parse-only
628    /// format. A weaker claim than it looks; see `twig_format_supports`.
629    pub fn twig_format_is_authorable(format: c_int, out_authorable: *mut c_int) -> TwigStatus;
630    pub fn twig_editor_table_edit(
631        editor: *mut TwigEditor,
632        offset: usize,
633        op: c_int,
634        arg: c_int,
635        out_change: *mut TwigChange,
636    ) -> TwigStatus;
637    pub fn twig_editor_insert_link(
638        editor: *mut TwigEditor,
639        start: usize,
640        end: usize,
641        destination: *const u8,
642        destination_len: usize,
643        out_change: *mut TwigChange,
644    ) -> TwigStatus;
645    pub fn twig_editor_insert_image(
646        editor: *mut TwigEditor,
647        start: usize,
648        end: usize,
649        destination: *const u8,
650        destination_len: usize,
651        out_change: *mut TwigChange,
652    ) -> TwigStatus;
653
654    pub fn twig_editor_insert_literal(
655        editor: *mut TwigEditor,
656        offset: usize,
657        text: *const u8,
658        text_len: usize,
659        out_change: *mut TwigChange,
660    ) -> TwigStatus;
661
662    pub fn twig_editor_insert_line_break(
663        editor: *mut TwigEditor,
664        offset: usize,
665        out_change: *mut TwigChange,
666    ) -> TwigStatus;
667
668    pub fn twig_editor_insert_thematic_break(
669        editor: *mut TwigEditor,
670        offset: usize,
671        out_change: *mut TwigChange,
672    ) -> TwigStatus;
673
674    pub fn twig_editor_split_block(
675        editor: *mut TwigEditor,
676        offset: usize,
677        out_change: *mut TwigChange,
678    ) -> TwigStatus;
679    // `language` is OPTIONAL, carried as this ABI's (ptr, len, has_*) triple:
680    // has_language == 0 leaves the fence bare, and "absent" is a different
681    // request from "present but empty".
682    pub fn twig_editor_toggle_code_block(
683        editor: *mut TwigEditor,
684        start: usize,
685        end: usize,
686        language: *const u8,
687        language_len: usize,
688        has_language: c_int,
689        out_change: *mut TwigChange,
690    ) -> TwigStatus;
691    pub fn twig_editor_set_code_language(
692        editor: *mut TwigEditor,
693        offset: usize,
694        language: *const u8,
695        language_len: usize,
696        has_language: c_int,
697        out_change: *mut TwigChange,
698    ) -> TwigStatus;
699    pub fn twig_editor_toggle_task_item(
700        editor: *mut TwigEditor,
701        offset: usize,
702        out_change: *mut TwigChange,
703    ) -> TwigStatus;
704    pub fn twig_editor_set_task_checked(
705        editor: *mut TwigEditor,
706        offset: usize,
707        checked: c_int,
708        out_change: *mut TwigChange,
709    ) -> TwigStatus;
710    pub fn twig_editor_toggle_task_checked(
711        editor: *mut TwigEditor,
712        offset: usize,
713        out_change: *mut TwigChange,
714    ) -> TwigStatus;
715    pub fn twig_editor_insert_footnote(
716        editor: *mut TwigEditor,
717        offset: usize,
718        label: *const u8,
719        label_len: usize,
720        out_change: *mut TwigChange,
721    ) -> TwigStatus;
722
723    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
724    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
725    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32)
726    -> TwigStatus;
727    pub fn twig_builder_add_text(
728        builder: *mut TwigBuilder,
729        kind: c_int,
730        text: *const u8,
731        text_len: usize,
732        out_id: *mut u32,
733    ) -> TwigStatus;
734    pub fn twig_builder_add_heading(
735        builder: *mut TwigBuilder,
736        level: u32,
737        out_id: *mut u32,
738    ) -> TwigStatus;
739    pub fn twig_builder_add_code_block(
740        builder: *mut TwigBuilder,
741        lang: *const u8,
742        lang_len: usize,
743        has_lang: c_int,
744        text: *const u8,
745        text_len: usize,
746        out_id: *mut u32,
747    ) -> TwigStatus;
748    pub fn twig_builder_add_raw_block(
749        builder: *mut TwigBuilder,
750        format: *const u8,
751        format_len: usize,
752        text: *const u8,
753        text_len: usize,
754        out_id: *mut u32,
755    ) -> TwigStatus;
756    pub fn twig_builder_add_metadata(
757        builder: *mut TwigBuilder,
758        lang: *const u8,
759        lang_len: usize,
760        text: *const u8,
761        text_len: usize,
762        out_id: *mut u32,
763    ) -> TwigStatus;
764    pub fn twig_builder_add_raw_inline(
765        builder: *mut TwigBuilder,
766        format: *const u8,
767        format_len: usize,
768        text: *const u8,
769        text_len: usize,
770        out_id: *mut u32,
771    ) -> TwigStatus;
772    pub fn twig_builder_add_smart_punctuation(
773        builder: *mut TwigBuilder,
774        punct_kind: c_int,
775        text: *const u8,
776        text_len: usize,
777        out_id: *mut u32,
778    ) -> TwigStatus;
779    pub fn twig_builder_add_link(
780        builder: *mut TwigBuilder,
781        destination: *const u8,
782        destination_len: usize,
783        has_destination: c_int,
784        reference: *const u8,
785        reference_len: usize,
786        has_reference: c_int,
787        out_id: *mut u32,
788    ) -> TwigStatus;
789    pub fn twig_builder_add_image(
790        builder: *mut TwigBuilder,
791        destination: *const u8,
792        destination_len: usize,
793        has_destination: c_int,
794        reference: *const u8,
795        reference_len: usize,
796        has_reference: c_int,
797        out_id: *mut u32,
798    ) -> TwigStatus;
799    pub fn twig_builder_add_directive(
800        builder: *mut TwigBuilder,
801        form: c_int,
802        name: *const u8,
803        name_len: usize,
804        out_id: *mut u32,
805    ) -> TwigStatus;
806    pub fn twig_builder_add_element(
807        builder: *mut TwigBuilder,
808        name: *const u8,
809        name_len: usize,
810        out_id: *mut u32,
811    ) -> TwigStatus;
812    pub fn twig_builder_add_processing_instruction(
813        builder: *mut TwigBuilder,
814        target: *const u8,
815        target_len: usize,
816        data: *const u8,
817        data_len: usize,
818        out_id: *mut u32,
819    ) -> TwigStatus;
820    pub fn twig_builder_add_footnote(
821        builder: *mut TwigBuilder,
822        label: *const u8,
823        label_len: usize,
824        out_id: *mut u32,
825    ) -> TwigStatus;
826    pub fn twig_builder_add_citation(
827        builder: *mut TwigBuilder,
828        label: *const u8,
829        label_len: usize,
830        out_id: *mut u32,
831    ) -> TwigStatus;
832    pub fn twig_builder_add_substitution(
833        builder: *mut TwigBuilder,
834        label: *const u8,
835        label_len: usize,
836        out_id: *mut u32,
837    ) -> TwigStatus;
838    pub fn twig_builder_add_reference(
839        builder: *mut TwigBuilder,
840        label: *const u8,
841        label_len: usize,
842        destination: *const u8,
843        destination_len: usize,
844        out_id: *mut u32,
845    ) -> TwigStatus;
846    pub fn twig_builder_add_bullet_list(
847        builder: *mut TwigBuilder,
848        style: c_int,
849        tight: c_int,
850        out_id: *mut u32,
851    ) -> TwigStatus;
852    pub fn twig_builder_add_ordered_list(
853        builder: *mut TwigBuilder,
854        numbering: c_int,
855        delim: c_int,
856        tight: c_int,
857        start: u32,
858        has_start: c_int,
859        out_id: *mut u32,
860    ) -> TwigStatus;
861    pub fn twig_builder_add_task_list(
862        builder: *mut TwigBuilder,
863        tight: c_int,
864        out_id: *mut u32,
865    ) -> TwigStatus;
866    pub fn twig_builder_add_task_list_item(
867        builder: *mut TwigBuilder,
868        checked: c_int,
869        out_id: *mut u32,
870    ) -> TwigStatus;
871    pub fn twig_builder_add_row(
872        builder: *mut TwigBuilder,
873        head: c_int,
874        out_id: *mut u32,
875    ) -> TwigStatus;
876    pub fn twig_builder_add_cell(
877        builder: *mut TwigBuilder,
878        head: c_int,
879        alignment: c_int,
880        out_id: *mut u32,
881    ) -> TwigStatus;
882    pub fn twig_builder_add_cell_spanning(
883        builder: *mut TwigBuilder,
884        head: c_int,
885        alignment: c_int,
886        colspan: u32,
887        rowspan: u32,
888        out_id: *mut u32,
889    ) -> TwigStatus;
890    pub fn twig_builder_set_children(
891        builder: *mut TwigBuilder,
892        parent: u32,
893        ids: *const u32,
894        ids_len: usize,
895    ) -> TwigStatus;
896    pub fn twig_builder_set_attrs(
897        builder: *mut TwigBuilder,
898        id: u32,
899        kvs: *const TwigKeyVal,
900        kvs_len: usize,
901    ) -> TwigStatus;
902    pub fn twig_builder_render_html(
903        builder: *mut TwigBuilder,
904        root: u32,
905        out_ptr: *mut *const u8,
906        out_len: *mut usize,
907    ) -> TwigStatus;
908    pub fn twig_builder_serialize(
909        builder: *mut TwigBuilder,
910        root: u32,
911        format: c_int,
912        out_ptr: *mut *const u8,
913        out_len: *mut usize,
914    ) -> TwigStatus;
915    pub fn twig_builder_ast_json(
916        builder: *mut TwigBuilder,
917        root: u32,
918        out_ptr: *mut *const u8,
919        out_len: *mut usize,
920    ) -> TwigStatus;
921    pub fn twig_builder_query(
922        builder: *mut TwigBuilder,
923        root: u32,
924        selector: *const u8,
925        selector_len: usize,
926        out_ptr: *mut *const TwigQueryMatch,
927        out_len: *mut usize,
928    ) -> TwigStatus;
929}