diff --git a/.github/workflows/stack.yml b/.github/workflows/stack.yml new file mode 100644 index 0000000..e29e121 --- /dev/null +++ b/.github/workflows/stack.yml @@ -0,0 +1,12 @@ +on: [push, pull_request] + +name: build + +jobs: + haskell: + name: Build and test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: stack build + - run: stack test diff --git a/.gitignore b/.gitignore index 01e585a..cd13951 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ cabal-dev .cabal-sandbox/ cabal.sandbox.config cabal.config +stack.yaml.lock diff --git a/cbits/cmark-gfm-core-extensions.h b/cbits/cmark-gfm-core-extensions.h index 8ab049d..0645915 100644 --- a/cbits/cmark-gfm-core-extensions.h +++ b/cbits/cmark-gfm-core-extensions.h @@ -7,6 +7,7 @@ extern "C" { #include "cmark-gfm-extension_api.h" #include "cmark-gfm-extensions_export.h" +#include "config.h" // for bool #include CMARK_GFM_EXTENSIONS_EXPORT @@ -15,14 +16,36 @@ void cmark_gfm_core_extensions_ensure_registered(void); CMARK_GFM_EXTENSIONS_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_EXTENSIONS_EXPORT +int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns); + CMARK_GFM_EXTENSIONS_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_EXTENSIONS_EXPORT +int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments); + CMARK_GFM_EXTENSIONS_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_EXTENSIONS_EXPORT +int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header); + +CMARK_GFM_EXTENSIONS_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_EXTENSIONS_EXPORT -char *cmark_gfm_extensions_get_tasklist_state(cmark_node *node); +int cmark_gfm_extensions_set_tasklist_item_checked(cmark_node *node, bool is_checked); #ifdef __cplusplus } diff --git a/cbits/ext_scanners.c b/cbits/ext_scanners.c index c3de227..0d3ba28 100644 --- a/cbits/ext_scanners.c +++ b/cbits/ext_scanners.c @@ -1,4 +1,5 @@ -/* Generated by re2c 1.1.1 */ +/* Generated by re2c 1.3 */ + #include "ext_scanners.h" #include @@ -39,265 +40,180 @@ bufsize_t _scan_table_start(const unsigned char *p) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - yych = *(marker = p); - if (yych <= '{') { - if (yych <= 0x1F) { - if (yych <= '\t') { - if (yych <= 0x08) - goto yy3; + yych = *p; + if (yych <= ' ') { + if (yych <= '\n') { + if (yych == '\t') goto yy4; - } else { - if (yych <= '\n') - goto yy2; - if (yych <= '\f') - goto yy4; - goto yy3; - } } else { - if (yych <= '-') { - if (yych <= ' ') - goto yy4; - if (yych <= ',') - goto yy3; - goto yy5; - } else { - if (yych == ':') - goto yy6; - goto yy3; - } + if (yych <= '\f') + goto yy4; + if (yych >= ' ') + goto yy4; } } else { - if (yych <= 0xEC) { - if (yych <= 0xC1) { - if (yych <= '|') - goto yy4; - if (yych <= 0x7F) - goto yy3; - } else { - if (yych <= 0xDF) - goto yy7; - if (yych <= 0xE0) - goto yy9; - goto yy10; - } + if (yych <= '9') { + if (yych == '-') + goto yy5; } else { - if (yych <= 0xF0) { - if (yych <= 0xED) - goto yy11; - if (yych <= 0xEF) - goto yy10; - goto yy12; - } else { - if (yych <= 0xF3) - goto yy13; - if (yych <= 0xF4) - goto yy14; - } + if (yych <= ':') + goto yy6; + if (yych == '|') + goto yy4; } } - yy2 : { return 0; } - yy3: ++p; - goto yy2; + yy3 : { return 0; } yy4: yych = *(marker = ++p); if (yybm[0 + yych] & 64) { - goto yy15; + goto yy7; } if (yych == '-') - goto yy17; + goto yy10; if (yych == ':') - goto yy19; - goto yy2; + goto yy12; + goto yy3; yy5: yych = *(marker = ++p); if (yybm[0 + yych] & 128) { - goto yy17; + goto yy10; } if (yych <= ' ') { if (yych <= 0x08) - goto yy2; + goto yy3; if (yych <= '\r') - goto yy21; + goto yy14; if (yych <= 0x1F) - goto yy2; - goto yy21; + goto yy3; + goto yy14; } else { if (yych <= ':') { if (yych <= '9') - goto yy2; - goto yy20; + goto yy3; + goto yy13; } else { if (yych == '|') - goto yy21; - goto yy2; + goto yy14; + goto yy3; } } yy6: yych = *(marker = ++p); if (yybm[0 + yych] & 128) { - goto yy17; + goto yy10; } - goto yy2; + goto yy3; yy7: - yych = *++p; - if (yych <= 0x7F) - goto yy8; - if (yych <= 0xBF) - goto yy3; - yy8: - p = marker; - goto yy2; - yy9: - yych = *++p; - if (yych <= 0x9F) - goto yy8; - if (yych <= 0xBF) - goto yy7; - goto yy8; - yy10: - yych = *++p; - if (yych <= 0x7F) - goto yy8; - if (yych <= 0xBF) - goto yy7; - goto yy8; - yy11: - yych = *++p; - if (yych <= 0x7F) - goto yy8; - if (yych <= 0x9F) - goto yy7; - goto yy8; - yy12: - yych = *++p; - if (yych <= 0x8F) - goto yy8; - if (yych <= 0xBF) - goto yy10; - goto yy8; - yy13: - yych = *++p; - if (yych <= 0x7F) - goto yy8; - if (yych <= 0xBF) - goto yy10; - goto yy8; - yy14: - yych = *++p; - if (yych <= 0x7F) - goto yy8; - if (yych <= 0x8F) - goto yy10; - goto yy8; - yy15: yych = *++p; if (yybm[0 + yych] & 64) { - goto yy15; + goto yy7; } if (yych == '-') - goto yy17; + goto yy10; if (yych == ':') - goto yy19; - goto yy8; - yy17: + goto yy12; + yy9: + p = marker; + goto yy3; + yy10: yych = *++p; if (yybm[0 + yych] & 128) { - goto yy17; + goto yy10; } if (yych <= 0x1F) { if (yych <= '\n') { if (yych <= 0x08) - goto yy8; + goto yy9; if (yych <= '\t') - goto yy20; - goto yy22; + goto yy13; + goto yy15; } else { if (yych <= '\f') - goto yy20; + goto yy13; if (yych <= '\r') - goto yy24; - goto yy8; + goto yy17; + goto yy9; } } else { if (yych <= ':') { if (yych <= ' ') - goto yy20; + goto yy13; if (yych <= '9') - goto yy8; - goto yy20; + goto yy9; + goto yy13; } else { if (yych == '|') - goto yy25; - goto yy8; + goto yy18; + goto yy9; } } - yy19: + yy12: yych = *++p; if (yybm[0 + yych] & 128) { - goto yy17; + goto yy10; } - goto yy8; - yy20: + goto yy9; + yy13: yych = *++p; - yy21: + yy14: if (yych <= '\r') { if (yych <= '\t') { if (yych <= 0x08) - goto yy8; - goto yy20; + goto yy9; + goto yy13; } else { if (yych <= '\n') - goto yy22; + goto yy15; if (yych <= '\f') - goto yy20; - goto yy24; + goto yy13; + goto yy17; } } else { if (yych <= ' ') { if (yych <= 0x1F) - goto yy8; - goto yy20; + goto yy9; + goto yy13; } else { if (yych == '|') - goto yy25; - goto yy8; + goto yy18; + goto yy9; } } - yy22: + yy15: ++p; { return (bufsize_t)(p - start); } - yy24: + yy17: yych = *++p; if (yych == '\n') - goto yy22; - goto yy8; - yy25: + goto yy15; + goto yy9; + yy18: yych = *++p; if (yybm[0 + yych] & 128) { - goto yy17; + goto yy10; } if (yych <= '\r') { if (yych <= '\t') { if (yych <= 0x08) - goto yy8; - goto yy25; + goto yy9; + goto yy18; } else { if (yych <= '\n') - goto yy22; + goto yy15; if (yych <= '\f') - goto yy25; - goto yy24; + goto yy18; + goto yy17; } } else { if (yych <= ' ') { if (yych <= 0x1F) - goto yy8; - goto yy25; + goto yy9; + goto yy18; } else { if (yych == ':') - goto yy19; - goto yy8; + goto yy12; + goto yy9; } } } @@ -309,6 +225,7 @@ bufsize_t _scan_table_cell(const unsigned char *p) { { unsigned char yych; + unsigned int yyaccept = 0; static const unsigned char yybm[] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 64, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, @@ -326,53 +243,51 @@ bufsize_t _scan_table_cell(const unsigned char *p) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - yych = *(marker = p); + yych = *p; if (yybm[0 + yych] & 64) { - goto yy30; + goto yy22; } - if (yych <= 0xE0) { - if (yych <= '\\') { - if (yych <= '\n') - goto yy29; + if (yych <= 0xEC) { + if (yych <= 0xC1) { if (yych <= '\r') - goto yy32; - goto yy34; + goto yy25; + if (yych <= '\\') + goto yy27; + goto yy25; } else { - if (yych <= '|') - goto yy32; - if (yych <= 0xC1) - goto yy29; if (yych <= 0xDF) - goto yy36; - goto yy38; + goto yy29; + if (yych <= 0xE0) + goto yy30; + goto yy31; } } else { - if (yych <= 0xEF) { - if (yych == 0xED) - goto yy40; - goto yy39; + if (yych <= 0xF0) { + if (yych <= 0xED) + goto yy32; + if (yych <= 0xEF) + goto yy31; + goto yy33; } else { - if (yych <= 0xF0) - goto yy41; if (yych <= 0xF3) - goto yy42; + goto yy34; if (yych <= 0xF4) - goto yy43; + goto yy35; + goto yy25; } } - yy29 : { return (bufsize_t)(p - start); } - yy30: + yy22: + yyaccept = 0; yych = *(marker = ++p); if (yybm[0 + yych] & 64) { - goto yy30; + goto yy22; } if (yych <= 0xEC) { if (yych <= 0xC1) { if (yych <= '\r') - goto yy29; + goto yy24; if (yych <= '\\') - goto yy34; - goto yy29; + goto yy27; } else { if (yych <= 0xDF) goto yy36; @@ -392,29 +307,31 @@ bufsize_t _scan_table_cell(const unsigned char *p) { goto yy42; if (yych <= 0xF4) goto yy43; - goto yy29; } } - yy32: + yy24 : { return (bufsize_t)(p - start); } + yy25: ++p; - { return 0; } - yy34: + yy26 : { return 0; } + yy27: + yyaccept = 0; yych = *(marker = ++p); if (yybm[0 + yych] & 128) { - goto yy34; + goto yy27; } if (yych <= 0xDF) { if (yych <= '\f') { if (yych == '\n') - goto yy29; - goto yy30; + goto yy24; + goto yy22; } else { if (yych <= '\r') - goto yy29; + goto yy24; if (yych <= 0x7F) - goto yy30; + goto yy22; if (yych <= 0xC1) - goto yy29; + goto yy24; + goto yy36; } } else { if (yych <= 0xEF) { @@ -430,18 +347,77 @@ bufsize_t _scan_table_cell(const unsigned char *p) { goto yy42; if (yych <= 0xF4) goto yy43; - goto yy29; + goto yy24; } } + yy29: + yych = *++p; + if (yych <= 0x7F) + goto yy26; + if (yych <= 0xBF) + goto yy22; + goto yy26; + yy30: + yyaccept = 1; + yych = *(marker = ++p); + if (yych <= 0x9F) + goto yy26; + if (yych <= 0xBF) + goto yy36; + goto yy26; + yy31: + yyaccept = 1; + yych = *(marker = ++p); + if (yych <= 0x7F) + goto yy26; + if (yych <= 0xBF) + goto yy36; + goto yy26; + yy32: + yyaccept = 1; + yych = *(marker = ++p); + if (yych <= 0x7F) + goto yy26; + if (yych <= 0x9F) + goto yy36; + goto yy26; + yy33: + yyaccept = 1; + yych = *(marker = ++p); + if (yych <= 0x8F) + goto yy26; + if (yych <= 0xBF) + goto yy39; + goto yy26; + yy34: + yyaccept = 1; + yych = *(marker = ++p); + if (yych <= 0x7F) + goto yy26; + if (yych <= 0xBF) + goto yy39; + goto yy26; + yy35: + yyaccept = 1; + yych = *(marker = ++p); + if (yych <= 0x7F) + goto yy26; + if (yych <= 0x8F) + goto yy39; + goto yy26; yy36: yych = *++p; if (yych <= 0x7F) goto yy37; if (yych <= 0xBF) - goto yy30; + goto yy22; yy37: p = marker; - goto yy29; + if (yyaccept == 0) { + goto yy24; + } else { + goto yy26; + } yy38: yych = *++p; if (yych <= 0x9F) @@ -488,12 +464,10 @@ bufsize_t _scan_table_cell(const unsigned char *p) { } bufsize_t _scan_table_cell_end(const unsigned char *p) { - const unsigned char *marker = NULL; const unsigned char *start = p; { unsigned char yych; - unsigned int yyaccept = 0; static const unsigned char yybm[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, @@ -509,115 +483,17 @@ bufsize_t _scan_table_cell_end(const unsigned char *p) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - yych = *(marker = p); - if (yych <= 0xDF) { - if (yych <= '{') { - if (yych != '\n') - goto yy47; - } else { - if (yych <= '|') - goto yy48; - if (yych <= 0x7F) - goto yy47; - if (yych >= 0xC2) - goto yy51; - } - } else { - if (yych <= 0xEF) { - if (yych <= 0xE0) - goto yy53; - if (yych == 0xED) - goto yy55; - goto yy54; - } else { - if (yych <= 0xF0) - goto yy56; - if (yych <= 0xF3) - goto yy57; - if (yych <= 0xF4) - goto yy58; - } - } - yy46 : { return 0; } - yy47: + yych = *p; + if (yych == '|') + goto yy48; ++p; - goto yy46; + { return 0; } yy48: - yyaccept = 1; - yych = *(marker = ++p); + yych = *++p; if (yybm[0 + yych] & 128) { goto yy48; } - if (yych <= 0x08) - goto yy50; - if (yych <= '\n') - goto yy59; - if (yych <= '\r') - goto yy60; - yy50 : { return (bufsize_t)(p - start); } - yy51: - yych = *++p; - if (yych <= 0x7F) - goto yy52; - if (yych <= 0xBF) - goto yy47; - yy52: - p = marker; - if (yyaccept == 0) { - goto yy46; - } else { - goto yy50; - } - yy53: - yych = *++p; - if (yych <= 0x9F) - goto yy52; - if (yych <= 0xBF) - goto yy51; - goto yy52; - yy54: - yych = *++p; - if (yych <= 0x7F) - goto yy52; - if (yych <= 0xBF) - goto yy51; - goto yy52; - yy55: - yych = *++p; - if (yych <= 0x7F) - goto yy52; - if (yych <= 0x9F) - goto yy51; - goto yy52; - yy56: - yych = *++p; - if (yych <= 0x8F) - goto yy52; - if (yych <= 0xBF) - goto yy54; - goto yy52; - yy57: - yych = *++p; - if (yych <= 0x7F) - goto yy52; - if (yych <= 0xBF) - goto yy54; - goto yy52; - yy58: - yych = *++p; - if (yych <= 0x7F) - goto yy52; - if (yych <= 0x8F) - goto yy54; - goto yy52; - yy59: - ++p; - goto yy50; - yy60: - yych = *++p; - if (yych == '\n') - goto yy59; - goto yy52; + { return (bufsize_t)(p - start); } } } @@ -642,138 +518,62 @@ bufsize_t _scan_table_row_end(const unsigned char *p) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - yych = *(marker = p); - if (yych <= 0xC1) { - if (yych <= '\f') { - if (yych <= 0x08) - goto yy64; - if (yych == '\n') - goto yy66; - goto yy65; - } else { - if (yych <= 0x1F) { - if (yych <= '\r') - goto yy68; - goto yy64; - } else { - if (yych <= ' ') - goto yy65; - if (yych <= 0x7F) - goto yy64; - } - } + yych = *p; + if (yych <= '\f') { + if (yych <= 0x08) + goto yy53; + if (yych == '\n') + goto yy56; + goto yy55; } else { - if (yych <= 0xED) { - if (yych <= 0xDF) - goto yy69; - if (yych <= 0xE0) - goto yy71; - if (yych <= 0xEC) - goto yy72; - goto yy73; - } else { - if (yych <= 0xF0) { - if (yych <= 0xEF) - goto yy72; - goto yy74; - } else { - if (yych <= 0xF3) - goto yy75; - if (yych <= 0xF4) - goto yy76; - } - } + if (yych <= '\r') + goto yy58; + if (yych == ' ') + goto yy55; } - yy63 : { return 0; } - yy64: + yy53: ++p; - goto yy63; - yy65: + yy54 : { return 0; } + yy55: yych = *(marker = ++p); if (yych <= 0x08) - goto yy63; + goto yy54; if (yych <= '\r') - goto yy78; + goto yy60; if (yych == ' ') - goto yy78; - goto yy63; - yy66: + goto yy60; + goto yy54; + yy56: ++p; { return (bufsize_t)(p - start); } - yy68: + yy58: yych = *++p; if (yych == '\n') - goto yy66; - goto yy63; - yy69: - yych = *++p; - if (yych <= 0x7F) - goto yy70; - if (yych <= 0xBF) - goto yy64; - yy70: - p = marker; - goto yy63; - yy71: - yych = *++p; - if (yych <= 0x9F) - goto yy70; - if (yych <= 0xBF) - goto yy69; - goto yy70; - yy72: - yych = *++p; - if (yych <= 0x7F) - goto yy70; - if (yych <= 0xBF) - goto yy69; - goto yy70; - yy73: - yych = *++p; - if (yych <= 0x7F) - goto yy70; - if (yych <= 0x9F) - goto yy69; - goto yy70; - yy74: - yych = *++p; - if (yych <= 0x8F) - goto yy70; - if (yych <= 0xBF) - goto yy72; - goto yy70; - yy75: - yych = *++p; - if (yych <= 0x7F) - goto yy70; - if (yych <= 0xBF) - goto yy72; - goto yy70; - yy76: - yych = *++p; - if (yych <= 0x7F) - goto yy70; - if (yych <= 0x8F) - goto yy72; - goto yy70; - yy77: + goto yy56; + goto yy54; + yy59: yych = *++p; - yy78: + yy60: if (yybm[0 + yych] & 128) { - goto yy77; + goto yy59; } if (yych <= 0x08) - goto yy70; + goto yy61; if (yych <= '\n') - goto yy66; - if (yych >= 0x0E) - goto yy70; + goto yy56; + if (yych <= '\r') + goto yy62; + yy61: + p = marker; + goto yy54; + yy62: yych = *++p; if (yych == '\n') - goto yy66; - goto yy70; + goto yy56; + goto yy61; } } + bufsize_t _scan_tasklist(const unsigned char *p) { const unsigned char *marker = NULL; const unsigned char *start = p; @@ -798,361 +598,281 @@ bufsize_t _scan_tasklist(const unsigned char *p) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - yych = *(marker = p); - if (yych <= '/') { - if (yych <= 0x1F) { - if (yych <= '\t') { - if (yych <= 0x08) - goto yy83; - goto yy84; - } else { - if (yych <= '\n') - goto yy82; - if (yych <= '\f') - goto yy84; - goto yy83; - } + yych = *p; + if (yych <= ' ') { + if (yych <= '\n') { + if (yych == '\t') + goto yy67; } else { - if (yych <= '+') { - if (yych <= ' ') - goto yy84; - if (yych <= ')') - goto yy83; - goto yy85; - } else { - if (yych == '-') - goto yy85; - goto yy83; - } + if (yych <= '\f') + goto yy67; + if (yych >= ' ') + goto yy67; } } else { - if (yych <= 0xEC) { - if (yych <= 0xC1) { - if (yych <= '9') - goto yy86; - if (yych <= 0x7F) - goto yy83; - } else { - if (yych <= 0xDF) - goto yy87; - if (yych <= 0xE0) - goto yy89; - goto yy90; - } + if (yych <= ',') { + if (yych <= ')') + goto yy65; + if (yych <= '+') + goto yy68; } else { - if (yych <= 0xF0) { - if (yych <= 0xED) - goto yy91; - if (yych <= 0xEF) - goto yy90; - goto yy92; - } else { - if (yych <= 0xF3) - goto yy93; - if (yych <= 0xF4) - goto yy94; - } + if (yych <= '-') + goto yy68; + if (yych <= '/') + goto yy65; + if (yych <= '9') + goto yy69; } } - yy82 : { return 0; } - yy83: + yy65: ++p; - goto yy82; - yy84: + yy66 : { return 0; } + yy67: yych = *(marker = ++p); if (yybm[0 + yych] & 64) { - goto yy95; + goto yy70; } if (yych <= ',') { if (yych <= ')') - goto yy82; + goto yy66; if (yych <= '+') - goto yy97; - goto yy82; + goto yy73; + goto yy66; } else { if (yych <= '-') - goto yy97; + goto yy73; if (yych <= '/') - goto yy82; + goto yy66; if (yych <= '9') - goto yy98; - goto yy82; + goto yy74; + goto yy66; } - yy85: + yy68: yych = *(marker = ++p); if (yych <= '\n') { if (yych == '\t') - goto yy99; - goto yy82; + goto yy75; + goto yy66; } else { if (yych <= '\f') - goto yy99; + goto yy75; if (yych == ' ') - goto yy99; - goto yy82; + goto yy75; + goto yy66; } - yy86: + yy69: yych = *(marker = ++p); if (yych <= 0x1F) { if (yych <= '\t') { if (yych <= 0x08) - goto yy102; - goto yy97; + goto yy78; + goto yy73; } else { if (yych <= '\n') - goto yy82; + goto yy66; if (yych <= '\f') - goto yy97; - goto yy102; + goto yy73; + goto yy78; } } else { if (yych <= 0x7F) { if (yych <= ' ') - goto yy97; - goto yy102; + goto yy73; + goto yy78; } else { if (yych <= 0xC1) - goto yy82; + goto yy66; if (yych <= 0xF4) - goto yy102; - goto yy82; + goto yy78; + goto yy66; } } - yy87: - yych = *++p; - if (yych <= 0x7F) - goto yy88; - if (yych <= 0xBF) - goto yy83; - yy88: - p = marker; - goto yy82; - yy89: - yych = *++p; - if (yych <= 0x9F) - goto yy88; - if (yych <= 0xBF) - goto yy87; - goto yy88; - yy90: - yych = *++p; - if (yych <= 0x7F) - goto yy88; - if (yych <= 0xBF) - goto yy87; - goto yy88; - yy91: - yych = *++p; - if (yych <= 0x7F) - goto yy88; - if (yych <= 0x9F) - goto yy87; - goto yy88; - yy92: - yych = *++p; - if (yych <= 0x8F) - goto yy88; - if (yych <= 0xBF) - goto yy90; - goto yy88; - yy93: - yych = *++p; - if (yych <= 0x7F) - goto yy88; - if (yych <= 0xBF) - goto yy90; - goto yy88; - yy94: - yych = *++p; - if (yych <= 0x7F) - goto yy88; - if (yych <= 0x8F) - goto yy90; - goto yy88; - yy95: + yy70: yych = *++p; if (yybm[0 + yych] & 64) { - goto yy95; + goto yy70; } if (yych <= ',') { if (yych <= ')') - goto yy88; - if (yych >= ',') - goto yy88; + goto yy72; + if (yych <= '+') + goto yy73; } else { if (yych <= '-') - goto yy97; + goto yy73; if (yych <= '/') - goto yy88; + goto yy72; if (yych <= '9') - goto yy98; - goto yy88; + goto yy74; } - yy97: + yy72: + p = marker; + goto yy66; + yy73: yych = *++p; if (yych == '[') - goto yy88; - goto yy100; - yy98: + goto yy72; + goto yy76; + yy74: yych = *++p; if (yych <= '\n') { if (yych == '\t') - goto yy97; - goto yy102; + goto yy73; + goto yy78; } else { if (yych <= '\f') - goto yy97; + goto yy73; if (yych == ' ') - goto yy97; - goto yy102; + goto yy73; + goto yy78; } - yy99: + yy75: yych = *++p; - yy100: + yy76: if (yych <= '\f') { if (yych == '\t') - goto yy99; + goto yy75; if (yych <= '\n') - goto yy88; - goto yy99; + goto yy72; + goto yy75; } else { if (yych <= ' ') { if (yych <= 0x1F) - goto yy88; - goto yy99; + goto yy72; + goto yy75; } else { if (yych == '[') - goto yy110; - goto yy88; + goto yy86; + goto yy72; } } - yy101: + yy77: yych = *++p; - yy102: + yy78: if (yybm[0 + yych] & 128) { - goto yy101; + goto yy77; } if (yych <= 0xC1) { if (yych <= '\f') { if (yych <= 0x08) - goto yy97; + goto yy73; if (yych == '\n') - goto yy88; - goto yy99; + goto yy72; + goto yy75; } else { if (yych == ' ') - goto yy99; + goto yy75; if (yych <= 0x7F) - goto yy97; - goto yy88; + goto yy73; + goto yy72; } } else { if (yych <= 0xED) { if (yych <= 0xDF) - goto yy103; + goto yy79; if (yych <= 0xE0) - goto yy104; + goto yy80; if (yych <= 0xEC) - goto yy105; - goto yy106; + goto yy81; + goto yy82; } else { if (yych <= 0xF0) { if (yych <= 0xEF) - goto yy105; - goto yy107; + goto yy81; + goto yy83; } else { if (yych <= 0xF3) - goto yy108; + goto yy84; if (yych <= 0xF4) - goto yy109; - goto yy88; + goto yy85; + goto yy72; } } } - yy103: + yy79: yych = *++p; if (yych <= 0x7F) - goto yy88; + goto yy72; if (yych <= 0xBF) - goto yy97; - goto yy88; - yy104: + goto yy73; + goto yy72; + yy80: yych = *++p; if (yych <= 0x9F) - goto yy88; + goto yy72; if (yych <= 0xBF) - goto yy103; - goto yy88; - yy105: + goto yy79; + goto yy72; + yy81: yych = *++p; if (yych <= 0x7F) - goto yy88; + goto yy72; if (yych <= 0xBF) - goto yy103; - goto yy88; - yy106: + goto yy79; + goto yy72; + yy82: yych = *++p; if (yych <= 0x7F) - goto yy88; + goto yy72; if (yych <= 0x9F) - goto yy103; - goto yy88; - yy107: + goto yy79; + goto yy72; + yy83: yych = *++p; if (yych <= 0x8F) - goto yy88; + goto yy72; if (yych <= 0xBF) - goto yy105; - goto yy88; - yy108: + goto yy81; + goto yy72; + yy84: yych = *++p; if (yych <= 0x7F) - goto yy88; + goto yy72; if (yych <= 0xBF) - goto yy105; - goto yy88; - yy109: + goto yy81; + goto yy72; + yy85: yych = *++p; if (yych <= 0x7F) - goto yy88; + goto yy72; if (yych <= 0x8F) - goto yy105; - goto yy88; - yy110: + goto yy81; + goto yy72; + yy86: yych = *++p; if (yych <= 'W') { if (yych != ' ') - goto yy88; + goto yy72; } else { if (yych <= 'X') - goto yy111; + goto yy87; if (yych != 'x') - goto yy88; + goto yy72; } - yy111: + yy87: yych = *++p; if (yych != ']') - goto yy88; + goto yy72; yych = *++p; if (yych <= '\n') { if (yych != '\t') - goto yy88; + goto yy72; } else { if (yych <= '\f') - goto yy113; + goto yy89; if (yych != ' ') - goto yy88; + goto yy72; } - yy113: + yy89: yych = *++p; if (yych <= '\n') { if (yych == '\t') - goto yy113; + goto yy89; } else { if (yych <= '\f') - goto yy113; + goto yy89; if (yych == ' ') - goto yy113; + goto yy89; } { return (bufsize_t)(p - start); } } diff --git a/cbits/node.h b/cbits/node.h index 6b05df1..6391db9 100644 --- a/cbits/node.h +++ b/cbits/node.h @@ -21,6 +21,7 @@ typedef struct { cmark_delim_type delimiter; unsigned char bullet_char; bool tight; + bool checked; // For task list extension } cmark_list; typedef struct { diff --git a/cbits/table.c b/cbits/table.c index 9829c3d..a5bb440 100644 --- a/cbits/table.c +++ b/cbits/table.c @@ -16,6 +16,7 @@ cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW, typedef struct { uint16_t n_columns; + int paragraph_offset; cmark_llist *cells; } table_row; @@ -113,22 +114,39 @@ static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsi static table_row *row_from_string(cmark_syntax_extension *self, cmark_parser *parser, unsigned char *string, int len) { + // Parses a single table row. It has the following form: + // `delim? table_cell (delim table_cell)* delim? newline` + // Note that cells are allowed to be empty. + // + // From the GitHub-flavored Markdown specification: + // + // > Each row consists of cells containing arbitrary text, in which inlines + // > are parsed, separated by pipes (|). A leading and trailing pipe is also + // > recommended for clarity of reading, and if there’s otherwise parsing + // > ambiguity. + table_row *row = NULL; bufsize_t cell_matched = 1, pipe_matched = 1, offset; + int expect_more_cells = 1; + int row_end_offset = 0; row = (table_row *)parser->mem->calloc(1, sizeof(table_row)); row->n_columns = 0; row->cells = NULL; + // Scan past the (optional) leading pipe. offset = scan_table_cell_end(string, len, 0); // Parse the cells of the row. Stop if we reach the end of the input, or if we // cannot detect any more cells. - while (offset < len && (cell_matched || pipe_matched)) { + while (offset < len && expect_more_cells) { cell_matched = scan_table_cell(string, len, offset); pipe_matched = scan_table_cell_end(string, len, offset + cell_matched); if (cell_matched || pipe_matched) { + // We are guaranteed to have a cell, since (1) either we found some + // content and cell_matched, or (2) we found an empty cell followed by a + // pipe. cmark_strbuf *cell_buf = unescape_pipes(parser->mem, string + offset, cell_matched); cmark_strbuf_trim(cell_buf); @@ -137,23 +155,46 @@ static table_row *row_from_string(cmark_syntax_extension *self, cell->buf = cell_buf; cell->start_offset = offset; cell->end_offset = offset + cell_matched - 1; + while (cell->start_offset > 0 && string[cell->start_offset - 1] != '|') { --cell->start_offset; ++cell->internal_offset; } + row->n_columns += 1; row->cells = cmark_llist_append(parser->mem, row->cells, cell); } offset += cell_matched + pipe_matched; - if (!pipe_matched) { - pipe_matched = scan_table_row_end(string, len, offset); - offset += pipe_matched; + if (pipe_matched) { + expect_more_cells = 1; + } else { + // We've scanned the last cell. Check if we have reached the end of the row + row_end_offset = scan_table_row_end(string, len, offset); + offset += row_end_offset; + + // If the end of the row is not the end of the input, + // the row is not a real row but potentially part of the paragraph + // preceding the table. + if (row_end_offset && offset != len) { + row->paragraph_offset = offset; + + cmark_llist_free_full(parser->mem, row->cells, (cmark_free_func)free_table_cell); + row->cells = NULL; + row->n_columns = 0; + + // Scan past the (optional) leading pipe. + offset += scan_table_cell_end(string, len, offset); + + expect_more_cells = 1; + } else { + expect_more_cells = 0; + } } } - if (offset != len || !row->n_columns) { + if (offset != len || row->n_columns == 0) { free_table_row(parser->mem, row); row = NULL; } @@ -161,12 +202,30 @@ static table_row *row_from_string(cmark_syntax_extension *self, return row; } +static void try_inserting_table_header_paragraph(cmark_parser *parser, + cmark_node *parent_container, + unsigned char *parent_string, + int paragraph_offset) { + cmark_node *paragraph; + cmark_strbuf *paragraph_content; + + paragraph = cmark_node_new_with_mem(CMARK_NODE_PARAGRAPH, parser->mem); + + paragraph_content = unescape_pipes(parser->mem, parent_string, paragraph_offset); + cmark_strbuf_trim(paragraph_content); + cmark_node_set_string_content(paragraph, (char *) paragraph_content->ptr); + cmark_strbuf_free(paragraph_content); + parser->mem->free(paragraph_content); + + if (!cmark_node_insert_before(parent_container, paragraph)) { + parser->mem->free(paragraph); + } +} + static cmark_node *try_opening_table_header(cmark_syntax_extension *self, cmark_parser *parser, cmark_node *parent_container, unsigned char *input, int len) { - bufsize_t matched = - scan_table_start(input, len, cmark_parser_get_first_nonspace(parser)); cmark_node *table_header; table_row *header_row = NULL; table_row *marker_row = NULL; @@ -174,41 +233,37 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self, const char *parent_string; uint16_t i; - if (!matched) - return parent_container; - - parent_string = cmark_node_get_string_content(parent_container); - - cmark_arena_push(); - - header_row = row_from_string(self, parser, (unsigned char *)parent_string, - (int)strlen(parent_string)); - - if (!header_row) { - free_table_row(parser->mem, header_row); - cmark_arena_pop(); + if (!scan_table_start(input, len, cmark_parser_get_first_nonspace(parser))) { return parent_container; } + // Since scan_table_start was successful, we must have a marker row. marker_row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser), len - cmark_parser_get_first_nonspace(parser)); - assert(marker_row); - if (header_row->n_columns != marker_row->n_columns) { - free_table_row(parser->mem, header_row); + cmark_arena_push(); + + // Check for a matching header row. We call `row_from_string` with the entire + // (potentially long) parent container as input, but this should be safe since + // `row_from_string` bails out early if it does not find a row. + parent_string = cmark_node_get_string_content(parent_container); + header_row = row_from_string(self, parser, (unsigned char *)parent_string, + (int)strlen(parent_string)); + if (!header_row || header_row->n_columns != marker_row->n_columns) { free_table_row(parser->mem, marker_row); + free_table_row(parser->mem, header_row); cmark_arena_pop(); return parent_container; } if (cmark_arena_pop()) { + marker_row = row_from_string( + self, parser, input + cmark_parser_get_first_nonspace(parser), + len - cmark_parser_get_first_nonspace(parser)); header_row = row_from_string(self, parser, (unsigned char *)parent_string, (int)strlen(parent_string)); - marker_row = row_from_string(self, parser, - input + cmark_parser_get_first_nonspace(parser), - len - cmark_parser_get_first_nonspace(parser)); } if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) { @@ -217,10 +272,13 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self, return parent_container; } - cmark_node_set_syntax_extension(parent_container, self); + if (header_row->paragraph_offset) { + try_inserting_table_header_paragraph(parser, parent_container, (unsigned char *)parent_string, + header_row->paragraph_offset); + } + cmark_node_set_syntax_extension(parent_container, self); parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table)); - set_n_table_columns(parent_container, header_row->n_columns); uint8_t *alignments = diff --git a/cbits/tasklist.c b/cbits/tasklist.c index 3c273fc..7bef454 100644 --- a/cbits/tasklist.c +++ b/cbits/tasklist.c @@ -9,19 +9,33 @@ typedef enum { CMARK_TASKLIST_CHECKED, } cmark_tasklist_type; +// Local constants +static const char *TYPE_STRING = "tasklist"; + static const char *get_type_string(cmark_syntax_extension *extension, cmark_node *node) { - return "tasklist"; + return TYPE_STRING; } -char *cmark_gfm_extensions_get_tasklist_state(cmark_node *node) { - if (!node || ((int)node->as.opaque != CMARK_TASKLIST_CHECKED && (int)node->as.opaque != CMARK_TASKLIST_NOCHECKED)) + +// Return 1 if state was set, 0 otherwise +int cmark_gfm_extensions_set_tasklist_item_checked(cmark_node *node, bool is_checked) { + // The node has to exist, and be an extension, and actually be the right type in order to get the value. + if (!node || !node->extension || strcmp(cmark_node_get_type_string(node), TYPE_STRING)) return 0; - if ((int)node->as.opaque != CMARK_TASKLIST_CHECKED) { - return "checked"; + node->as.list.checked = is_checked; + return 1; +} + +bool cmark_gfm_extensions_get_tasklist_item_checked(cmark_node *node) { + if (!node || !node->extension || strcmp(cmark_node_get_type_string(node), TYPE_STRING)) + return false; + + if (node->as.list.checked) { + return true; } else { - return "unchecked"; + return false; } } @@ -74,11 +88,8 @@ static cmark_node *open_tasklist_item(cmark_syntax_extension *self, cmark_node_set_syntax_extension(parent_container, self); cmark_parser_advance_offset(parser, (char *)input, 3, false); - if (strstr((char*)input, "[x]")) { - parent_container->as.opaque = (void *)CMARK_TASKLIST_CHECKED; - } else { - parent_container->as.opaque = (void *)CMARK_TASKLIST_NOCHECKED; - } + // Either an upper or lower case X means the task is completed. + parent_container->as.list.checked = (strstr((char*)input, "[x]") || strstr((char*)input, "[X]")); return NULL; } @@ -89,7 +100,7 @@ static void commonmark_render(cmark_syntax_extension *extension, bool entering = (ev_type == CMARK_EVENT_ENTER); if (entering) { renderer->cr(renderer); - if ((int)node->as.opaque == CMARK_TASKLIST_CHECKED) { + if (node->as.list.checked) { renderer->out(renderer, node, "- [x] ", false, LITERAL); } else { renderer->out(renderer, node, "- [ ] ", false, LITERAL); @@ -110,7 +121,7 @@ static void html_render(cmark_syntax_extension *extension, cmark_strbuf_puts(renderer->html, "html, options); cmark_strbuf_putc(renderer->html, '>'); - if ((int)node->as.opaque == CMARK_TASKLIST_CHECKED) { + if (node->as.list.checked) { cmark_strbuf_puts(renderer->html, " "); } else { cmark_strbuf_puts(renderer->html, " "); @@ -120,6 +131,15 @@ static void html_render(cmark_syntax_extension *extension, } } +static const char *xml_attr(cmark_syntax_extension *extension, + cmark_node *node) { + if (node->as.list.checked) { + return " completed=\"true\""; + } else { + return " completed=\"false\""; + } +} + cmark_syntax_extension *create_tasklist_extension(void) { cmark_syntax_extension *ext = cmark_syntax_extension_new("tasklist"); @@ -130,6 +150,7 @@ cmark_syntax_extension *create_tasklist_extension(void) { cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render); cmark_syntax_extension_set_plaintext_render_func(ext, commonmark_render); cmark_syntax_extension_set_html_render_func(ext, html_render); + cmark_syntax_extension_set_xml_attr_func(ext, xml_attr); return ext; }