diff --git a/md2html/md2html.c b/md2html/md2html.c index c959bfc1..a69bb354 100644 --- a/md2html/md2html.c +++ b/md2html/md2html.c @@ -220,6 +220,8 @@ static const CMDLINE_OPTION cmdline_options[] = { { 0, "funderline", '_', 0 }, { 0, "fverbatim-entities", 'E', 0 }, { 0, "fwiki-links", 'K', 0 }, + { 0, "fmention-links", 'M', 0 }, + { 0, "fno-html-blocks", 'F', 0 }, { 0, "fno-html-spans", 'G', 0 }, @@ -269,6 +271,7 @@ usage(void) " --ftasklists Enable task lists\n" " --funderline Enable underline spans\n" " --fwiki-links Enable wiki links\n" + " --fmention-links Enable mention links\n" "\n" "Markdown suppression options:\n" " --fno-html-blocks\n" @@ -335,6 +338,7 @@ cmdline_callback(int opt, char const* value, void* data) case 'K': parser_flags |= MD_FLAG_WIKILINKS; break; case 'X': parser_flags |= MD_FLAG_TASKLISTS; break; case '_': parser_flags |= MD_FLAG_UNDERLINE; break; + case 'M': parser_flags |= MD_FLAG_MENTIONS; break; default: fprintf(stderr, "Illegal option: %s\n", value); diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index c00b36a9..f93ac350 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -70,6 +70,10 @@ echo echo "Underline extension:" $PYTHON "$TEST_DIR/spec_tests.py" -s "$TEST_DIR/underline.txt" -p "$PROGRAM --funderline" +echo +echo "Mention links extension:" +$PYTHON "$TEST_DIR/spec_tests.py" -s "$TEST_DIR/mention-links.txt" -p "$PROGRAM --fmention-links" + echo echo "Pathological input:" $PYTHON "$TEST_DIR/pathological_tests.py" -p "$PROGRAM" diff --git a/src/md4c-html.c b/src/md4c-html.c index d604aecb..833cc2a6 100644 --- a/src/md4c-html.c +++ b/src/md4c-html.c @@ -265,6 +265,13 @@ render_attribute(MD_HTML* r, const MD_ATTRIBUTE* attr, } } +static void +render_mention_link(MD_HTML* r, const MD_SPAN_MENTION_DETAIL* det) +{ + RENDER_VERBATIM(r, "text, det->size, render_html_escaped); + RENDER_VERBATIM(r, "\">"); +} static void render_open_ol_block(MD_HTML* r, const MD_BLOCK_OL_DETAIL* det) @@ -466,6 +473,7 @@ enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) case MD_SPAN_LATEXMATH: RENDER_VERBATIM(r, ""); break; case MD_SPAN_LATEXMATH_DISPLAY: RENDER_VERBATIM(r, ""); break; case MD_SPAN_WIKILINK: render_open_wikilink_span(r, (MD_SPAN_WIKILINK_DETAIL*) detail); break; + case MD_SPAN_MENTION: render_mention_link(r, (MD_SPAN_MENTION_DETAIL*) detail); break; } return 0; @@ -495,6 +503,7 @@ leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) case MD_SPAN_LATEXMATH: /*fall through*/ case MD_SPAN_LATEXMATH_DISPLAY: RENDER_VERBATIM(r, ""); break; case MD_SPAN_WIKILINK: RENDER_VERBATIM(r, ""); break; + case MD_SPAN_MENTION: RENDER_VERBATIM(r, ""); break; } return 0; diff --git a/src/md4c.c b/src/md4c.c index 40066b2d..a52a4ccc 100644 --- a/src/md4c.c +++ b/src/md4c.c @@ -2709,7 +2709,7 @@ md_build_mark_char_map(MD_CTX* ctx) if(ctx->parser.flags & MD_FLAG_LATEXMATHSPANS) ctx->mark_char_map['$'] = 1; - if(ctx->parser.flags & MD_FLAG_PERMISSIVEEMAILAUTOLINKS) + if ((ctx->parser.flags & MD_FLAG_MENTIONS) || (ctx->parser.flags & MD_FLAG_PERMISSIVEEMAILAUTOLINKS)) ctx->mark_char_map['@'] = 1; if(ctx->parser.flags & MD_FLAG_PERMISSIVEURLAUTOLINKS) @@ -3190,9 +3190,26 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) continue; } + /* A potential mention link. */ /* A potential permissive e-mail autolink. */ if(ch == _T('@')) { - if(line->beg + 1 <= off && ISALNUM(off-1) && + if( (ctx->parser.flags & MD_FLAG_MENTIONS) && (line->beg == off || (CH(off-1) == _T(' '))) ) + { + OFF index = off + 1; + if (index == line->end || CH(index) == ' ') { + off++; + continue; + } + while (index <= line->end) + { + if (!(ISALNUM(index) || (CH(index) == '_'))) + break; + index++; + } + PUSH_MARK('@', off, index, MD_MARK_RESOLVED); + off = index; + } + else if(line->beg + 1 <= off && ISALNUM(off-1) && off + 3 < line->end && ISALNUM(off+1)) { PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); @@ -4291,6 +4308,7 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) MD_FALLTHROUGH(); case '@': /* Permissive e-mail autolink. */ + /* Mention link */ case ':': /* Permissive URL autolink. */ case '.': /* Permissive WWW autolink. */ { @@ -4299,6 +4317,17 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) const CHAR* dest = STR(opener->end); SZ dest_size = closer->beg - opener->end; + MD_SPAN_MENTION_DETAIL det; + if (CH(mark->beg) == '@') + { + det.text = (char *) ctx->text + mark->beg + 1; + det.size = mark->end - mark->beg - 1; + MD_ENTER_SPAN(MD_SPAN_MENTION, &det); + MD_TEXT(text_type, STR(mark->beg), mark->end - mark->beg); + MD_LEAVE_SPAN(MD_SPAN_MENTION, &det); + break; + } + /* For permissive auto-links we do not know closer mark * position at the time of md_collect_marks(), therefore * it can be out-of-order in ctx->marks[]. diff --git a/src/md4c.h b/src/md4c.h index 95f78f9b..521e14bd 100644 --- a/src/md4c.h +++ b/src/md4c.h @@ -145,7 +145,9 @@ typedef enum MD_SPANTYPE { /* ... * Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */ - MD_SPAN_U + MD_SPAN_U, + + MD_SPAN_MENTION } MD_SPANTYPE; /* Text is the actual textual contents of span. */ @@ -297,6 +299,12 @@ typedef struct MD_SPAN_WIKILINK { MD_ATTRIBUTE target; } MD_SPAN_WIKILINK_DETAIL; +/* Detailed info for MD_SPAN_MENTION. */ +typedef struct MD_SPAN_MENTION { + unsigned char size; + MD_CHAR* text; +} MD_SPAN_MENTION_DETAIL; + /* Flags specifying extensions/deviations from CommonMark specification. * * By default (when MD_PARSER::flags == 0), we follow CommonMark specification. @@ -316,6 +324,7 @@ typedef struct MD_SPAN_WIKILINK { #define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */ #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_MENTIONS 0x8000 /* Enable mention links extension. */ #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/mention-links.txt b/test/mention-links.txt new file mode 100644 index 00000000..27e33d9a --- /dev/null +++ b/test/mention-links.txt @@ -0,0 +1,29 @@ +# Mention links + +With the flag `MD_FLAG_MENTIONS`, MD4C enables extension for recognition of mention (@) style links. + +A link ends when a non-alphanumeric character is hit. The only such character allowed is an underscore. + +```````````````````````````````` example +someone said @me said such and such. +. +

someone said @me said such and such.

+```````````````````````````````` + +```````````````````````````````` example +@me said such and such. +. +

@me said such and such.

+```````````````````````````````` + +```````````````````````````````` example +An empty at character is not recognized as a mention: + +@ + +@ +. +

An empty at character is not recognized as a mention:

+

@

+

@

+```````````````````````````````` \ No newline at end of file