diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb6317c..68ba13e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -272,6 +272,8 @@ Fixes: Fix reference link definition label matching in a case when the label ends with a Unicode character with non-trivial case folding mapping. + * [#29](https://github.com/mity/md4c/issues/29): + Implement Wiky-style ('|2> text |') table colspan extension. ## Version 0.4.6 diff --git a/CMakeLists.txt b/CMakeLists.txt index aec8293d..bf1303d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,4 +68,23 @@ include(GNUInstallDirs) add_subdirectory(src) if (BUILD_MD2HTML_EXECUTABLE) add_subdirectory(md2html) -endif () + set(MINIMUM_PYTHON_VERSION "3.6") + set(TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") + find_package(Python3 COMPONENTS Interpreter) + if(Python3_FOUND AND NOT Python3_VERSION VERSION_LESS ${MINIMUM_PYTHON_VERSION}) + enable_testing() + file(GLOB TEST_FILES "${CMAKE_CURRENT_SOURCE_DIR}/test/*.txt") + # get_target_property(PROGRAM md2html LOCATION) + set(PROGRAM "${CMAKE_CURRENT_BINARY_DIR}/md2html/md2html") + foreach(TEST_FILE ${TEST_FILES}) + get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) + add_test(NAME ${TEST_NAME} + COMMAND ${Python3_EXECUTABLE} "${TEST_DIR}/run-testsuite.py" -s ${TEST_FILE} -p ${PROGRAM}) + endforeach() + add_test(NAME Pathological_inputs + COMMAND ${Python3_EXECUTABLE} "${TEST_DIR}/pathological-tests.py" -p ${PROGRAM}) + else() + message(WARNING "Python not found or version is less than ${MINIMUM_PYTHON_VERSION}.") + endif() +endif() + diff --git a/README.md b/README.md index 2c8ed267..7427decd 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,9 @@ extensions: * With the flag `MD_FLAG_TABLES`, GitHub-style tables are supported. +* With the flag `MD_FLAG_TABLES_COLSPAN`, wiky-style ('|2> text |') table + colspan are supported. + * With the flag `MD_FLAG_TASKLISTS`, GitHub-style task lists are supported. * With the flag `MD_FLAG_STRIKETHROUGH`, strike-through spans are enabled diff --git a/md2html/md2html.c b/md2html/md2html.c index de4ab18e..b525d132 100644 --- a/md2html/md2html.c +++ b/md2html/md2html.c @@ -255,6 +255,7 @@ static const CMDLINE_OPTION cmdline_options[] = { { 0, "fpermissive-www-autolinks", '.', 0 }, { 0, "fstrikethrough", 'S', 0 }, { 0, "ftables", 'T', 0 }, + { 0, "ftable-colspan", 'J', 0 }, { 0, "ftasklists", 'X', 0 }, { 0, "funderline", '_', 0 }, { 0, "fverbatim-entities", 'E', 0 }, @@ -310,6 +311,7 @@ usage(void) " Force all soft breaks to act as hard breaks\n" " --fstrikethrough Enable strike-through spans\n" " --ftables Enable tables\n" + " --ftable-colspan Enable table colspan extension ('|2> text |')\n" " --ftasklists Enable task lists\n" " --funderline Enable underline spans\n" " --fwiki-links Enable wiki links\n" @@ -382,6 +384,7 @@ cmdline_callback(int opt, char const* value, void* data) case '@': parser_flags |= MD_FLAG_PERMISSIVEEMAILAUTOLINKS; break; case 'V': parser_flags |= MD_FLAG_PERMISSIVEAUTOLINKS; break; case 'T': parser_flags |= MD_FLAG_TABLES; break; + case 'J': parser_flags |= MD_FLAG_TABLES_COLSPAN; break; case 'S': parser_flags |= MD_FLAG_STRIKETHROUGH; break; case 'L': parser_flags |= MD_FLAG_LATEXMATHSPANS; break; case 'K': parser_flags |= MD_FLAG_WIKILINKS; break; diff --git a/src/md4c-html.c b/src/md4c-html.c index 5229de54..b142cde1 100644 --- a/src/md4c-html.c +++ b/src/md4c-html.c @@ -312,15 +312,24 @@ render_open_code_block(MD_HTML* r, const MD_BLOCK_CODE_DETAIL* det) static void render_open_td_block(MD_HTML* r, const MD_CHAR* cell_type, const MD_BLOCK_TD_DETAIL* det) { + MD_CHAR tail[2]; + RENDER_VERBATIM(r, "<"); RENDER_VERBATIM(r, cell_type); switch(det->align) { - case MD_ALIGN_LEFT: RENDER_VERBATIM(r, " align=\"left\">"); break; - case MD_ALIGN_CENTER: RENDER_VERBATIM(r, " align=\"center\">"); break; - case MD_ALIGN_RIGHT: RENDER_VERBATIM(r, " align=\"right\">"); break; - default: RENDER_VERBATIM(r, ">"); break; + case MD_ALIGN_LEFT: RENDER_VERBATIM(r, " align=\"left\""); break; + case MD_ALIGN_CENTER: RENDER_VERBATIM(r, " align=\"center\""); break; + case MD_ALIGN_RIGHT: RENDER_VERBATIM(r, " align=\"right\""); break; + case MD_ALIGN_DEFAULT: break; + } + if (det->colspan > 1) { + RENDER_VERBATIM(r, " colspan=\""); + tail[0] = '0' + det->colspan; + tail[1] = '\"'; + render_verbatim(r, tail, 2); } + RENDER_VERBATIM(r, ">"); } static void diff --git a/src/md4c.c b/src/md4c.c index e3f5cf9d..9074aa3e 100644 --- a/src/md4c.c +++ b/src/md4c.c @@ -4529,10 +4529,9 @@ md_analyze_table_alignment(MD_CTX* ctx, OFF beg, OFF end, MD_ALIGN* align, int n static int md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines); static int -md_process_table_cell(MD_CTX* ctx, MD_BLOCKTYPE cell_type, MD_ALIGN align, OFF beg, OFF end) +md_process_table_cell(MD_CTX* ctx, MD_BLOCKTYPE cell_type, MD_BLOCK_TD_DETAIL *det, OFF beg, OFF end) { MD_LINE line; - MD_BLOCK_TD_DETAIL det; int ret = 0; while(beg < end && ISWHITESPACE(beg)) @@ -4540,13 +4539,12 @@ md_process_table_cell(MD_CTX* ctx, MD_BLOCKTYPE cell_type, MD_ALIGN align, OFF b while(end > beg && ISWHITESPACE(end-1)) end--; - det.align = align; line.beg = beg; line.end = end; - MD_ENTER_BLOCK(cell_type, &det); + MD_ENTER_BLOCK(cell_type, det); MD_CHECK(md_process_normal_block_contents(ctx, &line, 1)); - MD_LEAVE_BLOCK(cell_type, &det); + MD_LEAVE_BLOCK(cell_type, det); abort: return ret; @@ -4557,6 +4555,10 @@ md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, const MD_ALIGN* align, int col_count) { MD_LINE line; + MD_LINE cell; + MD_BLOCK_TD_DETAIL det; + + unsigned is_spans; OFF* pipe_offs = NULL; int i, j, k, n; int ret = 0; @@ -4583,19 +4585,36 @@ md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, MD_MARK* mark = &ctx->marks[i]; pipe_offs[j++] = mark->end; } - pipe_offs[j++] = end+1; + pipe_offs[j] = end+1; + is_spans = ctx->parser.flags & MD_FLAG_TABLES_COLSPAN; /* Process cells. */ MD_ENTER_BLOCK(MD_BLOCK_TR, NULL); k = 0; - for(i = 0; i < j-1 && k < col_count; i++) { - if(pipe_offs[i] < pipe_offs[i+1]-1) - MD_CHECK(md_process_table_cell(ctx, cell_type, align[k++], pipe_offs[i], pipe_offs[i+1]-1)); + for(i = 0; i < j && k < col_count; i++) { + cell.beg = pipe_offs[i]; + cell.end = pipe_offs[i+1]-1; + + if(cell.beg < cell.end) { + det.colspan = 0; + if (is_spans && cell.end - cell.beg >= 2 && + ISDIGIT(cell.beg) && CH(cell.beg+1) == '>') { + det.colspan = CH(cell.beg) - '0'; + cell.beg += 2; + } + det.align = align[k++]; + MD_CHECK(md_process_table_cell(ctx, cell_type, &det, cell.beg, cell.end)); + if (is_spans && det.colspan > 1) + k += det.colspan - 1; + } } /* Make sure we call enough table cells even if the current table contains * too few of them. */ - while(k < col_count) - MD_CHECK(md_process_table_cell(ctx, cell_type, align[k++], 0, 0)); + det.colspan = 0; + while(k < col_count) { + det.align = align[k++]; + MD_CHECK(md_process_table_cell(ctx, cell_type, &det, 0, 0)); + } MD_LEAVE_BLOCK(MD_BLOCK_TR, NULL); abort: diff --git a/src/md4c.h b/src/md4c.h index 8d6be1cb..8ca7e782 100644 --- a/src/md4c.h +++ b/src/md4c.h @@ -278,6 +278,7 @@ typedef struct MD_BLOCK_TABLE_DETAIL { /* Detailed info for MD_BLOCK_TH and MD_BLOCK_TD. */ typedef struct MD_BLOCK_TD_DETAIL { MD_ALIGN align; + int colspan; } MD_BLOCK_TD_DETAIL; /* Detailed info for MD_SPAN_A. */ @@ -318,6 +319,7 @@ typedef struct MD_SPAN_WIKILINK { #define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */ #define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */ #define MD_FLAG_HARD_SOFT_BREAKS 0x8000 /* Force all soft breaks to act as hard breaks. */ +#define MD_FLAG_TABLES_COLSPAN 0x10000 /* Enable table colspan extension '|2> 2-colspan cell |'. */ #define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS) #define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS) diff --git a/test/spec-tables-colspan.txt b/test/spec-tables-colspan.txt new file mode 100644 index 00000000..47a03e91 --- /dev/null +++ b/test/spec-tables-colspan.txt @@ -0,0 +1,74 @@ + +# Table colspan extension + +With the flag `MD_FLAG_TABLES_COLSPAN`, MD4C enables extension for recognition +of table colspan's. + +Basic table example of a table with colspan: + +```````````````````````````````` example +| Column 1 | Column 2 | +|----------|----------| +| foo | bar | +|2> baz | +| quux | quuz | +. + + + + + + + + + +
Column 1Column 2
foobar
baz
quuxquuz
+. +--ftables --ftable-colspan +```````````````````````````````` + +Only one digit are allowed: + +```````````````````````````````` example +| Column 1 | Column 2 | +|----------|----------| +| foo | bar | +|22> baz | +| quux | quuz | +. + + + + + + + + + +
Column 1Column 2
foobar
22> baz
quuxquuz
+. +--ftables --ftable-colspan +```````````````````````````````` + +The arrow character must follow without spaces: + +```````````````````````````````` example +| Column 1 | Column 2 | +|----------|----------| +| foo | bar | +| 2 > baz | +| quux | quuz | +. + + + + + + + + + +
Column 1Column 2
foobar
2 > baz
quuxquuz
+. +--ftables --ftable-colspan +````````````````````````````````