diff --git a/3rdparty/cmark-gfm/bin/cmark-gfm.exe b/3rdparty/cmark-gfm/bin/cmark-gfm.exe new file mode 100644 index 0000000..fdc406a Binary files /dev/null and b/3rdparty/cmark-gfm/bin/cmark-gfm.exe differ diff --git a/3rdparty/cmark-gfm/include/cmark-gfm-core-extensions.h b/3rdparty/cmark-gfm/include/cmark-gfm-core-extensions.h new file mode 100644 index 0000000..bc29ffd --- /dev/null +++ b/3rdparty/cmark-gfm/include/cmark-gfm-core-extensions.h @@ -0,0 +1,54 @@ +#ifndef CMARK_GFM_CORE_EXTENSIONS_H +#define CMARK_GFM_CORE_EXTENSIONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cmark-gfm-extension_api.h" +#include "cmark-gfm_export.h" +#include +#include + +CMARK_GFM_EXPORT +void cmark_gfm_core_extensions_ensure_registered(void); + +CMARK_GFM_EXPORT +uint16_t cmark_gfm_extensions_get_table_columns(cmark_node *node); + +/** Sets the number of columns for the table, returning 1 on success and 0 on error. + */ +CMARK_GFM_EXPORT +int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns); + +CMARK_GFM_EXPORT +uint8_t *cmark_gfm_extensions_get_table_alignments(cmark_node *node); + +/** Sets the alignments for the table, returning 1 on success and 0 on error. + */ +CMARK_GFM_EXPORT +int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments); + +CMARK_GFM_EXPORT +int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node); + +/** Sets whether the node is a table header row, returning 1 on success and 0 on error. + */ +CMARK_GFM_EXPORT +int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header); + +CMARK_GFM_EXPORT +bool cmark_gfm_extensions_get_tasklist_item_checked(cmark_node *node); +/* For backwards compatibility */ +#define cmark_gfm_extensions_tasklist_is_checked cmark_gfm_extensions_get_tasklist_item_checked + +/** Sets whether a tasklist item is "checked" (completed), returning 1 on success and 0 on error. + */ +CMARK_GFM_EXPORT +int cmark_gfm_extensions_set_tasklist_item_checked(cmark_node *node, bool is_checked); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rdparty/cmark-gfm/include/cmark-gfm-extension_api.h b/3rdparty/cmark-gfm/include/cmark-gfm-extension_api.h new file mode 100644 index 0000000..bb92a59 --- /dev/null +++ b/3rdparty/cmark-gfm/include/cmark-gfm-extension_api.h @@ -0,0 +1,737 @@ +#ifndef CMARK_GFM_EXTENSION_API_H +#define CMARK_GFM_EXTENSION_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cmark-gfm.h" + +struct cmark_renderer; +struct cmark_html_renderer; +struct cmark_chunk; + +/** + * ## Extension Support + * + * While the "core" of libcmark is strictly compliant with the + * specification, an API is provided for extension writers to + * hook into the parsing process. + * + * It should be noted that the cmark_node API already offers + * room for customization, with methods offered to traverse and + * modify the AST, and even define custom blocks. + * When the desired customization is achievable in an error-proof + * way using that API, it should be the preferred method. + * + * The following API requires a more in-depth understanding + * of libcmark's parsing strategy, which is exposed + * [here](http://spec.commonmark.org/0.24/#appendix-a-parsing-strategy). + * + * It should be used when "a posteriori" modification of the AST + * proves to be too difficult / impossible to implement correctly. + * + * It can also serve as an intermediary step before extending + * the specification, as an extension implemented using this API + * will be trivially integrated in the core if it proves to be + * desirable. + */ + +typedef struct cmark_plugin cmark_plugin; + +/** A syntax extension that can be attached to a cmark_parser + * with cmark_parser_attach_syntax_extension(). + * + * Extension writers should assign functions matching + * the signature of the following 'virtual methods' to + * implement new functionality. + * + * Their calling order and expected behaviour match the procedure outlined + * at : + * + * During step 1, cmark will call the function provided through + * 'cmark_syntax_extension_set_match_block_func' when it + * iterates over an open block created by this extension, + * to determine whether it could contain the new line. + * If no function was provided, cmark will close the block. + * + * During step 2, if and only if the new line doesn't match any + * of the standard syntax rules, cmark will call the function + * provided through 'cmark_syntax_extension_set_open_block_func' + * to let the extension determine whether that new line matches + * one of its syntax rules. + * It is the responsibility of the parser to create and add the + * new block with cmark_parser_make_block and cmark_parser_add_child. + * If no function was provided is NULL, the extension will have + * no effect at all on the final block structure of the AST. + * + * #### Inline parsing phase hooks + * + * For each character provided by the extension through + * 'cmark_syntax_extension_set_special_inline_chars', + * the function provided by the extension through + * 'cmark_syntax_extension_set_match_inline_func' + * will get called, it is the responsibility of the extension + * to scan the characters located at the current inline parsing offset + * with the cmark_inline_parser API. + * + * Depending on the type of the extension, it can either: + * + * * Scan forward, determine that the syntax matches and return + * a newly-created inline node with the appropriate type. + * This is the technique that would be used if inline code + * (with backticks) was implemented as an extension. + * * Scan only the character(s) that its syntax rules require + * for opening and closing nodes, push a delimiter on the + * delimiter stack, and return a simple text node with its + * contents set to the character(s) consumed. + * This is the technique that would be used if emphasis + * inlines were implemented as an extension. + * + * When an extension has pushed delimiters on the stack, + * the function provided through + * 'cmark_syntax_extension_set_inline_from_delim_func' + * will get called in a latter phase, + * when the inline parser has matched opener and closer delimiters + * created by the extension together. + * + * It is then the responsibility of the extension to modify + * and populate the opener inline text node, and to remove + * the necessary delimiters from the delimiter stack. + * + * Finally, the extension should return NULL if its scan didn't + * match its syntax rules. + * + * The extension can store whatever private data it might need + * with 'cmark_syntax_extension_set_private', + * and optionally define a free function for this data. + */ +typedef struct subject cmark_inline_parser; + +/** Exposed raw for now */ + +typedef struct delimiter { + struct delimiter *previous; + struct delimiter *next; + cmark_node *inl_text; + bufsize_t position; + bufsize_t length; + unsigned char delim_char; + int can_open; + int can_close; +} delimiter; + +/** + * ### Plugin API. + * + * Extensions should be distributed as dynamic libraries, + * with a single exported function named after the distributed + * filename. + * + * When discovering extensions (see cmark_init), cmark will + * try to load a symbol named "init_{{filename}}" in all the + * dynamic libraries it encounters. + * + * For example, given a dynamic library named myextension.so + * (or myextension.dll), cmark will try to load the symbol + * named "init_myextension". This means that the filename + * must lend itself to forming a valid C identifier, with + * the notable exception of dashes, which will be translated + * to underscores, which means cmark will look for a function + * named "init_my_extension" if it encounters a dynamic library + * named "my-extension.so". + * + * See the 'cmark_plugin_init_func' typedef for the exact prototype + * this function should follow. + * + * For now the extensibility of cmark is not complete, as + * it only offers API to hook into the block parsing phase + * (). + * + * See 'cmark_plugin_register_syntax_extension' for more information. + */ + +/** The prototype plugins' init function should follow. + */ +typedef int (*cmark_plugin_init_func)(cmark_plugin *plugin); + +/** Register a syntax 'extension' with the 'plugin', it will be made + * available as an extension and, if attached to a cmark_parser + * with 'cmark_parser_attach_syntax_extension', it will contribute + * to the block parsing process. + * + * See the documentation for 'cmark_syntax_extension' for information + * on how to implement one. + * + * This function will typically be called from the init function + * of external modules. + * + * This takes ownership of 'extension', one should not call + * 'cmark_syntax_extension_free' on a registered extension. + */ +CMARK_GFM_EXPORT +int cmark_plugin_register_syntax_extension(cmark_plugin *plugin, + cmark_syntax_extension *extension); + +/** This will search for the syntax extension named 'name' among the + * registered syntax extensions. + * + * It can then be attached to a cmark_parser + * with the cmark_parser_attach_syntax_extension method. + */ +CMARK_GFM_EXPORT +cmark_syntax_extension *cmark_find_syntax_extension(const char *name); + +/** Should create and add a new open block to 'parent_container' if + * 'input' matches a syntax rule for that block type. It is allowed + * to modify the type of 'parent_container'. + * + * Should return the newly created block if there is one, or + * 'parent_container' if its type was modified, or NULL. + */ +typedef cmark_node * (*cmark_open_block_func) (cmark_syntax_extension *extension, + int indented, + cmark_parser *parser, + cmark_node *parent_container, + unsigned char *input, + int len); + +typedef cmark_node *(*cmark_match_inline_func)(cmark_syntax_extension *extension, + cmark_parser *parser, + cmark_node *parent, + unsigned char character, + cmark_inline_parser *inline_parser); + +typedef delimiter *(*cmark_inline_from_delim_func)(cmark_syntax_extension *extension, + cmark_parser *parser, + cmark_inline_parser *inline_parser, + delimiter *opener, + delimiter *closer); + +/** Should return 'true' if 'input' can be contained in 'container', + * 'false' otherwise. + */ +typedef int (*cmark_match_block_func) (cmark_syntax_extension *extension, + cmark_parser *parser, + unsigned char *input, + int len, + cmark_node *container); + +typedef const char *(*cmark_get_type_string_func) (cmark_syntax_extension *extension, + cmark_node *node); + +typedef int (*cmark_can_contain_func) (cmark_syntax_extension *extension, + cmark_node *node, + cmark_node_type child); + +typedef int (*cmark_contains_inlines_func) (cmark_syntax_extension *extension, + cmark_node *node); + +typedef void (*cmark_common_render_func) (cmark_syntax_extension *extension, + struct cmark_renderer *renderer, + cmark_node *node, + cmark_event_type ev_type, + int options); + +typedef int (*cmark_commonmark_escape_func) (cmark_syntax_extension *extension, + cmark_node *node, + int c); + +typedef const char* (*cmark_xml_attr_func) (cmark_syntax_extension *extension, + cmark_node *node); + +typedef void (*cmark_html_render_func) (cmark_syntax_extension *extension, + struct cmark_html_renderer *renderer, + cmark_node *node, + cmark_event_type ev_type, + int options); + +typedef int (*cmark_html_filter_func) (cmark_syntax_extension *extension, + const unsigned char *tag, + size_t tag_len); + +typedef cmark_node *(*cmark_postprocess_func) (cmark_syntax_extension *extension, + cmark_parser *parser, + cmark_node *root); + +typedef int (*cmark_ispunct_func) (char c); + +typedef void (*cmark_opaque_alloc_func) (cmark_syntax_extension *extension, + cmark_mem *mem, + cmark_node *node); + +typedef void (*cmark_opaque_free_func) (cmark_syntax_extension *extension, + cmark_mem *mem, + cmark_node *node); + +/** Free a cmark_syntax_extension. + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_free (cmark_mem *mem, cmark_syntax_extension *extension); + +/** Return a newly-constructed cmark_syntax_extension, named 'name'. + */ +CMARK_GFM_EXPORT +cmark_syntax_extension *cmark_syntax_extension_new (const char *name); + +CMARK_GFM_EXPORT +cmark_node_type cmark_syntax_extension_add_node(int is_inline); + +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_emphasis(cmark_syntax_extension *extension, int emphasis); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_open_block_func(cmark_syntax_extension *extension, + cmark_open_block_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_match_block_func(cmark_syntax_extension *extension, + cmark_match_block_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_match_inline_func(cmark_syntax_extension *extension, + cmark_match_inline_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_inline_from_delim_func(cmark_syntax_extension *extension, + cmark_inline_from_delim_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_special_inline_chars(cmark_syntax_extension *extension, + cmark_llist *special_chars); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_get_type_string_func(cmark_syntax_extension *extension, + cmark_get_type_string_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_can_contain_func(cmark_syntax_extension *extension, + cmark_can_contain_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_contains_inlines_func(cmark_syntax_extension *extension, + cmark_contains_inlines_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_commonmark_render_func(cmark_syntax_extension *extension, + cmark_common_render_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_plaintext_render_func(cmark_syntax_extension *extension, + cmark_common_render_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_latex_render_func(cmark_syntax_extension *extension, + cmark_common_render_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_xml_attr_func(cmark_syntax_extension *extension, + cmark_xml_attr_func func); + + /** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_man_render_func(cmark_syntax_extension *extension, + cmark_common_render_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_html_render_func(cmark_syntax_extension *extension, + cmark_html_render_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_html_filter_func(cmark_syntax_extension *extension, + cmark_html_filter_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_commonmark_escape_func(cmark_syntax_extension *extension, + cmark_commonmark_escape_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_private(cmark_syntax_extension *extension, + void *priv, + cmark_free_func free_func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void *cmark_syntax_extension_get_private(cmark_syntax_extension *extension); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_postprocess_func(cmark_syntax_extension *extension, + cmark_postprocess_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_opaque_alloc_func(cmark_syntax_extension *extension, + cmark_opaque_alloc_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_syntax_extension_set_opaque_free_func(cmark_syntax_extension *extension, + cmark_opaque_free_func func); + +/** See the documentation for 'cmark_syntax_extension' + */ +CMARK_GFM_EXPORT +void cmark_parser_set_backslash_ispunct_func(cmark_parser *parser, + cmark_ispunct_func func); + +/** Return the index of the line currently being parsed, starting with 1. + */ +CMARK_GFM_EXPORT +int cmark_parser_get_line_number(cmark_parser *parser); + +/** Return the offset in bytes in the line being processed. + * + * Example: + * + * ### foo + * + * Here, offset will first be 0, then 5 (the index of the 'f' character). + */ +CMARK_GFM_EXPORT +int cmark_parser_get_offset(cmark_parser *parser); + +/** + * Return the offset in 'columns' in the line being processed. + * + * This value may differ from the value returned by + * cmark_parser_get_offset() in that it accounts for tabs, + * and as such should not be used as an index in the current line's + * buffer. + * + * Example: + * + * cmark_parser_advance_offset() can be called to advance the + * offset by a number of columns, instead of a number of bytes. + * + * In that case, if offset falls "in the middle" of a tab + * character, 'column' and offset will differ. + * + * ``` + * foo \t bar + * ^ ^^ + * offset (0) 20 + * ``` + * + * If cmark_parser_advance_offset is called here with 'columns' + * set to 'true' and 'offset' set to 22, cmark_parser_get_offset() + * will return 20, whereas cmark_parser_get_column() will return + * 22. + * + * Additionally, as tabs expand to the next multiple of 4 column, + * cmark_parser_has_partially_consumed_tab() will now return + * 'true'. + */ +CMARK_GFM_EXPORT +int cmark_parser_get_column(cmark_parser *parser); + +/** Return the absolute index in bytes of the first nonspace + * character coming after the offset as returned by + * cmark_parser_get_offset() in the line currently being processed. + * + * Example: + * + * ``` + * foo bar baz \n + * ^ ^ ^ + * 0 offset (16) first_nonspace (28) + * ``` + */ +CMARK_GFM_EXPORT +int cmark_parser_get_first_nonspace(cmark_parser *parser); + +/** Return the absolute index of the first nonspace column coming after 'offset' + * in the line currently being processed, counting tabs as multiple + * columns as appropriate. + * + * See the documentation for cmark_parser_get_first_nonspace() and + * cmark_parser_get_column() for more information. + */ +CMARK_GFM_EXPORT +int cmark_parser_get_first_nonspace_column(cmark_parser *parser); + +/** Return the difference between the values returned by + * cmark_parser_get_first_nonspace_column() and + * cmark_parser_get_column(). + * + * This is not a byte offset, as it can count one tab as multiple + * characters. + */ +CMARK_GFM_EXPORT +int cmark_parser_get_indent(cmark_parser *parser); + +/** Return 'true' if the line currently being processed has been entirely + * consumed, 'false' otherwise. + * + * Example: + * + * ``` + * foo bar baz \n + * ^ + * offset + * ``` + * + * This function will return 'false' here. + * + * ``` + * foo bar baz \n + * ^ + * offset + * ``` + * This function will still return 'false'. + * + * ``` + * foo bar baz \n + * ^ + * offset + * ``` + * + * At this point, this function will now return 'true'. + */ +CMARK_GFM_EXPORT +int cmark_parser_is_blank(cmark_parser *parser); + +/** Return 'true' if the value returned by cmark_parser_get_offset() + * is 'inside' an expanded tab. + * + * See the documentation for cmark_parser_get_column() for more + * information. + */ +CMARK_GFM_EXPORT +int cmark_parser_has_partially_consumed_tab(cmark_parser *parser); + +/** Return the length in bytes of the previously processed line, excluding potential + * newline (\n) and carriage return (\r) trailing characters. + */ +CMARK_GFM_EXPORT +int cmark_parser_get_last_line_length(cmark_parser *parser); + +/** Add a child to 'parent' during the parsing process. + * + * If 'parent' isn't the kind of node that can accept this child, + * this function will back up till it hits a node that can, closing + * blocks as appropriate. + */ +CMARK_GFM_EXPORT +cmark_node*cmark_parser_add_child(cmark_parser *parser, + cmark_node *parent, + cmark_node_type block_type, + int start_column); + +/** Advance the 'offset' of the parser in the current line. + * + * See the documentation of cmark_parser_get_offset() and + * cmark_parser_get_column() for more information. + */ +CMARK_GFM_EXPORT +void cmark_parser_advance_offset(cmark_parser *parser, + const char *input, + int count, + int columns); + + +CMARK_GFM_EXPORT +void cmark_parser_feed_reentrant(cmark_parser *parser, const char *buffer, size_t len); + +/** Attach the syntax 'extension' to the 'parser', to provide extra syntax + * rules. + * See the documentation for cmark_syntax_extension for more information. + * + * Returns 'true' if the 'extension' was successfully attached, + * 'false' otherwise. + */ +CMARK_GFM_EXPORT +int cmark_parser_attach_syntax_extension(cmark_parser *parser, cmark_syntax_extension *extension); + +/** Change the type of 'node'. + * + * Return 0 if the type could be changed, 1 otherwise. + */ +CMARK_GFM_EXPORT int cmark_node_set_type(cmark_node *node, cmark_node_type type); + +/** Return the string content for all types of 'node'. + * The pointer stays valid as long as 'node' isn't freed. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_string_content(cmark_node *node); + +/** Set the string 'content' for all types of 'node'. + * Copies 'content'. + */ +CMARK_GFM_EXPORT int cmark_node_set_string_content(cmark_node *node, const char *content); + +/** Get the syntax extension responsible for the creation of 'node'. + * Return NULL if 'node' was created because it matched standard syntax rules. + */ +CMARK_GFM_EXPORT cmark_syntax_extension *cmark_node_get_syntax_extension(cmark_node *node); + +/** Set the syntax extension responsible for creating 'node'. + */ +CMARK_GFM_EXPORT int cmark_node_set_syntax_extension(cmark_node *node, + cmark_syntax_extension *extension); + +/** + * ## Inline syntax extension helpers + * + * The inline parsing process is described in detail at + * + */ + +/** Should return 'true' if the predicate matches 'c', 'false' otherwise + */ +typedef int (*cmark_inline_predicate)(int c); + +/** Advance the current inline parsing offset */ +CMARK_GFM_EXPORT +void cmark_inline_parser_advance_offset(cmark_inline_parser *parser); + +/** Get the current inline parsing offset */ +CMARK_GFM_EXPORT +int cmark_inline_parser_get_offset(cmark_inline_parser *parser); + +/** Set the offset in bytes in the chunk being processed by the given inline parser. + */ +CMARK_GFM_EXPORT +void cmark_inline_parser_set_offset(cmark_inline_parser *parser, int offset); + +/** Gets the cmark_chunk being operated on by the given inline parser. + * Use cmark_inline_parser_get_offset to get our current position in the chunk. + */ +CMARK_GFM_EXPORT +struct cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser); + +/** Returns 1 if the inline parser is currently in a bracket; pass 1 for 'image' + * if you want to know about an image-type bracket, 0 for link-type. */ +CMARK_GFM_EXPORT +int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int image); + +/** Remove the last n characters from the last child of the given node. + * This only works where all n characters are in the single last child, and the last + * child is CMARK_NODE_TEXT. + */ +CMARK_GFM_EXPORT +void cmark_node_unput(cmark_node *node, int n); + + +/** Get the character located at the current inline parsing offset + */ +CMARK_GFM_EXPORT +unsigned char cmark_inline_parser_peek_char(cmark_inline_parser *parser); + +/** Get the character located 'pos' bytes in the current line. + */ +CMARK_GFM_EXPORT +unsigned char cmark_inline_parser_peek_at(cmark_inline_parser *parser, int pos); + +/** Whether the inline parser has reached the end of the current line + */ +CMARK_GFM_EXPORT +int cmark_inline_parser_is_eof(cmark_inline_parser *parser); + +/** Get the characters located after the current inline parsing offset + * while 'pred' matches. Free after usage. + */ +CMARK_GFM_EXPORT +char *cmark_inline_parser_take_while(cmark_inline_parser *parser, cmark_inline_predicate pred); + +/** Push a delimiter on the delimiter stack. + * See < for + * more information on the parameters + */ +CMARK_GFM_EXPORT +void cmark_inline_parser_push_delimiter(cmark_inline_parser *parser, + unsigned char c, + int can_open, + int can_close, + cmark_node *inl_text); + +/** Remove 'delim' from the delimiter stack + */ +CMARK_GFM_EXPORT +void cmark_inline_parser_remove_delimiter(cmark_inline_parser *parser, delimiter *delim); + +CMARK_GFM_EXPORT +delimiter *cmark_inline_parser_get_last_delimiter(cmark_inline_parser *parser); + +CMARK_GFM_EXPORT +int cmark_inline_parser_get_line(cmark_inline_parser *parser); + +CMARK_GFM_EXPORT +int cmark_inline_parser_get_column(cmark_inline_parser *parser); + +/** Convenience function to scan a given delimiter. + * + * 'left_flanking' and 'right_flanking' will be set to true if they + * respectively precede and follow a non-space, non-punctuation + * character. + * + * Additionally, 'punct_before' and 'punct_after' will respectively be set + * if the preceding or following character is a punctuation character. + * + * Note that 'left_flanking' and 'right_flanking' can both be 'true'. + * + * Returns the number of delimiters encountered, in the limit + * of 'max_delims', and advances the inline parsing offset. + */ +CMARK_GFM_EXPORT +int cmark_inline_parser_scan_delimiters(cmark_inline_parser *parser, + int max_delims, + unsigned char c, + int *left_flanking, + int *right_flanking, + int *punct_before, + int *punct_after); + +CMARK_GFM_EXPORT +void cmark_manage_extensions_special_characters(cmark_parser *parser, int add); + +CMARK_GFM_EXPORT +cmark_llist *cmark_parser_get_syntax_extensions(cmark_parser *parser); + +CMARK_GFM_EXPORT +void cmark_arena_push(void); + +CMARK_GFM_EXPORT +int cmark_arena_pop(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rdparty/cmark-gfm/include/cmark-gfm.h b/3rdparty/cmark-gfm/include/cmark-gfm.h new file mode 100644 index 0000000..0544057 --- /dev/null +++ b/3rdparty/cmark-gfm/include/cmark-gfm.h @@ -0,0 +1,833 @@ +#ifndef CMARK_GFM_H +#define CMARK_GFM_H + +#include +#include +#include "cmark-gfm_export.h" +#include "cmark-gfm_version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** # NAME + * + * **cmark-gfm** - CommonMark parsing, manipulating, and rendering + */ + +/** # DESCRIPTION + * + * ## Simple Interface + */ + +/** Convert 'text' (assumed to be a UTF-8 encoded string with length + * 'len') from CommonMark Markdown to HTML, returning a null-terminated, + * UTF-8-encoded string. It is the caller's responsibility + * to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_markdown_to_html(const char *text, size_t len, int options); + +/** ## Node Structure + */ + +#define CMARK_NODE_TYPE_PRESENT (0x8000) +#define CMARK_NODE_TYPE_BLOCK (CMARK_NODE_TYPE_PRESENT | 0x0000) +#define CMARK_NODE_TYPE_INLINE (CMARK_NODE_TYPE_PRESENT | 0x4000) +#define CMARK_NODE_TYPE_MASK (0xc000) +#define CMARK_NODE_VALUE_MASK (0x3fff) + +typedef enum { + /* Error status */ + CMARK_NODE_NONE = 0x0000, + + /* Block */ + CMARK_NODE_DOCUMENT = CMARK_NODE_TYPE_BLOCK | 0x0001, + CMARK_NODE_BLOCK_QUOTE = CMARK_NODE_TYPE_BLOCK | 0x0002, + CMARK_NODE_LIST = CMARK_NODE_TYPE_BLOCK | 0x0003, + CMARK_NODE_ITEM = CMARK_NODE_TYPE_BLOCK | 0x0004, + CMARK_NODE_CODE_BLOCK = CMARK_NODE_TYPE_BLOCK | 0x0005, + CMARK_NODE_HTML_BLOCK = CMARK_NODE_TYPE_BLOCK | 0x0006, + CMARK_NODE_CUSTOM_BLOCK = CMARK_NODE_TYPE_BLOCK | 0x0007, + CMARK_NODE_PARAGRAPH = CMARK_NODE_TYPE_BLOCK | 0x0008, + CMARK_NODE_HEADING = CMARK_NODE_TYPE_BLOCK | 0x0009, + CMARK_NODE_THEMATIC_BREAK = CMARK_NODE_TYPE_BLOCK | 0x000a, + CMARK_NODE_FOOTNOTE_DEFINITION = CMARK_NODE_TYPE_BLOCK | 0x000b, + + /* Inline */ + CMARK_NODE_TEXT = CMARK_NODE_TYPE_INLINE | 0x0001, + CMARK_NODE_SOFTBREAK = CMARK_NODE_TYPE_INLINE | 0x0002, + CMARK_NODE_LINEBREAK = CMARK_NODE_TYPE_INLINE | 0x0003, + CMARK_NODE_CODE = CMARK_NODE_TYPE_INLINE | 0x0004, + CMARK_NODE_HTML_INLINE = CMARK_NODE_TYPE_INLINE | 0x0005, + CMARK_NODE_CUSTOM_INLINE = CMARK_NODE_TYPE_INLINE | 0x0006, + CMARK_NODE_EMPH = CMARK_NODE_TYPE_INLINE | 0x0007, + CMARK_NODE_STRONG = CMARK_NODE_TYPE_INLINE | 0x0008, + CMARK_NODE_LINK = CMARK_NODE_TYPE_INLINE | 0x0009, + CMARK_NODE_IMAGE = CMARK_NODE_TYPE_INLINE | 0x000a, + CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b, +} cmark_node_type; + +extern cmark_node_type CMARK_NODE_LAST_BLOCK; +extern cmark_node_type CMARK_NODE_LAST_INLINE; + +/* For backwards compatibility: */ +#define CMARK_NODE_HEADER CMARK_NODE_HEADING +#define CMARK_NODE_HRULE CMARK_NODE_THEMATIC_BREAK +#define CMARK_NODE_HTML CMARK_NODE_HTML_BLOCK +#define CMARK_NODE_INLINE_HTML CMARK_NODE_HTML_INLINE + +typedef enum { + CMARK_NO_LIST, + CMARK_BULLET_LIST, + CMARK_ORDERED_LIST +} cmark_list_type; + +typedef enum { + CMARK_NO_DELIM, + CMARK_PERIOD_DELIM, + CMARK_PAREN_DELIM +} cmark_delim_type; + +typedef struct cmark_node cmark_node; +typedef struct cmark_parser cmark_parser; +typedef struct cmark_iter cmark_iter; +typedef struct cmark_syntax_extension cmark_syntax_extension; + +/** + * ## Custom memory allocator support + */ + +/** Defines the memory allocation functions to be used by CMark + * when parsing and allocating a document tree + */ +typedef struct cmark_mem { + void *(*calloc)(size_t, size_t); + void *(*realloc)(void *, size_t); + void (*free)(void *); +} cmark_mem; + +/** The default memory allocator; uses the system's calloc, + * realloc and free. + */ +CMARK_GFM_EXPORT +cmark_mem *cmark_get_default_mem_allocator(void); + +/** An arena allocator; uses system calloc to allocate large + * slabs of memory. Memory in these slabs is not reused at all. + */ +CMARK_GFM_EXPORT +cmark_mem *cmark_get_arena_mem_allocator(void); + +/** Resets the arena allocator, quickly returning all used memory + * to the operating system. + */ +CMARK_GFM_EXPORT +void cmark_arena_reset(void); + +/** Callback for freeing user data with a 'cmark_mem' context. + */ +typedef void (*cmark_free_func) (cmark_mem *mem, void *user_data); + + +/* + * ## Basic data structures + * + * To keep dependencies to the strict minimum, libcmark implements + * its own versions of "classic" data structures. + */ + +/** + * ### Linked list + */ + +/** A generic singly linked list. + */ +typedef struct _cmark_llist +{ + struct _cmark_llist *next; + void *data; +} cmark_llist; + +/** Append an element to the linked list, return the possibly modified + * head of the list. + */ +CMARK_GFM_EXPORT +cmark_llist * cmark_llist_append (cmark_mem * mem, + cmark_llist * head, + void * data); + +/** Free the list starting with 'head', calling 'free_func' with the + * data pointer of each of its elements + */ +CMARK_GFM_EXPORT +void cmark_llist_free_full (cmark_mem * mem, + cmark_llist * head, + cmark_free_func free_func); + +/** Free the list starting with 'head' + */ +CMARK_GFM_EXPORT +void cmark_llist_free (cmark_mem * mem, + cmark_llist * head); + +/** + * ## Creating and Destroying Nodes + */ + +/** Creates a new node of type 'type'. Note that the node may have + * other required properties, which it is the caller's responsibility + * to assign. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_new(cmark_node_type type); + +/** Same as `cmark_node_new`, but explicitly listing the memory + * allocator used to allocate the node. Note: be sure to use the same + * allocator for every node in a tree, or bad things can happen. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem(cmark_node_type type, + cmark_mem *mem); + +CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_ext(cmark_node_type type, + cmark_syntax_extension *extension); + +CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem_and_ext(cmark_node_type type, + cmark_mem *mem, + cmark_syntax_extension *extension); + +/** Frees the memory allocated for a node and any children. + */ +CMARK_GFM_EXPORT void cmark_node_free(cmark_node *node); + +/** + * ## Tree Traversal + */ + +/** Returns the next node in the sequence after 'node', or NULL if + * there is none. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_next(cmark_node *node); + +/** Returns the previous node in the sequence after 'node', or NULL if + * there is none. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_previous(cmark_node *node); + +/** Returns the parent of 'node', or NULL if there is none. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_parent(cmark_node *node); + +/** Returns the first child of 'node', or NULL if 'node' has no children. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_first_child(cmark_node *node); + +/** Returns the last child of 'node', or NULL if 'node' has no children. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_last_child(cmark_node *node); + +/** Returns the footnote reference of 'node', or NULL if 'node' doesn't have a + * footnote reference. + */ +CMARK_GFM_EXPORT cmark_node *cmark_node_parent_footnote_def(cmark_node *node); + +/** + * ## Iterator + * + * An iterator will walk through a tree of nodes, starting from a root + * node, returning one node at a time, together with information about + * whether the node is being entered or exited. The iterator will + * first descend to a child node, if there is one. When there is no + * child, the iterator will go to the next sibling. When there is no + * next sibling, the iterator will return to the parent (but with + * a 'cmark_event_type' of `CMARK_EVENT_EXIT`). The iterator will + * return `CMARK_EVENT_DONE` when it reaches the root node again. + * One natural application is an HTML renderer, where an `ENTER` event + * outputs an open tag and an `EXIT` event outputs a close tag. + * An iterator might also be used to transform an AST in some systematic + * way, for example, turning all level-3 headings into regular paragraphs. + * + * void + * usage_example(cmark_node *root) { + * cmark_event_type ev_type; + * cmark_iter *iter = cmark_iter_new(root); + * + * while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { + * cmark_node *cur = cmark_iter_get_node(iter); + * // Do something with `cur` and `ev_type` + * } + * + * cmark_iter_free(iter); + * } + * + * Iterators will never return `EXIT` events for leaf nodes, which are nodes + * of type: + * + * * CMARK_NODE_HTML_BLOCK + * * CMARK_NODE_THEMATIC_BREAK + * * CMARK_NODE_CODE_BLOCK + * * CMARK_NODE_TEXT + * * CMARK_NODE_SOFTBREAK + * * CMARK_NODE_LINEBREAK + * * CMARK_NODE_CODE + * * CMARK_NODE_HTML_INLINE + * + * Nodes must only be modified after an `EXIT` event, or an `ENTER` event for + * leaf nodes. + */ + +typedef enum { + CMARK_EVENT_NONE, + CMARK_EVENT_DONE, + CMARK_EVENT_ENTER, + CMARK_EVENT_EXIT +} cmark_event_type; + +/** Creates a new iterator starting at 'root'. The current node and event + * type are undefined until 'cmark_iter_next' is called for the first time. + * The memory allocated for the iterator should be released using + * 'cmark_iter_free' when it is no longer needed. + */ +CMARK_GFM_EXPORT +cmark_iter *cmark_iter_new(cmark_node *root); + +/** Frees the memory allocated for an iterator. + */ +CMARK_GFM_EXPORT +void cmark_iter_free(cmark_iter *iter); + +/** Advances to the next node and returns the event type (`CMARK_EVENT_ENTER`, + * `CMARK_EVENT_EXIT` or `CMARK_EVENT_DONE`). + */ +CMARK_GFM_EXPORT +cmark_event_type cmark_iter_next(cmark_iter *iter); + +/** Returns the current node. + */ +CMARK_GFM_EXPORT +cmark_node *cmark_iter_get_node(cmark_iter *iter); + +/** Returns the current event type. + */ +CMARK_GFM_EXPORT +cmark_event_type cmark_iter_get_event_type(cmark_iter *iter); + +/** Returns the root node. + */ +CMARK_GFM_EXPORT +cmark_node *cmark_iter_get_root(cmark_iter *iter); + +/** Resets the iterator so that the current node is 'current' and + * the event type is 'event_type'. The new current node must be a + * descendant of the root node or the root node itself. + */ +CMARK_GFM_EXPORT +void cmark_iter_reset(cmark_iter *iter, cmark_node *current, + cmark_event_type event_type); + +/** + * ## Accessors + */ + +/** Returns the user data of 'node'. + */ +CMARK_GFM_EXPORT void *cmark_node_get_user_data(cmark_node *node); + +/** Sets arbitrary user data for 'node'. Returns 1 on success, + * 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_user_data(cmark_node *node, void *user_data); + +/** Set free function for user data */ +CMARK_GFM_EXPORT +int cmark_node_set_user_data_free_func(cmark_node *node, + cmark_free_func free_func); + +/** Returns the type of 'node', or `CMARK_NODE_NONE` on error. + */ +CMARK_GFM_EXPORT cmark_node_type cmark_node_get_type(cmark_node *node); + +/** Like 'cmark_node_get_type', but returns a string representation + of the type, or `""`. + */ +CMARK_GFM_EXPORT +const char *cmark_node_get_type_string(cmark_node *node); + +/** Returns the string contents of 'node', or an empty + string if none is set. Returns NULL if called on a + node that does not have string content. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_literal(cmark_node *node); + +/** Sets the string contents of 'node'. Returns 1 on success, + * 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_literal(cmark_node *node, const char *content); + +/** Returns the heading level of 'node', or 0 if 'node' is not a heading. + */ +CMARK_GFM_EXPORT int cmark_node_get_heading_level(cmark_node *node); + +/* For backwards compatibility */ +#define cmark_node_get_header_level cmark_node_get_heading_level +#define cmark_node_set_header_level cmark_node_set_heading_level + +/** Sets the heading level of 'node', returning 1 on success and 0 on error. + */ +CMARK_GFM_EXPORT int cmark_node_set_heading_level(cmark_node *node, int level); + +/** Returns the list type of 'node', or `CMARK_NO_LIST` if 'node' + * is not a list. + */ +CMARK_GFM_EXPORT cmark_list_type cmark_node_get_list_type(cmark_node *node); + +/** Sets the list type of 'node', returning 1 on success and 0 on error. + */ +CMARK_GFM_EXPORT int cmark_node_set_list_type(cmark_node *node, + cmark_list_type type); + +/** Returns the list delimiter type of 'node', or `CMARK_NO_DELIM` if 'node' + * is not a list. + */ +CMARK_GFM_EXPORT cmark_delim_type cmark_node_get_list_delim(cmark_node *node); + +/** Sets the list delimiter type of 'node', returning 1 on success and 0 + * on error. + */ +CMARK_GFM_EXPORT int cmark_node_set_list_delim(cmark_node *node, + cmark_delim_type delim); + +/** Returns starting number of 'node', if it is an ordered list, otherwise 0. + */ +CMARK_GFM_EXPORT int cmark_node_get_list_start(cmark_node *node); + +/** Sets starting number of 'node', if it is an ordered list. Returns 1 + * on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_list_start(cmark_node *node, int start); + +/** Returns 1 if 'node' is a tight list, 0 otherwise. + */ +CMARK_GFM_EXPORT int cmark_node_get_list_tight(cmark_node *node); + +/** Sets the "tightness" of a list. Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight); + +/** + * Returns item index of 'node'. This is only used when rendering output + * formats such as commonmark, which need to output the index. It is not + * required for formats such as html or latex. + */ +CMARK_GFM_EXPORT int cmark_node_get_item_index(cmark_node *node); + +/** Sets item index of 'node'. Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx); + +/** Returns the info string from a fenced code block. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_fence_info(cmark_node *node); + +/** Sets the info string in a fenced code block, returning 1 on + * success and 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_fence_info(cmark_node *node, const char *info); + +/** Sets code blocks fencing details + */ +CMARK_GFM_EXPORT int cmark_node_set_fenced(cmark_node * node, int fenced, + int length, int offset, char character); + +/** Returns code blocks fencing details + */ +CMARK_GFM_EXPORT int cmark_node_get_fenced(cmark_node *node, int *length, int *offset, char *character); + +/** Returns the URL of a link or image 'node', or an empty string + if no URL is set. Returns NULL if called on a node that is + not a link or image. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_url(cmark_node *node); + +/** Sets the URL of a link or image 'node'. Returns 1 on success, + * 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_url(cmark_node *node, const char *url); + +/** Returns the title of a link or image 'node', or an empty + string if no title is set. Returns NULL if called on a node + that is not a link or image. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_title(cmark_node *node); + +/** Sets the title of a link or image 'node'. Returns 1 on success, + * 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_title(cmark_node *node, const char *title); + +/** Returns the literal "on enter" text for a custom 'node', or + an empty string if no on_enter is set. Returns NULL if called + on a non-custom node. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_on_enter(cmark_node *node); + +/** Sets the literal text to render "on enter" for a custom 'node'. + Any children of the node will be rendered after this text. + Returns 1 on success 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_on_enter(cmark_node *node, + const char *on_enter); + +/** Returns the literal "on exit" text for a custom 'node', or + an empty string if no on_exit is set. Returns NULL if + called on a non-custom node. + */ +CMARK_GFM_EXPORT const char *cmark_node_get_on_exit(cmark_node *node); + +/** Sets the literal text to render "on exit" for a custom 'node'. + Any children of the node will be rendered before this text. + Returns 1 on success 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_set_on_exit(cmark_node *node, const char *on_exit); + +/** Returns the line on which 'node' begins. + */ +CMARK_GFM_EXPORT int cmark_node_get_start_line(cmark_node *node); + +/** Returns the column at which 'node' begins. + */ +CMARK_GFM_EXPORT int cmark_node_get_start_column(cmark_node *node); + +/** Returns the line on which 'node' ends. + */ +CMARK_GFM_EXPORT int cmark_node_get_end_line(cmark_node *node); + +/** Returns the column at which 'node' ends. + */ +CMARK_GFM_EXPORT int cmark_node_get_end_column(cmark_node *node); + +/** + * ## Tree Manipulation + */ + +/** Unlinks a 'node', removing it from the tree, but not freeing its + * memory. (Use 'cmark_node_free' for that.) + */ +CMARK_GFM_EXPORT void cmark_node_unlink(cmark_node *node); + +/** Inserts 'sibling' before 'node'. Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_insert_before(cmark_node *node, + cmark_node *sibling); + +/** Inserts 'sibling' after 'node'. Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_insert_after(cmark_node *node, cmark_node *sibling); + +/** Replaces 'oldnode' with 'newnode' and unlinks 'oldnode' (but does + * not free its memory). + * Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_replace(cmark_node *oldnode, cmark_node *newnode); + +/** Adds 'child' to the beginning of the children of 'node'. + * Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_prepend_child(cmark_node *node, cmark_node *child); + +/** Adds 'child' to the end of the children of 'node'. + * Returns 1 on success, 0 on failure. + */ +CMARK_GFM_EXPORT int cmark_node_append_child(cmark_node *node, cmark_node *child); + +/** Consolidates adjacent text nodes. + */ +CMARK_GFM_EXPORT void cmark_consolidate_text_nodes(cmark_node *root); + +/** Ensures a node and all its children own their own chunk memory. + */ +CMARK_GFM_EXPORT void cmark_node_own(cmark_node *root); + +/** + * ## Parsing + * + * Simple interface: + * + * cmark_node *document = cmark_parse_document("Hello *world*", 13, + * CMARK_OPT_DEFAULT); + * + * Streaming interface: + * + * cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT); + * FILE *fp = fopen("myfile.md", "rb"); + * while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { + * cmark_parser_feed(parser, buffer, bytes); + * if (bytes < sizeof(buffer)) { + * break; + * } + * } + * document = cmark_parser_finish(parser); + * cmark_parser_free(parser); + */ + +/** Creates a new parser object. + */ +CMARK_GFM_EXPORT +cmark_parser *cmark_parser_new(int options); + +/** Creates a new parser object with the given memory allocator + */ +CMARK_GFM_EXPORT +cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem); + +/** Frees memory allocated for a parser object. + */ +CMARK_GFM_EXPORT +void cmark_parser_free(cmark_parser *parser); + +/** Feeds a string of length 'len' to 'parser'. + */ +CMARK_GFM_EXPORT +void cmark_parser_feed(cmark_parser *parser, const char *buffer, size_t len); + +/** Finish parsing and return a pointer to a tree of nodes. + */ +CMARK_GFM_EXPORT +cmark_node *cmark_parser_finish(cmark_parser *parser); + +/** Parse a CommonMark document in 'buffer' of length 'len'. + * Returns a pointer to a tree of nodes. The memory allocated for + * the node tree should be released using 'cmark_node_free' + * when it is no longer needed. + */ +CMARK_GFM_EXPORT +cmark_node *cmark_parse_document(const char *buffer, size_t len, int options); + +/** Parse a CommonMark document in file 'f', returning a pointer to + * a tree of nodes. The memory allocated for the node tree should be + * released using 'cmark_node_free' when it is no longer needed. + */ +CMARK_GFM_EXPORT +cmark_node *cmark_parse_file(FILE *f, int options); + +/** + * ## Rendering + */ + +/** Render a 'node' tree as XML. It is the caller's responsibility + * to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_render_xml(cmark_node *root, int options); + +/** As for 'cmark_render_xml', but specifying the allocator to use for + * the resulting string. + */ +CMARK_GFM_EXPORT +char *cmark_render_xml_with_mem(cmark_node *root, int options, cmark_mem *mem); + +/** Render a 'node' tree as an HTML fragment. It is up to the user + * to add an appropriate header and footer. It is the caller's + * responsibility to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_render_html(cmark_node *root, int options, cmark_llist *extensions); + +/** As for 'cmark_render_html', but specifying the allocator to use for + * the resulting string. + */ +CMARK_GFM_EXPORT +char *cmark_render_html_with_mem(cmark_node *root, int options, cmark_llist *extensions, cmark_mem *mem); + +/** Render a 'node' tree as a groff man page, without the header. + * It is the caller's responsibility to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_render_man(cmark_node *root, int options, int width); + +/** As for 'cmark_render_man', but specifying the allocator to use for + * the resulting string. + */ +CMARK_GFM_EXPORT +char *cmark_render_man_with_mem(cmark_node *root, int options, int width, cmark_mem *mem); + +/** Render a 'node' tree as a commonmark document. + * It is the caller's responsibility to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_render_commonmark(cmark_node *root, int options, int width); + +/** As for 'cmark_render_commonmark', but specifying the allocator to use for + * the resulting string. + */ +CMARK_GFM_EXPORT +char *cmark_render_commonmark_with_mem(cmark_node *root, int options, int width, cmark_mem *mem); + +/** Render a 'node' tree as a plain text document. + * It is the caller's responsibility to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_render_plaintext(cmark_node *root, int options, int width); + +/** As for 'cmark_render_plaintext', but specifying the allocator to use for + * the resulting string. + */ +CMARK_GFM_EXPORT +char *cmark_render_plaintext_with_mem(cmark_node *root, int options, int width, cmark_mem *mem); + +/** Render a 'node' tree as a LaTeX document. + * It is the caller's responsibility to free the returned buffer. + */ +CMARK_GFM_EXPORT +char *cmark_render_latex(cmark_node *root, int options, int width); + +/** As for 'cmark_render_latex', but specifying the allocator to use for + * the resulting string. + */ +CMARK_GFM_EXPORT +char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmark_mem *mem); + +/** + * ## Options + */ + +/** Default options. + */ +#define CMARK_OPT_DEFAULT 0 + +/** + * ### Options affecting rendering + */ + +/** Include a `data-sourcepos` attribute on all block elements. + */ +#define CMARK_OPT_SOURCEPOS (1 << 1) + +/** Render `softbreak` elements as hard line breaks. + */ +#define CMARK_OPT_HARDBREAKS (1 << 2) + +/** `CMARK_OPT_SAFE` is defined here for API compatibility, + but it no longer has any effect. "Safe" mode is now the default: + set `CMARK_OPT_UNSAFE` to disable it. + */ +#define CMARK_OPT_SAFE (1 << 3) + +/** Render raw HTML and unsafe links (`javascript:`, `vbscript:`, + * `file:`, and `data:`, except for `image/png`, `image/gif`, + * `image/jpeg`, or `image/webp` mime types). By default, + * raw HTML is replaced by a placeholder HTML comment. Unsafe + * links are replaced by empty strings. + */ +#define CMARK_OPT_UNSAFE (1 << 17) + +/** Render `softbreak` elements as spaces. + */ +#define CMARK_OPT_NOBREAKS (1 << 4) + +/** + * ### Options affecting parsing + */ + +/** Legacy option (no effect). + */ +#define CMARK_OPT_NORMALIZE (1 << 8) + +/** Validate UTF-8 in the input before parsing, replacing illegal + * sequences with the replacement character U+FFFD. + */ +#define CMARK_OPT_VALIDATE_UTF8 (1 << 9) + +/** Convert straight quotes to curly, --- to em dashes, -- to en dashes. + */ +#define CMARK_OPT_SMART (1 << 10) + +/** Use GitHub-style
 tags for code blocks instead of 
.
+ */
+#define CMARK_OPT_GITHUB_PRE_LANG (1 << 11)
+
+/** Be liberal in interpreting inline HTML tags.
+ */
+#define CMARK_OPT_LIBERAL_HTML_TAG (1 << 12)
+
+/** Parse footnotes.
+ */
+#define CMARK_OPT_FOOTNOTES (1 << 13)
+
+/** Only parse strikethroughs if surrounded by exactly 2 tildes.
+ * Gives some compatibility with redcarpet.
+ */
+#define CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE (1 << 14)
+
+/** Use style attributes to align table cells instead of align attributes.
+ */
+#define CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES (1 << 15)
+
+/** Include the remainder of the info string in code blocks in
+ * a separate attribute.
+ */
+#define CMARK_OPT_FULL_INFO_STRING (1 << 16)
+
+/**
+ * ## Version information
+ */
+
+/** The library version as integer for runtime checks. Also available as
+ * macro CMARK_VERSION for compile time checks.
+ *
+ * * Bits 16-23 contain the major version.
+ * * Bits 8-15 contain the minor version.
+ * * Bits 0-7 contain the patchlevel.
+ *
+ * In hexadecimal format, the number 0x010203 represents version 1.2.3.
+ */
+CMARK_GFM_EXPORT
+int cmark_version(void);
+
+/** The library version string for runtime checks. Also available as
+ * macro CMARK_VERSION_STRING for compile time checks.
+ */
+CMARK_GFM_EXPORT
+const char *cmark_version_string(void);
+
+/** # AUTHORS
+ *
+ * John MacFarlane, Vicent Marti,  Kārlis Gaņģis, Nick Wellnhofer.
+ */
+
+#ifndef CMARK_NO_SHORT_NAMES
+#define NODE_DOCUMENT CMARK_NODE_DOCUMENT
+#define NODE_BLOCK_QUOTE CMARK_NODE_BLOCK_QUOTE
+#define NODE_LIST CMARK_NODE_LIST
+#define NODE_ITEM CMARK_NODE_ITEM
+#define NODE_CODE_BLOCK CMARK_NODE_CODE_BLOCK
+#define NODE_HTML_BLOCK CMARK_NODE_HTML_BLOCK
+#define NODE_CUSTOM_BLOCK CMARK_NODE_CUSTOM_BLOCK
+#define NODE_PARAGRAPH CMARK_NODE_PARAGRAPH
+#define NODE_HEADING CMARK_NODE_HEADING
+#define NODE_HEADER CMARK_NODE_HEADER
+#define NODE_THEMATIC_BREAK CMARK_NODE_THEMATIC_BREAK
+#define NODE_HRULE CMARK_NODE_HRULE
+#define NODE_TEXT CMARK_NODE_TEXT
+#define NODE_SOFTBREAK CMARK_NODE_SOFTBREAK
+#define NODE_LINEBREAK CMARK_NODE_LINEBREAK
+#define NODE_CODE CMARK_NODE_CODE
+#define NODE_HTML_INLINE CMARK_NODE_HTML_INLINE
+#define NODE_CUSTOM_INLINE CMARK_NODE_CUSTOM_INLINE
+#define NODE_EMPH CMARK_NODE_EMPH
+#define NODE_STRONG CMARK_NODE_STRONG
+#define NODE_LINK CMARK_NODE_LINK
+#define NODE_IMAGE CMARK_NODE_IMAGE
+#define BULLET_LIST CMARK_BULLET_LIST
+#define ORDERED_LIST CMARK_ORDERED_LIST
+#define PERIOD_DELIM CMARK_PERIOD_DELIM
+#define PAREN_DELIM CMARK_PAREN_DELIM
+#endif
+
+typedef int32_t bufsize_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdparty/cmark-gfm/include/cmark-gfm_export.h b/3rdparty/cmark-gfm/include/cmark-gfm_export.h
new file mode 100644
index 0000000..29d31b8
--- /dev/null
+++ b/3rdparty/cmark-gfm/include/cmark-gfm_export.h
@@ -0,0 +1,43 @@
+
+#ifndef CMARK_GFM_EXPORT_H
+#define CMARK_GFM_EXPORT_H
+
+#ifdef CMARK_GFM_STATIC_DEFINE
+#  define CMARK_GFM_EXPORT
+#  define CMARK_GFM_NO_EXPORT
+#else
+#  ifndef CMARK_GFM_EXPORT
+#    ifdef libcmark_gfm_static_EXPORTS
+        /* We are building this library */
+#      define CMARK_GFM_EXPORT 
+#    else
+        /* We are using this library */
+#      define CMARK_GFM_EXPORT 
+#    endif
+#  endif
+
+#  ifndef CMARK_GFM_NO_EXPORT
+#    define CMARK_GFM_NO_EXPORT 
+#  endif
+#endif
+
+#ifndef CMARK_GFM_DEPRECATED
+#  define CMARK_GFM_DEPRECATED __declspec(deprecated)
+#endif
+
+#ifndef CMARK_GFM_DEPRECATED_EXPORT
+#  define CMARK_GFM_DEPRECATED_EXPORT CMARK_GFM_EXPORT CMARK_GFM_DEPRECATED
+#endif
+
+#ifndef CMARK_GFM_DEPRECATED_NO_EXPORT
+#  define CMARK_GFM_DEPRECATED_NO_EXPORT CMARK_GFM_NO_EXPORT CMARK_GFM_DEPRECATED
+#endif
+
+/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */
+#if 0 /* DEFINE_NO_DEPRECATED */
+#  ifndef CMARK_GFM_NO_DEPRECATED
+#    define CMARK_GFM_NO_DEPRECATED
+#  endif
+#endif
+
+#endif /* CMARK_GFM_EXPORT_H */
diff --git a/3rdparty/cmark-gfm/include/cmark-gfm_version.h b/3rdparty/cmark-gfm/include/cmark-gfm_version.h
new file mode 100644
index 0000000..cf4dfee
--- /dev/null
+++ b/3rdparty/cmark-gfm/include/cmark-gfm_version.h
@@ -0,0 +1,7 @@
+#ifndef CMARK_GFM_VERSION_H
+#define CMARK_GFM_VERSION_H
+
+#define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 13)
+#define CMARK_GFM_VERSION_STRING "0.29.0.gfm.13"
+
+#endif
diff --git a/3rdparty/cmark-gfm/lib/cmake-gfm-extensions/cmark-gfm-extensions-release.cmake b/3rdparty/cmark-gfm/lib/cmake-gfm-extensions/cmark-gfm-extensions-release.cmake
new file mode 100644
index 0000000..6b7a72d
--- /dev/null
+++ b/3rdparty/cmark-gfm/lib/cmake-gfm-extensions/cmark-gfm-extensions-release.cmake
@@ -0,0 +1,19 @@
+#----------------------------------------------------------------
+# Generated CMake target import file for configuration "Release".
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Import target "libcmark-gfm-extensions_static" for configuration "Release"
+set_property(TARGET libcmark-gfm-extensions_static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(libcmark-gfm-extensions_static PROPERTIES
+  IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libcmark-gfm-extensions.a"
+  )
+
+list(APPEND _cmake_import_check_targets libcmark-gfm-extensions_static )
+list(APPEND _cmake_import_check_files_for_libcmark-gfm-extensions_static "${_IMPORT_PREFIX}/lib/libcmark-gfm-extensions.a" )
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
diff --git a/3rdparty/cmark-gfm/lib/cmake-gfm-extensions/cmark-gfm-extensions.cmake b/3rdparty/cmark-gfm/lib/cmake-gfm-extensions/cmark-gfm-extensions.cmake
new file mode 100644
index 0000000..fa842a1
--- /dev/null
+++ b/3rdparty/cmark-gfm/lib/cmake-gfm-extensions/cmark-gfm-extensions.cmake
@@ -0,0 +1,101 @@
+# Generated by CMake
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
+   message(FATAL_ERROR "CMake >= 2.8.0 required")
+endif()
+if(CMAKE_VERSION VERSION_LESS "2.8.3")
+   message(FATAL_ERROR "CMake >= 2.8.3 required")
+endif()
+cmake_policy(PUSH)
+cmake_policy(VERSION 2.8.3...3.28)
+#----------------------------------------------------------------
+# Generated CMake target import file.
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Protect against multiple inclusion, which would fail when already imported targets are added once more.
+set(_cmake_targets_defined "")
+set(_cmake_targets_not_defined "")
+set(_cmake_expected_targets "")
+foreach(_cmake_expected_target IN ITEMS libcmark-gfm-extensions_static)
+  list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
+  if(TARGET "${_cmake_expected_target}")
+    list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
+  else()
+    list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
+  endif()
+endforeach()
+unset(_cmake_expected_target)
+if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
+  unset(_cmake_targets_defined)
+  unset(_cmake_targets_not_defined)
+  unset(_cmake_expected_targets)
+  unset(CMAKE_IMPORT_FILE_VERSION)
+  cmake_policy(POP)
+  return()
+endif()
+if(NOT _cmake_targets_defined STREQUAL "")
+  string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
+  string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
+  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
+endif()
+unset(_cmake_targets_defined)
+unset(_cmake_targets_not_defined)
+unset(_cmake_expected_targets)
+
+
+# Compute the installation prefix relative to this file.
+get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+if(_IMPORT_PREFIX STREQUAL "/")
+  set(_IMPORT_PREFIX "")
+endif()
+
+# Create imported target libcmark-gfm-extensions_static
+add_library(libcmark-gfm-extensions_static STATIC IMPORTED)
+
+# Load information for each installed configuration.
+file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/cmark-gfm-extensions-*.cmake")
+foreach(_cmake_config_file IN LISTS _cmake_config_files)
+  include("${_cmake_config_file}")
+endforeach()
+unset(_cmake_config_file)
+unset(_cmake_config_files)
+
+# Cleanup temporary variables.
+set(_IMPORT_PREFIX)
+
+# Loop over all imported files and verify that they actually exist
+foreach(_cmake_target IN LISTS _cmake_import_check_targets)
+  if(CMAKE_VERSION VERSION_LESS "3.28"
+      OR NOT DEFINED _cmake_import_check_xcframework_for_${_cmake_target}
+      OR NOT IS_DIRECTORY "${_cmake_import_check_xcframework_for_${_cmake_target}}")
+    foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
+      if(NOT EXISTS "${_cmake_file}")
+        message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
+   \"${_cmake_file}\"
+but this file does not exist.  Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty and contained
+   \"${CMAKE_CURRENT_LIST_FILE}\"
+but not all the files it references.
+")
+      endif()
+    endforeach()
+  endif()
+  unset(_cmake_file)
+  unset("_cmake_import_check_files_for_${_cmake_target}")
+endforeach()
+unset(_cmake_target)
+unset(_cmake_import_check_targets)
+
+# This file does not depend on other imported targets which have
+# been exported from the same project but in a separate export set.
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
+cmake_policy(POP)
diff --git a/3rdparty/cmark-gfm/lib/cmake/cmark-gfm-release.cmake b/3rdparty/cmark-gfm/lib/cmake/cmark-gfm-release.cmake
new file mode 100644
index 0000000..c4bd5fa
--- /dev/null
+++ b/3rdparty/cmark-gfm/lib/cmake/cmark-gfm-release.cmake
@@ -0,0 +1,28 @@
+#----------------------------------------------------------------
+# Generated CMake target import file for configuration "Release".
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Import target "cmark-gfm" for configuration "Release"
+set_property(TARGET cmark-gfm APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(cmark-gfm PROPERTIES
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/cmark-gfm.exe"
+  )
+
+list(APPEND _cmake_import_check_targets cmark-gfm )
+list(APPEND _cmake_import_check_files_for_cmark-gfm "${_IMPORT_PREFIX}/bin/cmark-gfm.exe" )
+
+# Import target "libcmark-gfm_static" for configuration "Release"
+set_property(TARGET libcmark-gfm_static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(libcmark-gfm_static PROPERTIES
+  IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libcmark-gfm.a"
+  )
+
+list(APPEND _cmake_import_check_targets libcmark-gfm_static )
+list(APPEND _cmake_import_check_files_for_libcmark-gfm_static "${_IMPORT_PREFIX}/lib/libcmark-gfm.a" )
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
diff --git a/3rdparty/cmark-gfm/lib/cmake/cmark-gfm.cmake b/3rdparty/cmark-gfm/lib/cmake/cmark-gfm.cmake
new file mode 100644
index 0000000..20a966d
--- /dev/null
+++ b/3rdparty/cmark-gfm/lib/cmake/cmark-gfm.cmake
@@ -0,0 +1,104 @@
+# Generated by CMake
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
+   message(FATAL_ERROR "CMake >= 2.8.0 required")
+endif()
+if(CMAKE_VERSION VERSION_LESS "2.8.3")
+   message(FATAL_ERROR "CMake >= 2.8.3 required")
+endif()
+cmake_policy(PUSH)
+cmake_policy(VERSION 2.8.3...3.28)
+#----------------------------------------------------------------
+# Generated CMake target import file.
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Protect against multiple inclusion, which would fail when already imported targets are added once more.
+set(_cmake_targets_defined "")
+set(_cmake_targets_not_defined "")
+set(_cmake_expected_targets "")
+foreach(_cmake_expected_target IN ITEMS cmark-gfm libcmark-gfm_static)
+  list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
+  if(TARGET "${_cmake_expected_target}")
+    list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
+  else()
+    list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
+  endif()
+endforeach()
+unset(_cmake_expected_target)
+if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
+  unset(_cmake_targets_defined)
+  unset(_cmake_targets_not_defined)
+  unset(_cmake_expected_targets)
+  unset(CMAKE_IMPORT_FILE_VERSION)
+  cmake_policy(POP)
+  return()
+endif()
+if(NOT _cmake_targets_defined STREQUAL "")
+  string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
+  string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
+  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
+endif()
+unset(_cmake_targets_defined)
+unset(_cmake_targets_not_defined)
+unset(_cmake_expected_targets)
+
+
+# Compute the installation prefix relative to this file.
+get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+if(_IMPORT_PREFIX STREQUAL "/")
+  set(_IMPORT_PREFIX "")
+endif()
+
+# Create imported target cmark-gfm
+add_executable(cmark-gfm IMPORTED)
+
+# Create imported target libcmark-gfm_static
+add_library(libcmark-gfm_static STATIC IMPORTED)
+
+# Load information for each installed configuration.
+file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/cmark-gfm-*.cmake")
+foreach(_cmake_config_file IN LISTS _cmake_config_files)
+  include("${_cmake_config_file}")
+endforeach()
+unset(_cmake_config_file)
+unset(_cmake_config_files)
+
+# Cleanup temporary variables.
+set(_IMPORT_PREFIX)
+
+# Loop over all imported files and verify that they actually exist
+foreach(_cmake_target IN LISTS _cmake_import_check_targets)
+  if(CMAKE_VERSION VERSION_LESS "3.28"
+      OR NOT DEFINED _cmake_import_check_xcframework_for_${_cmake_target}
+      OR NOT IS_DIRECTORY "${_cmake_import_check_xcframework_for_${_cmake_target}}")
+    foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
+      if(NOT EXISTS "${_cmake_file}")
+        message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
+   \"${_cmake_file}\"
+but this file does not exist.  Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty and contained
+   \"${CMAKE_CURRENT_LIST_FILE}\"
+but not all the files it references.
+")
+      endif()
+    endforeach()
+  endif()
+  unset(_cmake_file)
+  unset("_cmake_import_check_files_for_${_cmake_target}")
+endforeach()
+unset(_cmake_target)
+unset(_cmake_import_check_targets)
+
+# This file does not depend on other imported targets which have
+# been exported from the same project but in a separate export set.
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
+cmake_policy(POP)
diff --git a/3rdparty/cmark-gfm/lib/libcmark-gfm-extensions.a b/3rdparty/cmark-gfm/lib/libcmark-gfm-extensions.a
new file mode 100644
index 0000000..e71f96b
Binary files /dev/null and b/3rdparty/cmark-gfm/lib/libcmark-gfm-extensions.a differ
diff --git a/3rdparty/cmark-gfm/lib/libcmark-gfm.a b/3rdparty/cmark-gfm/lib/libcmark-gfm.a
new file mode 100644
index 0000000..fe66334
Binary files /dev/null and b/3rdparty/cmark-gfm/lib/libcmark-gfm.a differ
diff --git a/3rdparty/cmark-gfm/lib/pkgconfig/libcmark-gfm.pc b/3rdparty/cmark-gfm/lib/pkgconfig/libcmark-gfm.pc
new file mode 100644
index 0000000..efb23d6
--- /dev/null
+++ b/3rdparty/cmark-gfm/lib/pkgconfig/libcmark-gfm.pc
@@ -0,0 +1,10 @@
+prefix=F:/SourceCode/ctai/3rdparty/cmark-gfm
+exec_prefix=F:/SourceCode/ctai/3rdparty/cmark-gfm
+libdir=F:/SourceCode/ctai/3rdparty/cmark-gfm/lib
+includedir=F:/SourceCode/ctai/3rdparty/cmark-gfm/include
+
+Name: libcmark-gfm
+Description: CommonMark parsing, rendering, and manipulation with GitHub Flavored Markdown extensions
+Version: 0.29.0.gfm.13
+Libs: -L${libdir} -lcmark-gfm -lcmark-gfm-extensions
+Cflags: -I${includedir}
diff --git a/3rdparty/cmark-gfm/share/man/man1/cmark-gfm.1 b/3rdparty/cmark-gfm/share/man/man1/cmark-gfm.1
new file mode 100644
index 0000000..4fca627
--- /dev/null
+++ b/3rdparty/cmark-gfm/share/man/man1/cmark-gfm.1
@@ -0,0 +1,78 @@
+.TH "cmark-gfm" "1" "March 24, 2016" "LOCAL" "General Commands Manual"
+.SH "NAME"
+\fBcmark\fR
+\- convert CommonMark formatted text with GitHub Flavored Markdown extensions to HTML
+.SH "SYNOPSIS"
+.HP 6n
+\fBcmark-gfm\fR
+[options]
+file*
+.SH "DESCRIPTION"
+\fBcmark-gfm\fR
+converts Markdown formatted plain text to either HTML, groff man,
+CommonMark XML, LaTeX, or CommonMark, using the conventions
+described in the CommonMark spec.  It reads input from \fIstdin\fR
+or the specified files (concatenating their contents) and writes
+output to \fIstdout\fR.
+.SH "OPTIONS"
+.TP 12n
+.B \-\-to, \-t \f[I]FORMAT\f[]
+Specify output format (\f[C]html\f[], \f[C]man\f[], \f[C]xml\f[],
+\f[C]latex\f[], \f[C]commonmark\f[]).
+.TP 12n
+.B \-\-width \f[I]WIDTH\f[]
+Specify a column width to which to wrap the output. For no wrapping, use
+the value 0 (the default).  This option currently only affects the
+commonmark, latex, and man renderers.
+.TP 12n
+.B \-\-hardbreaks
+Render soft breaks (newlines inside paragraphs in the CommonMark source)
+as hard line breaks in the target format.  If this option is specified,
+hard wrapping is disabled for CommonMark output, regardless of the value
+given with \-\-width.
+.TP 12n
+.B \-\-nobreaks
+Render soft breaks as spaces.  If this option is specified,
+hard wrapping is disabled for all output formats, regardless of the value
+given with \-\-width.
+.TP 12n
+.B \-\-sourcepos
+Include source position attribute.
+.TP 12n
+.B \-\-normalize
+Consolidate adjacent text nodes.
+.TP 12n
+.B \-\-extension, \-e \f[I]EXTENSION_NAME\f[]
+Specify an extension name to use.
+.TP 12n
+.B \-\-list\-extensions
+List available extensions and quit.
+.TP 12n
+.B \-\-validate-utf8
+Validate UTF-8, replacing illegal sequences with U+FFFD.
+.TP 12n
+.B \-\-smart
+Use smart punctuation.  Straight double and single quotes will
+be rendered as curly quotes, depending on their position.
+\f[C]\-\-\f[] will be rendered as an en-dash.
+\f[C]\-\-\-\f[] will be rendered as an em-dash.
+\f[C]...\f[] will be rendered as ellipses.
+.TP 12n
+.B \-\-unsafe
+Render raw HTML and potentially dangerous URLs.
+(Raw HTML is not replaced by a placeholder comment; potentially
+dangerous URLs are not replaced by empty strings.)  Dangerous
+URLs are those that begin with `javascript:`, `vbscript:`,
+`file:`, or `data:` (except for `image/png`, `image/gif`,
+`image/jpeg`, or `image/webp` mime types).
+.TP 12n
+.B \-\-help
+Print usage information.
+.TP 12n
+.B \-\-version
+Print version.
+.SH "AUTHORS"
+John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
+.SH "SEE ALSO"
+.PP
+CommonMark spec:  \f[C]http://spec.commonmark.org\f[].
diff --git a/3rdparty/cmark-gfm/share/man/man3/cmark-gfm.3 b/3rdparty/cmark-gfm/share/man/man3/cmark-gfm.3
new file mode 100644
index 0000000..001b7c7
--- /dev/null
+++ b/3rdparty/cmark-gfm/share/man/man3/cmark-gfm.3
@@ -0,0 +1,1041 @@
+.TH cmark-gfm 3 "April 08, 2019" "LOCAL" "Library Functions Manual"
+.SH
+NAME
+.PP
+\f[B]cmark\-gfm\f[] \- CommonMark parsing, manipulating, and rendering
+
+.SH
+DESCRIPTION
+.SS
+Simple Interface
+
+.PP
+\fIchar *\f[] \fBcmark_markdown_to_html\f[](\fIconst char *text\f[], \fIsize_t len\f[], \fIint options\f[])
+
+.PP
+Convert \f[I]text\f[] (assumed to be a UTF\-8 encoded string with length
+\f[I]len\f[]) from CommonMark Markdown to HTML, returning a
+null\-terminated, UTF\-8\-encoded string. It is the caller's
+responsibility to free the returned buffer.
+
+.SS
+Node Structure
+
+.PP
+.nf
+\fC
+.RS 0n
+typedef enum {
+  /* Error status */
+  CMARK_NODE_NONE = 0x0000,
+
+  /* Block */
+  CMARK_NODE_DOCUMENT       = CMARK_NODE_TYPE_BLOCK | 0x0001,
+  CMARK_NODE_BLOCK_QUOTE    = CMARK_NODE_TYPE_BLOCK | 0x0002,
+  CMARK_NODE_LIST           = CMARK_NODE_TYPE_BLOCK | 0x0003,
+  CMARK_NODE_ITEM           = CMARK_NODE_TYPE_BLOCK | 0x0004,
+  CMARK_NODE_CODE_BLOCK     = CMARK_NODE_TYPE_BLOCK | 0x0005,
+  CMARK_NODE_HTML_BLOCK     = CMARK_NODE_TYPE_BLOCK | 0x0006,
+  CMARK_NODE_CUSTOM_BLOCK   = CMARK_NODE_TYPE_BLOCK | 0x0007,
+  CMARK_NODE_PARAGRAPH      = CMARK_NODE_TYPE_BLOCK | 0x0008,
+  CMARK_NODE_HEADING        = CMARK_NODE_TYPE_BLOCK | 0x0009,
+  CMARK_NODE_THEMATIC_BREAK = CMARK_NODE_TYPE_BLOCK | 0x000a,
+  CMARK_NODE_FOOTNOTE_DEFINITION = CMARK_NODE_TYPE_BLOCK | 0x000b,
+
+  /* Inline */
+  CMARK_NODE_TEXT          = CMARK_NODE_TYPE_INLINE | 0x0001,
+  CMARK_NODE_SOFTBREAK     = CMARK_NODE_TYPE_INLINE | 0x0002,
+  CMARK_NODE_LINEBREAK     = CMARK_NODE_TYPE_INLINE | 0x0003,
+  CMARK_NODE_CODE          = CMARK_NODE_TYPE_INLINE | 0x0004,
+  CMARK_NODE_HTML_INLINE   = CMARK_NODE_TYPE_INLINE | 0x0005,
+  CMARK_NODE_CUSTOM_INLINE = CMARK_NODE_TYPE_INLINE | 0x0006,
+  CMARK_NODE_EMPH          = CMARK_NODE_TYPE_INLINE | 0x0007,
+  CMARK_NODE_STRONG        = CMARK_NODE_TYPE_INLINE | 0x0008,
+  CMARK_NODE_LINK          = CMARK_NODE_TYPE_INLINE | 0x0009,
+  CMARK_NODE_IMAGE         = CMARK_NODE_TYPE_INLINE | 0x000a,
+  CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b,
+} cmark_node_type;
+.RE
+\f[]
+.fi
+
+
+
+.PP
+.nf
+\fC
+.RS 0n
+typedef enum {
+  CMARK_NO_LIST,
+  CMARK_BULLET_LIST,
+  CMARK_ORDERED_LIST
+} cmark_list_type;
+.RE
+\f[]
+.fi
+
+
+
+.PP
+.nf
+\fC
+.RS 0n
+typedef enum {
+  CMARK_NO_DELIM,
+  CMARK_PERIOD_DELIM,
+  CMARK_PAREN_DELIM
+} cmark_delim_type;
+.RE
+\f[]
+.fi
+
+
+
+.SS
+Custom memory allocator support
+
+.PP
+.nf
+\fC
+.RS 0n
+typedef struct cmark_mem {
+  void *(*calloc)(size_t, size_t);
+  void *(*realloc)(void *, size_t);
+  void (*free)(void *);
+} cmark_mem;
+.RE
+\f[]
+.fi
+
+.PP
+Defines the memory allocation functions to be used by CMark when parsing
+and allocating a document tree
+
+.PP
+\fIcmark_mem *\f[] \fBcmark_get_default_mem_allocator\f[](\fI\f[])
+
+.PP
+The default memory allocator; uses the system's calloc, realloc and
+free.
+
+.PP
+\fIcmark_mem *\f[] \fBcmark_get_arena_mem_allocator\f[](\fI\f[])
+
+.PP
+An arena allocator; uses system calloc to allocate large slabs of
+memory. Memory in these slabs is not reused at all.
+
+.PP
+\fIvoid\f[] \fBcmark_arena_reset\f[](\fIvoid\f[])
+
+.PP
+Resets the arena allocator, quickly returning all used memory to the
+operating system.
+
+.PP
+\fItypedef\f[] \fBvoid\f[](\fI*cmark_free_func\f[])
+
+.PP
+Callback for freeing user data with a \f[I]cmark_mem\f[] context.
+
+.SS
+Linked list
+
+.PP
+.nf
+\fC
+.RS 0n
+typedef struct _cmark_llist
+{
+  struct _cmark_llist *next;
+  void         *data;
+} cmark_llist;
+.RE
+\f[]
+.fi
+
+.PP
+A generic singly linked list.
+
+.PP
+\fIcmark_llist *\f[] \fBcmark_llist_append\f[](\fIcmark_mem         * mem\f[], \fIcmark_llist       * head\f[], \fIvoid              * data\f[])
+
+.PP
+Append an element to the linked list, return the possibly modified head
+of the list.
+
+.PP
+\fIvoid\f[] \fBcmark_llist_free_full\f[](\fIcmark_mem         * mem\f[], \fIcmark_llist       * head\f[], \fIcmark_free_func     free_func\f[])
+
+.PP
+Free the list starting with \f[I]head\f[], calling \f[I]free_func\f[]
+with the data pointer of each of its elements
+
+.PP
+\fIvoid\f[] \fBcmark_llist_free\f[](\fIcmark_mem         * mem\f[], \fIcmark_llist       * head\f[])
+
+.PP
+Free the list starting with \f[I]head\f[]
+
+.SS
+Creating and Destroying Nodes
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_new\f[](\fIcmark_node_type type\f[])
+
+.PP
+Creates a new node of type \f[I]type\f[]. Note that the node may have
+other required properties, which it is the caller's responsibility to
+assign.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_new_with_mem\f[](\fIcmark_node_type type\f[], \fIcmark_mem *mem\f[])
+
+.PP
+Same as \f[C]cmark_node_new\f[], but explicitly listing the memory
+allocator used to allocate the node. Note: be sure to use the same
+allocator for every node in a tree, or bad things can happen.
+
+.PP
+\fIvoid\f[] \fBcmark_node_free\f[](\fIcmark_node *node\f[])
+
+.PP
+Frees the memory allocated for a node and any children.
+
+.SS
+Tree Traversal
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_next\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the next node in the sequence after \f[I]node\f[], or NULL if
+there is none.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_previous\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the previous node in the sequence after \f[I]node\f[], or NULL
+if there is none.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_parent\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the parent of \f[I]node\f[], or NULL if there is none.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_first_child\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the first child of \f[I]node\f[], or NULL if \f[I]node\f[] has
+no children.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_node_last_child\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the last child of \f[I]node\f[], or NULL if \f[I]node\f[] has no
+children.
+
+.SS
+Iterator
+.PP
+An iterator will walk through a tree of nodes, starting from a root
+node, returning one node at a time, together with information about
+whether the node is being entered or exited. The iterator will first
+descend to a child node, if there is one. When there is no child, the
+iterator will go to the next sibling. When there is no next sibling, the
+iterator will return to the parent (but with a \f[I]cmark_event_type\f[]
+of \f[C]CMARK_EVENT_EXIT\f[]). The iterator will return
+\f[C]CMARK_EVENT_DONE\f[] when it reaches the root node again. One
+natural application is an HTML renderer, where an \f[C]ENTER\f[] event
+outputs an open tag and an \f[C]EXIT\f[] event outputs a close tag. An
+iterator might also be used to transform an AST in some systematic way,
+for example, turning all level\-3 headings into regular paragraphs.
+.IP
+.nf
+\f[C]
+void
+usage_example(cmark_node *root) {
+    cmark_event_type ev_type;
+    cmark_iter *iter = cmark_iter_new(root);
+
+    while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
+        cmark_node *cur = cmark_iter_get_node(iter);
+        // Do something with `cur` and `ev_type`
+    }
+
+    cmark_iter_free(iter);
+}
+\f[]
+.fi
+.PP
+Iterators will never return \f[C]EXIT\f[] events for leaf nodes, which
+are nodes of type:
+.IP \[bu] 2
+CMARK_NODE_HTML_BLOCK
+.IP \[bu] 2
+CMARK_NODE_THEMATIC_BREAK
+.IP \[bu] 2
+CMARK_NODE_CODE_BLOCK
+.IP \[bu] 2
+CMARK_NODE_TEXT
+.IP \[bu] 2
+CMARK_NODE_SOFTBREAK
+.IP \[bu] 2
+CMARK_NODE_LINEBREAK
+.IP \[bu] 2
+CMARK_NODE_CODE
+.IP \[bu] 2
+CMARK_NODE_HTML_INLINE
+.PP
+Nodes must only be modified after an \f[C]EXIT\f[] event, or an
+\f[C]ENTER\f[] event for leaf nodes.
+
+.PP
+.nf
+\fC
+.RS 0n
+typedef enum {
+  CMARK_EVENT_NONE,
+  CMARK_EVENT_DONE,
+  CMARK_EVENT_ENTER,
+  CMARK_EVENT_EXIT
+} cmark_event_type;
+.RE
+\f[]
+.fi
+
+
+
+.PP
+\fIcmark_iter *\f[] \fBcmark_iter_new\f[](\fIcmark_node *root\f[])
+
+.PP
+Creates a new iterator starting at \f[I]root\f[]. The current node and
+event type are undefined until \f[I]cmark_iter_next\f[] is called for
+the first time. The memory allocated for the iterator should be released
+using \f[I]cmark_iter_free\f[] when it is no longer needed.
+
+.PP
+\fIvoid\f[] \fBcmark_iter_free\f[](\fIcmark_iter *iter\f[])
+
+.PP
+Frees the memory allocated for an iterator.
+
+.PP
+\fIcmark_event_type\f[] \fBcmark_iter_next\f[](\fIcmark_iter *iter\f[])
+
+.PP
+Advances to the next node and returns the event type
+(\f[C]CMARK_EVENT_ENTER\f[], \f[C]CMARK_EVENT_EXIT\f[] or
+\f[C]CMARK_EVENT_DONE\f[]).
+
+.PP
+\fIcmark_node *\f[] \fBcmark_iter_get_node\f[](\fIcmark_iter *iter\f[])
+
+.PP
+Returns the current node.
+
+.PP
+\fIcmark_event_type\f[] \fBcmark_iter_get_event_type\f[](\fIcmark_iter *iter\f[])
+
+.PP
+Returns the current event type.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_iter_get_root\f[](\fIcmark_iter *iter\f[])
+
+.PP
+Returns the root node.
+
+.PP
+\fIvoid\f[] \fBcmark_iter_reset\f[](\fIcmark_iter *iter\f[], \fIcmark_node *current\f[], \fIcmark_event_type event_type\f[])
+
+.PP
+Resets the iterator so that the current node is \f[I]current\f[] and the
+event type is \f[I]event_type\f[]. The new current node must be a
+descendant of the root node or the root node itself.
+
+.SS
+Accessors
+
+.PP
+\fIvoid *\f[] \fBcmark_node_get_user_data\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the user data of \f[I]node\f[].
+
+.PP
+\fIint\f[] \fBcmark_node_set_user_data\f[](\fIcmark_node *node\f[], \fIvoid *user_data\f[])
+
+.PP
+Sets arbitrary user data for \f[I]node\f[]. Returns 1 on success, 0 on
+failure.
+
+.PP
+\fIint\f[] \fBcmark_node_set_user_data_free_func\f[](\fIcmark_node *node\f[], \fIcmark_free_func free_func\f[])
+
+.PP
+Set free function for user data */
+
+.PP
+\fIcmark_node_type\f[] \fBcmark_node_get_type\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the type of \f[I]node\f[], or \f[C]CMARK_NODE_NONE\f[] on error.
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_type_string\f[](\fIcmark_node *node\f[])
+
+.PP
+Like \f[I]cmark_node_get_type\f[], but returns a string representation
+of the type, or \f[C]""\f[].
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_literal\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the string contents of \f[I]node\f[], or an empty string if none
+is set. Returns NULL if called on a node that does not have string
+content.
+
+.PP
+\fIint\f[] \fBcmark_node_set_literal\f[](\fIcmark_node *node\f[], \fIconst char *content\f[])
+
+.PP
+Sets the string contents of \f[I]node\f[]. Returns 1 on success, 0 on
+failure.
+
+.PP
+\fIint\f[] \fBcmark_node_get_heading_level\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the heading level of \f[I]node\f[], or 0 if \f[I]node\f[] is not
+a heading.
+
+.PP
+\fIint\f[] \fBcmark_node_set_heading_level\f[](\fIcmark_node *node\f[], \fIint level\f[])
+
+.PP
+Sets the heading level of \f[I]node\f[], returning 1 on success and 0 on
+error.
+
+.PP
+\fIcmark_list_type\f[] \fBcmark_node_get_list_type\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the list type of \f[I]node\f[], or \f[C]CMARK_NO_LIST\f[] if
+\f[I]node\f[] is not a list.
+
+.PP
+\fIint\f[] \fBcmark_node_set_list_type\f[](\fIcmark_node *node\f[], \fIcmark_list_type type\f[])
+
+.PP
+Sets the list type of \f[I]node\f[], returning 1 on success and 0 on
+error.
+
+.PP
+\fIcmark_delim_type\f[] \fBcmark_node_get_list_delim\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the list delimiter type of \f[I]node\f[], or
+\f[C]CMARK_NO_DELIM\f[] if \f[I]node\f[] is not a list.
+
+.PP
+\fIint\f[] \fBcmark_node_set_list_delim\f[](\fIcmark_node *node\f[], \fIcmark_delim_type delim\f[])
+
+.PP
+Sets the list delimiter type of \f[I]node\f[], returning 1 on success
+and 0 on error.
+
+.PP
+\fIint\f[] \fBcmark_node_get_list_start\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns starting number of \f[I]node\f[], if it is an ordered list,
+otherwise 0.
+
+.PP
+\fIint\f[] \fBcmark_node_set_list_start\f[](\fIcmark_node *node\f[], \fIint start\f[])
+
+.PP
+Sets starting number of \f[I]node\f[], if it is an ordered list.
+Returns 1 on success, 0 on failure.
+
+.PP
+\fIint\f[] \fBcmark_node_get_list_tight\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns 1 if \f[I]node\f[] is a tight list, 0 otherwise.
+
+.PP
+\fIint\f[] \fBcmark_node_set_list_tight\f[](\fIcmark_node *node\f[], \fIint tight\f[])
+
+.PP
+Sets the "tightness" of a list. Returns 1 on success, 0 on failure.
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_fence_info\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the info string from a fenced code block.
+
+.PP
+\fIint\f[] \fBcmark_node_set_fence_info\f[](\fIcmark_node *node\f[], \fIconst char *info\f[])
+
+.PP
+Sets the info string in a fenced code block, returning 1 on success
+and 0 on failure.
+
+.PP
+\fIint\f[] \fBcmark_node_set_fenced\f[](\fIcmark_node * node\f[], \fIint fenced\f[], \fIint length\f[], \fIint offset\f[], \fIchar character\f[])
+
+.PP
+Sets code blocks fencing details
+
+.PP
+\fIint\f[] \fBcmark_node_get_fenced\f[](\fIcmark_node *node\f[], \fIint *length\f[], \fIint *offset\f[], \fIchar *character\f[])
+
+.PP
+Returns code blocks fencing details
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_url\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the URL of a link or image \f[I]node\f[], or an empty string if
+no URL is set. Returns NULL if called on a node that is not a link or
+image.
+
+.PP
+\fIint\f[] \fBcmark_node_set_url\f[](\fIcmark_node *node\f[], \fIconst char *url\f[])
+
+.PP
+Sets the URL of a link or image \f[I]node\f[]. Returns 1 on success, 0
+on failure.
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_title\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the title of a link or image \f[I]node\f[], or an empty string
+if no title is set. Returns NULL if called on a node that is not a link
+or image.
+
+.PP
+\fIint\f[] \fBcmark_node_set_title\f[](\fIcmark_node *node\f[], \fIconst char *title\f[])
+
+.PP
+Sets the title of a link or image \f[I]node\f[]. Returns 1 on success, 0
+on failure.
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_on_enter\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the literal "on enter" text for a custom \f[I]node\f[], or an
+empty string if no on_enter is set. Returns NULL if called on a
+non\-custom node.
+
+.PP
+\fIint\f[] \fBcmark_node_set_on_enter\f[](\fIcmark_node *node\f[], \fIconst char *on_enter\f[])
+
+.PP
+Sets the literal text to render "on enter" for a custom \f[I]node\f[].
+Any children of the node will be rendered after this text. Returns 1 on
+success 0 on failure.
+
+.PP
+\fIconst char *\f[] \fBcmark_node_get_on_exit\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the literal "on exit" text for a custom \f[I]node\f[], or an
+empty string if no on_exit is set. Returns NULL if called on a
+non\-custom node.
+
+.PP
+\fIint\f[] \fBcmark_node_set_on_exit\f[](\fIcmark_node *node\f[], \fIconst char *on_exit\f[])
+
+.PP
+Sets the literal text to render "on exit" for a custom \f[I]node\f[].
+Any children of the node will be rendered before this text. Returns 1 on
+success 0 on failure.
+
+.PP
+\fIint\f[] \fBcmark_node_get_start_line\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the line on which \f[I]node\f[] begins.
+
+.PP
+\fIint\f[] \fBcmark_node_get_start_column\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the column at which \f[I]node\f[] begins.
+
+.PP
+\fIint\f[] \fBcmark_node_get_end_line\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the line on which \f[I]node\f[] ends.
+
+.PP
+\fIint\f[] \fBcmark_node_get_end_column\f[](\fIcmark_node *node\f[])
+
+.PP
+Returns the column at which \f[I]node\f[] ends.
+
+.SS
+Tree Manipulation
+
+.PP
+\fIvoid\f[] \fBcmark_node_unlink\f[](\fIcmark_node *node\f[])
+
+.PP
+Unlinks a \f[I]node\f[], removing it from the tree, but not freeing its
+memory. (Use \f[I]cmark_node_free\f[] for that.)
+
+.PP
+\fIint\f[] \fBcmark_node_insert_before\f[](\fIcmark_node *node\f[], \fIcmark_node *sibling\f[])
+
+.PP
+Inserts \f[I]sibling\f[] before \f[I]node\f[]. Returns 1 on success, 0
+on failure.
+
+.PP
+\fIint\f[] \fBcmark_node_insert_after\f[](\fIcmark_node *node\f[], \fIcmark_node *sibling\f[])
+
+.PP
+Inserts \f[I]sibling\f[] after \f[I]node\f[]. Returns 1 on success, 0 on
+failure.
+
+.PP
+\fIint\f[] \fBcmark_node_replace\f[](\fIcmark_node *oldnode\f[], \fIcmark_node *newnode\f[])
+
+.PP
+Replaces \f[I]oldnode\f[] with \f[I]newnode\f[] and unlinks
+\f[I]oldnode\f[] (but does not free its memory). Returns 1 on success, 0
+on failure.
+
+.PP
+\fIint\f[] \fBcmark_node_prepend_child\f[](\fIcmark_node *node\f[], \fIcmark_node *child\f[])
+
+.PP
+Adds \f[I]child\f[] to the beginning of the children of \f[I]node\f[].
+Returns 1 on success, 0 on failure.
+
+.PP
+\fIint\f[] \fBcmark_node_append_child\f[](\fIcmark_node *node\f[], \fIcmark_node *child\f[])
+
+.PP
+Adds \f[I]child\f[] to the end of the children of \f[I]node\f[].
+Returns 1 on success, 0 on failure.
+
+.PP
+\fIvoid\f[] \fBcmark_consolidate_text_nodes\f[](\fIcmark_node *root\f[])
+
+.PP
+Consolidates adjacent text nodes.
+
+.PP
+\fIvoid\f[] \fBcmark_node_own\f[](\fIcmark_node *root\f[])
+
+.PP
+Ensures a node and all its children own their own chunk memory.
+
+.SS
+Parsing
+.PP
+Simple interface:
+.IP
+.nf
+\f[C]
+cmark_node *document = cmark_parse_document("Hello *world*", 13,
+                                            CMARK_OPT_DEFAULT);
+\f[]
+.fi
+.PP
+Streaming interface:
+.IP
+.nf
+\f[C]
+cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
+FILE *fp = fopen("myfile.md", "rb");
+while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
+	   cmark_parser_feed(parser, buffer, bytes);
+	   if (bytes < sizeof(buffer)) {
+	       break;
+	   }
+}
+document = cmark_parser_finish(parser);
+cmark_parser_free(parser);
+\f[]
+.fi
+
+.PP
+\fIcmark_parser *\f[] \fBcmark_parser_new\f[](\fIint options\f[])
+
+.PP
+Creates a new parser object.
+
+.PP
+\fIcmark_parser *\f[] \fBcmark_parser_new_with_mem\f[](\fIint options\f[], \fIcmark_mem *mem\f[])
+
+.PP
+Creates a new parser object with the given memory allocator
+
+.PP
+\fIvoid\f[] \fBcmark_parser_free\f[](\fIcmark_parser *parser\f[])
+
+.PP
+Frees memory allocated for a parser object.
+
+.PP
+\fIvoid\f[] \fBcmark_parser_feed\f[](\fIcmark_parser *parser\f[], \fIconst char *buffer\f[], \fIsize_t len\f[])
+
+.PP
+Feeds a string of length \f[I]len\f[] to \f[I]parser\f[].
+
+.PP
+\fIcmark_node *\f[] \fBcmark_parser_finish\f[](\fIcmark_parser *parser\f[])
+
+.PP
+Finish parsing and return a pointer to a tree of nodes.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_parse_document\f[](\fIconst char *buffer\f[], \fIsize_t len\f[], \fIint options\f[])
+
+.PP
+Parse a CommonMark document in \f[I]buffer\f[] of length \f[I]len\f[].
+Returns a pointer to a tree of nodes. The memory allocated for the node
+tree should be released using \f[I]cmark_node_free\f[] when it is no
+longer needed.
+
+.PP
+\fIcmark_node *\f[] \fBcmark_parse_file\f[](\fIFILE *f\f[], \fIint options\f[])
+
+.PP
+Parse a CommonMark document in file \f[I]f\f[], returning a pointer to a
+tree of nodes. The memory allocated for the node tree should be released
+using \f[I]cmark_node_free\f[] when it is no longer needed.
+
+.SS
+Rendering
+
+.PP
+\fIchar *\f[] \fBcmark_render_xml\f[](\fIcmark_node *root\f[], \fIint options\f[])
+
+.PP
+Render a \f[I]node\f[] tree as XML. It is the caller's responsibility to
+free the returned buffer.
+
+.PP
+\fIchar *\f[] \fBcmark_render_xml_with_mem\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIcmark_mem *mem\f[])
+
+.PP
+As for \f[I]cmark_render_xml\f[], but specifying the allocator to use
+for the resulting string.
+
+.PP
+\fIchar *\f[] \fBcmark_render_html\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIcmark_llist *extensions\f[])
+
+.PP
+Render a \f[I]node\f[] tree as an HTML fragment. It is up to the user to
+add an appropriate header and footer. It is the caller's responsibility
+to free the returned buffer.
+
+.PP
+\fIchar *\f[] \fBcmark_render_html_with_mem\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIcmark_llist *extensions\f[], \fIcmark_mem *mem\f[])
+
+.PP
+As for \f[I]cmark_render_html\f[], but specifying the allocator to use
+for the resulting string.
+
+.PP
+\fIchar *\f[] \fBcmark_render_man\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
+
+.PP
+Render a \f[I]node\f[] tree as a groff man page, without the header. It
+is the caller's responsibility to free the returned buffer.
+
+.PP
+\fIchar *\f[] \fBcmark_render_man_with_mem\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[], \fIcmark_mem *mem\f[])
+
+.PP
+As for \f[I]cmark_render_man\f[], but specifying the allocator to use
+for the resulting string.
+
+.PP
+\fIchar *\f[] \fBcmark_render_commonmark\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
+
+.PP
+Render a \f[I]node\f[] tree as a commonmark document. It is the caller's
+responsibility to free the returned buffer.
+
+.PP
+\fIchar *\f[] \fBcmark_render_commonmark_with_mem\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[], \fIcmark_mem *mem\f[])
+
+.PP
+As for \f[I]cmark_render_commonmark\f[], but specifying the allocator to
+use for the resulting string.
+
+.PP
+\fIchar *\f[] \fBcmark_render_plaintext\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
+
+.PP
+Render a \f[I]node\f[] tree as a plain text document. It is the caller's
+responsibility to free the returned buffer.
+
+.PP
+\fIchar *\f[] \fBcmark_render_plaintext_with_mem\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[], \fIcmark_mem *mem\f[])
+
+.PP
+As for \f[I]cmark_render_plaintext\f[], but specifying the allocator to
+use for the resulting string.
+
+.PP
+\fIchar *\f[] \fBcmark_render_latex\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
+
+.PP
+Render a \f[I]node\f[] tree as a LaTeX document. It is the caller's
+responsibility to free the returned buffer.
+
+.PP
+\fIchar *\f[] \fBcmark_render_latex_with_mem\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[], \fIcmark_mem *mem\f[])
+
+.PP
+As for \f[I]cmark_render_latex\f[], but specifying the allocator to use
+for the resulting string.
+
+.SS
+Options
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_DEFAULT 0
+.RE
+\f[]
+.fi
+
+.PP
+Default options.
+
+.SS
+Options affecting rendering
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_SOURCEPOS (1 << 1)
+.RE
+\f[]
+.fi
+
+.PP
+Include a \f[C]data\-sourcepos\f[] attribute on all block elements.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_HARDBREAKS (1 << 2)
+.RE
+\f[]
+.fi
+
+.PP
+Render \f[C]softbreak\f[] elements as hard line breaks.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_SAFE (1 << 3)
+.RE
+\f[]
+.fi
+
+.PP
+\f[C]CMARK_OPT_SAFE\f[] is defined here for API compatibility, but it no
+longer has any effect. "Safe" mode is now the default: set
+\f[C]CMARK_OPT_UNSAFE\f[] to disable it.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_UNSAFE (1 << 17)
+.RE
+\f[]
+.fi
+
+.PP
+Render raw HTML and unsafe links (\f[C]javascript:\f[],
+\f[C]vbscript:\f[], \f[C]file:\f[], and \f[C]data:\f[], except for
+\f[C]image/png\f[], \f[C]image/gif\f[], \f[C]image/jpeg\f[], or
+\f[C]image/webp\f[] mime types). By default, raw HTML is replaced by a
+placeholder HTML comment. Unsafe links are replaced by empty strings.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_NOBREAKS (1 << 4)
+.RE
+\f[]
+.fi
+
+.PP
+Render \f[C]softbreak\f[] elements as spaces.
+
+.SS
+Options affecting parsing
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_NORMALIZE (1 << 8)
+.RE
+\f[]
+.fi
+
+.PP
+Legacy option (no effect).
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_VALIDATE_UTF8 (1 << 9)
+.RE
+\f[]
+.fi
+
+.PP
+Validate UTF\-8 in the input before parsing, replacing illegal sequences
+with the replacement character U+FFFD.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_SMART (1 << 10)
+.RE
+\f[]
+.fi
+
+.PP
+Convert straight quotes to curly, \-\-\- to em dashes, \-\- to en
+dashes.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_GITHUB_PRE_LANG (1 << 11)
+.RE
+\f[]
+.fi
+
+.PP
+Use GitHub\-style  tags for code blocks instead of .
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_LIBERAL_HTML_TAG (1 << 12)
+.RE
+\f[]
+.fi
+
+.PP
+Be liberal in interpreting inline HTML tags.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_FOOTNOTES (1 << 13)
+.RE
+\f[]
+.fi
+
+.PP
+Parse footnotes.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE (1 << 14)
+.RE
+\f[]
+.fi
+
+.PP
+Only parse strikethroughs if surrounded by exactly 2 tildes. Gives some
+compatibility with redcarpet.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES (1 << 15)
+.RE
+\f[]
+.fi
+
+.PP
+Use style attributes to align table cells instead of align attributes.
+
+.PP
+.nf
+\fC
+.RS 0n
+#define CMARK_OPT_FULL_INFO_STRING (1 << 16)
+.RE
+\f[]
+.fi
+
+.PP
+Include the remainder of the info string in code blocks in a separate
+attribute.
+
+.SS
+Version information
+
+.PP
+\fIint\f[] \fBcmark_version\f[](\fIvoid\f[])
+
+.PP
+The library version as integer for runtime checks. Also available as
+macro CMARK_VERSION for compile time checks.
+.IP \[bu] 2
+Bits 16\-23 contain the major version.
+.IP \[bu] 2
+Bits 8\-15 contain the minor version.
+.IP \[bu] 2
+Bits 0\-7 contain the patchlevel.
+.PP
+In hexadecimal format, the number 0x010203 represents version 1.2.3.
+
+.PP
+\fIconst char *\f[] \fBcmark_version_string\f[](\fIvoid\f[])
+
+.PP
+The library version string for runtime checks. Also available as macro
+CMARK_VERSION_STRING for compile time checks.
+
+.SH
+AUTHORS
+.PP
+John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 822636d..790c145 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,6 +53,11 @@ else()
 endif()
 
 LIST(APPEND CMAKE_PREFIX_PATH ${QT_DIR} ${MSYS})
+
+#JSON库
+SET(JSON ${PROJECT_SOURCE_DIR}/3rdparty/json)
+include_directories(${JSON}/include)
+
 #CURL
 find_package(CURL REQUIRED)
 #cmark
@@ -67,9 +72,6 @@ MESSAGE(STATUS "QT_DIR_PATH=${QT_DIR}")
 MESSAGE(STATUS "JSON_DIR_PATH=${JSON}")
 MESSAGE(STATUS "----------基础路径输出 END---------------")
 
-#JSON库
-SET(JSON ${PROJECT_SOURCE_DIR}/3rdparty/json)
-include_directories(${JSON}/include)
 
 
 #增加sui的cpp
@@ -135,12 +137,12 @@ configure_file (version.h.in version.h @ONLY)
 SET(CMAKE_EXE_LINKER_FLAGS -static)
 
 target_link_libraries(
-    ${PROJECT_NAME} PRIVATE 
+    ${PROJECT_NAME} PRIVATE
     Qt6::Core 
     Qt6::Gui 
     Qt6::Widgets 
-    cmark::cmark
     CURL::libcurl 
+    cmark::cmark
 )
 
 
diff --git a/src/ctai_cmark-gfm.cpp b/src/ctai_cmark-gfm.cpp
new file mode 100644
index 0000000..06e408a
--- /dev/null
+++ b/src/ctai_cmark-gfm.cpp
@@ -0,0 +1,183 @@
+#include "ctai_cmark-gfm.h"
+
+ctai_cmark_gfm::ctai_cmark_gfm(QTextEdit *editor, QObject *parent) : QObject(parent),
+                                                                     m_editor(editor),
+                                                                     m_bufferMax(8192)
+{
+}
+
+void ctai_cmark_gfm::processInput(const QString &text)
+{
+    // 更新缓冲区
+    m_buffer += text;
+    qDebug()<<"处理的块:"< m_bufferMax * 1.2)
+    {
+        int removeCount = m_buffer.length() - m_bufferMax;
+        m_buffer.remove(0, removeCount);
+        adjustSyntaxPositions(removeCount);
+    }
+}
+// 调整语法标签位置
+void ctai_cmark_gfm::adjustSyntaxPositions(int offset)
+{
+    std::stack adjustedStack;
+    while (!m_syntaxStack.empty())
+    {
+        SyntaxTag tag = m_syntaxStack.top();
+        m_syntaxStack.pop();
+        tag.startPos = qMax(0, tag.startPos - offset);
+        if (tag.startPos >= 0)
+        {
+            adjustedStack.push(tag);
+        }
+    }
+    m_syntaxStack = adjustedStack;
+}
+// 语法状态检测
+SyntaxState ctai_cmark_gfm::detectSyntaxState(const QString &buffer)
+{
+    SyntaxState state;
+    static const QVector> syntaxPatterns = {
+        {QRegularExpression(R"(\*\*(?!\s))"), Bold},
+        {QRegularExpression(R"(\*(?!\s))"), Italic},
+        {QRegularExpression(R"(^#{1,6}\s)"), Heading},
+        {QRegularExpression(R"(^```+[\s\S]*?```)", QRegularExpression::MultilineOption), CodeBlock},
+        {QRegularExpression(R"(^[\*\+-]\s)"), List},
+        {QRegularExpression(R"(^>\s)"), Blockquote},
+        {QRegularExpression(R"($$.*?$$$.*?$)"), Link},
+        {QRegularExpression(R"(!$$.*?$$$.*?$)"), Image}};
+
+    for (const auto &[pattern, type] : syntaxPatterns)
+    {
+        QRegularExpressionMatchIterator it = pattern.globalMatch(buffer);
+        while (it.hasNext())
+        {
+            QRegularExpressionMatch match = it.next();
+            state.activeTags.insert(type, match.captured());
+        }
+    }
+
+    state.needsClosure = !m_syntaxStack.empty() &&
+                         (buffer.length() - m_syntaxStack.top().startPos) > m_bufferMax / 2;
+    return state;
+}
+// 更新语法标签栈
+void ctai_cmark_gfm::updateSyntaxStack(const SyntaxState &newState)
+{
+    // 处理标签闭合
+    auto it = newState.activeTags.begin();
+    while (it != newState.activeTags.end())
+    {
+        if (it.key() == CodeBlock && m_syntaxStack.top().type == CodeBlock)
+        {
+            m_syntaxStack.pop();
+        }
+        // 其他标签闭合逻辑...
+        ++it;
+    }
+
+    // 处理新标签
+    for (auto type : newState.activeTags.keys())
+    {
+        SyntaxTag newTag;
+        newTag.type = type;
+        newTag.startPos = m_buffer.lastIndexOf(newState.activeTags[type]);
+        newTag.opener = newState.activeTags[type];
+        newTag.closer = getCloserForType(type);
+        m_syntaxStack.push(newTag);
+    }
+}
+// 获取闭合标签
+QString ctai_cmark_gfm::getCloserForType(SyntaxType type)
+{
+    static const QMap closers = {
+        {CodeBlock, "\n```"},
+        {Bold, "**"},
+        {Italic, "*"},
+        {Heading, "\n"},
+        {List, "\n"},
+        {Blockquote, "\n"},
+        {Link, ")"},
+        {Image, ")"}};
+    return closers.value(type, "");
+}
+
+// 渲染决策
+bool ctai_cmark_gfm::shouldRender(const SyntaxState &state)
+{
+    return state.needsClosure ||
+           (!m_syntaxStack.empty() && m_buffer.length() - m_syntaxStack.top().startPos > 512) ||
+           m_buffer.length() >= m_bufferMax;
+}
+
+void ctai_cmark_gfm::renderContent()
+{
+    QString renderContent = m_buffer;
+
+    // 自动闭合未完成的标签
+    while (!m_syntaxStack.empty())
+    {
+        const SyntaxTag &tag = m_syntaxStack.top();
+        if (!renderContent.contains(tag.closer))
+        {
+            renderContent += tag.closer;
+        }
+        m_syntaxStack.pop();
+    }
+
+    // 使用cmark解析
+    cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
+    cmark_parser_feed(parser, renderContent.toUtf8().constData(), renderContent.size());
+    cmark_node *doc = cmark_parser_finish(parser);
+
+    // 转换为HTML
+    char *html = cmark_render_html(doc, CMARK_OPT_DEFAULT);
+    updateEditor(QString::fromUtf8(html));
+
+    // 清理资源
+    cmark_node_free(doc);
+    cmark_parser_free(parser);
+    free(html);
+
+    // 重置缓冲区
+    m_buffer.clear();
+}
+// 更新编辑器内容(保持滚动位置)
+void ctai_cmark_gfm::updateEditor(const QString &html)
+{
+    QTextCursor cursor(m_editor->document());
+    int oldHeight = m_editor->document()->size().height();
+    bool atBottom = m_editor->verticalScrollBar()->value() ==
+                    m_editor->verticalScrollBar()->maximum();
+
+    cursor.select(QTextCursor::Document);
+    cursor.insertHtml(html);
+
+    // 滚动位置处理
+    if (atBottom)
+    {
+        m_editor->verticalScrollBar()->setValue(m_editor->verticalScrollBar()->maximum());
+    }
+    else
+    {
+        int newHeight = m_editor->document()->size().height();
+        m_editor->verticalScrollBar()->setValue(m_editor->verticalScrollBar()->value() + (newHeight - oldHeight));
+    }
+}
\ No newline at end of file
diff --git a/src/ctai_cmark-gfm.h b/src/ctai_cmark-gfm.h
new file mode 100644
index 0000000..37f486b
--- /dev/null
+++ b/src/ctai_cmark-gfm.h
@@ -0,0 +1,52 @@
+#ifndef CTAI_CMARK_GFM_H
+#define CTAI_CMARK_GFM_H
+
+#include 
+#include 
+#include 
+#include 
+#include 
+enum SyntaxType {
+    CodeBlock,    // 代码块
+    Bold,         // 粗体
+    Italic,       // 斜体
+    Heading,      // 标题
+    List,         // 列表
+    Blockquote,   // 引用
+    Link,         // 链接
+    Image,        // 图片
+    CustomBlock   // 自定义块
+};
+struct SyntaxTag {
+    SyntaxType type;
+    int startPos;
+    QString opener;
+    QString closer;
+};
+
+struct SyntaxState {
+    QMap activeTags;
+    bool needsClosure = false;
+};
+
+class ctai_cmark_gfm : public QObject {
+    Q_OBJECT
+public:
+    ctai_cmark_gfm(QTextEdit *editor, QObject *parent = nullptr);
+    void processInput(const QString &text);
+private:
+    QTextEdit *m_editor;
+    const int m_bufferMax;
+    QString m_buffer;
+    std::stack m_syntaxStack;  // 语法标签栈  
+    void handleBufferOverflow();  
+    void adjustSyntaxPositions(int offset);  
+    SyntaxState detectSyntaxState(const QString &buffer); 
+    void updateSyntaxStack(const SyntaxState &newState);
+    bool shouldRender(const SyntaxState &state);
+    QString getCloserForType(SyntaxType type);
+    void renderContent();
+    void updateEditor(const QString &html);
+};
+
+#endif
\ No newline at end of file
diff --git a/src/ctai_session_info.cpp b/src/ctai_session_info.cpp
index 227cafc..7071894 100644
--- a/src/ctai_session_info.cpp
+++ b/src/ctai_session_info.cpp
@@ -148,8 +148,8 @@ void ctai_session_info::ctai_session_curl_state_tips(std::string state_str)
 }
 void ctai_session_info::ctai_session_set_steam_mode(bool state)
 {
-    m_stream_mode!=state;
-    qDebug() << "steam mode:" << state;
+    m_stream_mode = state;  // 直接将switch的状态赋值给m_stream_mode
+    qDebug() << "steam mode:" << m_stream_mode;
 }
 void ctai_session_info::ctai_session_user_sending()
 {
diff --git a/src/ctai_switch_button.cpp b/src/ctai_switch_button.cpp
index 6f29a39..090635a 100644
--- a/src/ctai_switch_button.cpp
+++ b/src/ctai_switch_button.cpp
@@ -23,7 +23,7 @@ bool ctai_switch_button::SwBtn_isChecked() const
 void ctai_switch_button::setSwBtn_Status(bool check)
 {
     m_SwBtn_Status=check;
-    m_SwBtn_timer.start(10);
+    m_SwBtn_timer.start(1);
 }
 
 void ctai_switch_button::setSwBtn_BackgroundColor(QColor color)