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