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    /// `twig_format_supports` for a document parsed with `md_flags` — the
630    /// question asked of the table the editor actually holds. A Markdown
631    /// extension can widen what may be authored: `==x==` is text under default
632    /// options and a `mark` under `TWIG_MD_HIGHLIGHT`, and
633    /// `TWIG_GESTURE_SET_MARK_COLOR` needs `TWIG_MD_HIGHLIGHT_COLORS` on top.
634    pub fn twig_format_supports_ext(
635        format: c_int,
636        md_flags: u32,
637        gesture: c_int,
638        kind: c_int,
639        out_supported: *mut c_int,
640    ) -> TwigStatus;
641    /// Whether `format` can be authored into at all — 0 for a parse-only
642    /// format. A weaker claim than it looks; see `twig_format_supports`.
643    pub fn twig_format_is_authorable(format: c_int, out_authorable: *mut c_int) -> TwigStatus;
644    pub fn twig_editor_table_edit(
645        editor: *mut TwigEditor,
646        offset: usize,
647        op: c_int,
648        arg: c_int,
649        out_change: *mut TwigChange,
650    ) -> TwigStatus;
651    pub fn twig_editor_insert_link(
652        editor: *mut TwigEditor,
653        start: usize,
654        end: usize,
655        destination: *const u8,
656        destination_len: usize,
657        out_change: *mut TwigChange,
658    ) -> TwigStatus;
659    pub fn twig_editor_insert_image(
660        editor: *mut TwigEditor,
661        start: usize,
662        end: usize,
663        destination: *const u8,
664        destination_len: usize,
665        out_change: *mut TwigChange,
666    ) -> TwigStatus;
667
668    pub fn twig_editor_insert_literal(
669        editor: *mut TwigEditor,
670        offset: usize,
671        text: *const u8,
672        text_len: usize,
673        out_change: *mut TwigChange,
674    ) -> TwigStatus;
675
676    pub fn twig_editor_insert_line_break(
677        editor: *mut TwigEditor,
678        offset: usize,
679        out_change: *mut TwigChange,
680    ) -> TwigStatus;
681
682    pub fn twig_editor_insert_thematic_break(
683        editor: *mut TwigEditor,
684        offset: usize,
685        out_change: *mut TwigChange,
686    ) -> TwigStatus;
687
688    pub fn twig_editor_split_block(
689        editor: *mut TwigEditor,
690        offset: usize,
691        out_change: *mut TwigChange,
692    ) -> TwigStatus;
693    // `language` is OPTIONAL, carried as this ABI's (ptr, len, has_*) triple:
694    // has_language == 0 leaves the fence bare, and "absent" is a different
695    // request from "present but empty".
696    pub fn twig_editor_toggle_code_block(
697        editor: *mut TwigEditor,
698        start: usize,
699        end: usize,
700        language: *const u8,
701        language_len: usize,
702        has_language: c_int,
703        out_change: *mut TwigChange,
704    ) -> TwigStatus;
705    pub fn twig_editor_set_code_language(
706        editor: *mut TwigEditor,
707        offset: usize,
708        language: *const u8,
709        language_len: usize,
710        has_language: c_int,
711        out_change: *mut TwigChange,
712    ) -> TwigStatus;
713    /// Set — or clear, with `has_color == 0` — the colour of the highlight the
714    /// caret at `offset` is inside. Markdown with `TWIG_MD_HIGHLIGHT_COLORS`
715    /// only; the name is a `data-color` value (`red`, `blue`, …).
716    pub fn twig_editor_set_mark_color(
717        editor: *mut TwigEditor,
718        offset: usize,
719        color: *const u8,
720        color_len: usize,
721        has_color: c_int,
722        out_change: *mut TwigChange,
723    ) -> TwigStatus;
724    pub fn twig_editor_toggle_task_item(
725        editor: *mut TwigEditor,
726        offset: usize,
727        out_change: *mut TwigChange,
728    ) -> TwigStatus;
729    pub fn twig_editor_set_task_checked(
730        editor: *mut TwigEditor,
731        offset: usize,
732        checked: c_int,
733        out_change: *mut TwigChange,
734    ) -> TwigStatus;
735    pub fn twig_editor_toggle_task_checked(
736        editor: *mut TwigEditor,
737        offset: usize,
738        out_change: *mut TwigChange,
739    ) -> TwigStatus;
740    pub fn twig_editor_insert_footnote(
741        editor: *mut TwigEditor,
742        offset: usize,
743        label: *const u8,
744        label_len: usize,
745        out_change: *mut TwigChange,
746    ) -> TwigStatus;
747
748    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
749    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
750    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32)
751    -> TwigStatus;
752    pub fn twig_builder_add_text(
753        builder: *mut TwigBuilder,
754        kind: c_int,
755        text: *const u8,
756        text_len: usize,
757        out_id: *mut u32,
758    ) -> TwigStatus;
759    pub fn twig_builder_add_heading(
760        builder: *mut TwigBuilder,
761        level: u32,
762        out_id: *mut u32,
763    ) -> TwigStatus;
764    pub fn twig_builder_add_code_block(
765        builder: *mut TwigBuilder,
766        lang: *const u8,
767        lang_len: usize,
768        has_lang: c_int,
769        text: *const u8,
770        text_len: usize,
771        out_id: *mut u32,
772    ) -> TwigStatus;
773    pub fn twig_builder_add_raw_block(
774        builder: *mut TwigBuilder,
775        format: *const u8,
776        format_len: usize,
777        text: *const u8,
778        text_len: usize,
779        out_id: *mut u32,
780    ) -> TwigStatus;
781    pub fn twig_builder_add_metadata(
782        builder: *mut TwigBuilder,
783        lang: *const u8,
784        lang_len: usize,
785        text: *const u8,
786        text_len: usize,
787        out_id: *mut u32,
788    ) -> TwigStatus;
789    pub fn twig_builder_add_raw_inline(
790        builder: *mut TwigBuilder,
791        format: *const u8,
792        format_len: usize,
793        text: *const u8,
794        text_len: usize,
795        out_id: *mut u32,
796    ) -> TwigStatus;
797    pub fn twig_builder_add_smart_punctuation(
798        builder: *mut TwigBuilder,
799        punct_kind: c_int,
800        text: *const u8,
801        text_len: usize,
802        out_id: *mut u32,
803    ) -> TwigStatus;
804    pub fn twig_builder_add_link(
805        builder: *mut TwigBuilder,
806        destination: *const u8,
807        destination_len: usize,
808        has_destination: c_int,
809        reference: *const u8,
810        reference_len: usize,
811        has_reference: c_int,
812        out_id: *mut u32,
813    ) -> TwigStatus;
814    pub fn twig_builder_add_image(
815        builder: *mut TwigBuilder,
816        destination: *const u8,
817        destination_len: usize,
818        has_destination: c_int,
819        reference: *const u8,
820        reference_len: usize,
821        has_reference: c_int,
822        out_id: *mut u32,
823    ) -> TwigStatus;
824    pub fn twig_builder_add_directive(
825        builder: *mut TwigBuilder,
826        form: c_int,
827        name: *const u8,
828        name_len: usize,
829        out_id: *mut u32,
830    ) -> TwigStatus;
831    pub fn twig_builder_add_element(
832        builder: *mut TwigBuilder,
833        name: *const u8,
834        name_len: usize,
835        out_id: *mut u32,
836    ) -> TwigStatus;
837    pub fn twig_builder_add_processing_instruction(
838        builder: *mut TwigBuilder,
839        target: *const u8,
840        target_len: usize,
841        data: *const u8,
842        data_len: usize,
843        out_id: *mut u32,
844    ) -> TwigStatus;
845    pub fn twig_builder_add_footnote(
846        builder: *mut TwigBuilder,
847        label: *const u8,
848        label_len: usize,
849        out_id: *mut u32,
850    ) -> TwigStatus;
851    pub fn twig_builder_add_citation(
852        builder: *mut TwigBuilder,
853        label: *const u8,
854        label_len: usize,
855        out_id: *mut u32,
856    ) -> TwigStatus;
857    pub fn twig_builder_add_substitution(
858        builder: *mut TwigBuilder,
859        label: *const u8,
860        label_len: usize,
861        out_id: *mut u32,
862    ) -> TwigStatus;
863    pub fn twig_builder_add_reference(
864        builder: *mut TwigBuilder,
865        label: *const u8,
866        label_len: usize,
867        destination: *const u8,
868        destination_len: usize,
869        out_id: *mut u32,
870    ) -> TwigStatus;
871    pub fn twig_builder_add_bullet_list(
872        builder: *mut TwigBuilder,
873        style: c_int,
874        tight: c_int,
875        out_id: *mut u32,
876    ) -> TwigStatus;
877    pub fn twig_builder_add_ordered_list(
878        builder: *mut TwigBuilder,
879        numbering: c_int,
880        delim: c_int,
881        tight: c_int,
882        start: u32,
883        has_start: c_int,
884        out_id: *mut u32,
885    ) -> TwigStatus;
886    pub fn twig_builder_add_task_list(
887        builder: *mut TwigBuilder,
888        tight: c_int,
889        out_id: *mut u32,
890    ) -> TwigStatus;
891    pub fn twig_builder_add_task_list_item(
892        builder: *mut TwigBuilder,
893        checked: c_int,
894        out_id: *mut u32,
895    ) -> TwigStatus;
896    pub fn twig_builder_add_row(
897        builder: *mut TwigBuilder,
898        head: c_int,
899        out_id: *mut u32,
900    ) -> TwigStatus;
901    pub fn twig_builder_add_cell(
902        builder: *mut TwigBuilder,
903        head: c_int,
904        alignment: c_int,
905        out_id: *mut u32,
906    ) -> TwigStatus;
907    pub fn twig_builder_add_cell_spanning(
908        builder: *mut TwigBuilder,
909        head: c_int,
910        alignment: c_int,
911        colspan: u32,
912        rowspan: u32,
913        out_id: *mut u32,
914    ) -> TwigStatus;
915    pub fn twig_builder_set_children(
916        builder: *mut TwigBuilder,
917        parent: u32,
918        ids: *const u32,
919        ids_len: usize,
920    ) -> TwigStatus;
921    pub fn twig_builder_set_attrs(
922        builder: *mut TwigBuilder,
923        id: u32,
924        kvs: *const TwigKeyVal,
925        kvs_len: usize,
926    ) -> TwigStatus;
927    pub fn twig_builder_render_html(
928        builder: *mut TwigBuilder,
929        root: u32,
930        out_ptr: *mut *const u8,
931        out_len: *mut usize,
932    ) -> TwigStatus;
933    pub fn twig_builder_serialize(
934        builder: *mut TwigBuilder,
935        root: u32,
936        format: c_int,
937        out_ptr: *mut *const u8,
938        out_len: *mut usize,
939    ) -> TwigStatus;
940    pub fn twig_builder_ast_json(
941        builder: *mut TwigBuilder,
942        root: u32,
943        out_ptr: *mut *const u8,
944        out_len: *mut usize,
945    ) -> TwigStatus;
946    pub fn twig_builder_query(
947        builder: *mut TwigBuilder,
948        root: u32,
949        selector: *const u8,
950        selector_len: usize,
951        out_ptr: *mut *const TwigQueryMatch,
952        out_len: *mut usize,
953    ) -> TwigStatus;
954}