From 8bd90e4ef92ed6d3ac00b73ad10d2e0a0b0ded1f Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 13 Aug 2014 23:01:12 -0700 Subject: [PATCH 1/6] Don't match inline spans that contain a blank line Inline styles, such as emphasis, code, and links, cannot contain an empty line. Change the definitions so they won't ever try to match across an empty line, and in fact won't even start the match unless there's a valid end. --- syntax/markdown.vim | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/syntax/markdown.vim b/syntax/markdown.vim index 8c981bb..52d8f60 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -70,21 +70,28 @@ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained -syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\_[^]]*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart +syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\ze\%([^]]\|\n\s*\S\@=\)*] \=[[(]" end="]" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline -let s:concealends = has('conceal') ? ' concealends' : '' -exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" keepend contains=markdownLineStart' . s:concealends -exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=_\|_\S\@=" end="\S\@<=_\|_\S\@=" keepend contains=markdownLineStart' . s:concealends -exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=\*\*\|\*\*\S\@=" end="\S\@<=\*\*\|\*\*\S\@=" keepend contains=markdownLineStart,markdownItalic' . s:concealends -exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=__\|__\S\@=" end="\S\@<=__\|__\S\@=" keepend contains=markdownLineStart,markdownItalic' . s:concealends -exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=\*\*\*\|\*\*\*\S\@=" end="\S\@<=\*\*\*\|\*\*\*\S\@=" keepend contains=markdownLineStart' . s:concealends -exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=___\|___\S\@=" end="\S\@<=___\|___\S\@=" keepend contains=markdownLineStart' . s:concealends - -syn region markdownCode matchgroup=markdownCodeDelimiter start="`" end="`" keepend contains=markdownLineStart -syn region markdownCode matchgroup=markdownCodeDelimiter start="`` \=" end=" \=``" keepend contains=markdownLineStart +let s:concealends = has('conceal') ? 'concealends' : '' +function! s:InlineRegionPatterns(start, end) + " generate a new start pattern that matches the given start, followed by + " the end after some text, without any blank lines in between. + let l:start = '\%(' . a:start . '\)\ze\%(.\|\n\s*\S\@=\)\{-}\%(' . a:end . '\)' + " assume that the '"' character can be used as a delimiter + return 'start="' . l:start . '" end="' . a:end . '"' +endfunction +exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter' s:InlineRegionPatterns('\%(\S\@<=\*\|\*\S\@=\)\ze\%(.\|\n\s*\S\@=\)\{-}', '\S\@<=\*\|\*\S\@=') 'keepend contains=markdownLineStart' s:concealends +exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter' s:InlineRegionPatterns('\S\@<=_\|_\S\@=', '\S\@<=_\|_\S\@=') 'keepend contains=markdownLineStart' s:concealends +exe 'syn region markdownBold matchgroup=markdownBoldDelimiter' s:InlineRegionPatterns('\S\@<=\*\*\|\*\*\S\@=', '\S\@<=\*\*\|\*\*\S\@=') 'keepend contains=markdownLineStart,markdownItalic' s:concealends +exe 'syn region markdownBold matchgroup=markdownBoldDelimiter' s:InlineRegionPatterns('\S\@<=__\|__\S\@=', '\S\@<=__\|__\S\@=') 'keepend contains=markdownLineStart,markdownItalic' s:concealends +exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter' s:InlineRegionPatterns('\S\@<=\*\*\*\|\*\*\*\S\@=', '\S\@<=\*\*\*\|\*\*\*\S\@=') 'keepend contains=markdownLineStart' s:concealends +exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter' s:InlineRegionPatterns('\S\@<=___\|___\S\@=', '\S\@<=___\|___\S\@=') 'keepend contains=markdownLineStart' s:concealends + +exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`', '`') 'keepend contains=markdownLineStart' +exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`` \=', ' \=``') 'keepend contains=markdownLineStart' syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*```.*$" end="^\s*```\ze\s*$" keepend syn match markdownFootnote "\[^[^\]]\+\]" From 85bf3e27f16ec8f0fa576eeb8a5cde7b3c3608a1 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 13 Aug 2014 23:09:57 -0700 Subject: [PATCH 2/6] Consider lines with just blockquote start to be blank When detecting whether an inline span applies, consider a line that only contains a blockquote start indicator to be blank. --- syntax/markdown.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syntax/markdown.vim b/syntax/markdown.vim index 52d8f60..66cb946 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -70,7 +70,7 @@ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained -syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\ze\%([^]]\|\n\s*\S\@=\)*] \=[[(]" end="]" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart +syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\ze\%([^]]\|\n\%(>\s\=\)*\s*[^> \t]\@=\)*] \=[[(]" end="]" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline @@ -79,7 +79,7 @@ let s:concealends = has('conceal') ? 'concealends' : '' function! s:InlineRegionPatterns(start, end) " generate a new start pattern that matches the given start, followed by " the end after some text, without any blank lines in between. - let l:start = '\%(' . a:start . '\)\ze\%(.\|\n\s*\S\@=\)\{-}\%(' . a:end . '\)' + let l:start = '\%(' . a:start . '\)\ze\%(.\|\n\%(>\s\=\)*\s*[^> \t]\@=\)\{-}\%(' . a:end . '\)' " assume that the '"' character can be used as a delimiter return 'start="' . l:start . '" end="' . a:end . '"' endfunction From 05583fb94e9e06ec7f37792bdc53c1a7cd11cb23 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 13 Aug 2014 23:11:46 -0700 Subject: [PATCH 3/6] Allow newline after [link text] block A newline (followed by any number of spaces) is always allowed between a [link text] and a [link destination] block, even in the original Markdown. Various implementations allow it between [link text] and (inline url) blocks as well. --- syntax/markdown.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/markdown.vim b/syntax/markdown.vim index 66cb946..a9fb8b8 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -70,7 +70,7 @@ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+ syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained -syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\ze\%([^]]\|\n\%(>\s\=\)*\s*[^> \t]\@=\)*] \=[[(]" end="]" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart +syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\ze\%([^]]\|\n\%(>\s\=\)*\s*[^> \t]\@=\)*]\%(\n\s*\| \=\)[[(]" end="]" nextgroup=markdownLink,markdownId skipwhite skipnl contains=@markdownInline,markdownLineStart syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline From 53aaeae385cf78ebb87f19d03498db84653954f2 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 13 Aug 2014 23:43:54 -0700 Subject: [PATCH 4/6] Treat fenced code blocks like other block-level elements Add fenced code blocks to @markdownBlock, so they can start inside a blockquote. --- syntax/markdown.vim | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/syntax/markdown.vim b/syntax/markdown.vim index a9fb8b8..45656d8 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -90,18 +90,23 @@ exe 'syn region markdownBold matchgroup=markdownBoldDelimiter' s:InlineRegionPat exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter' s:InlineRegionPatterns('\S\@<=\*\*\*\|\*\*\*\S\@=', '\S\@<=\*\*\*\|\*\*\*\S\@=') 'keepend contains=markdownLineStart' s:concealends exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter' s:InlineRegionPatterns('\S\@<=___\|___\S\@=', '\S\@<=___\|___\S\@=') 'keepend contains=markdownLineStart' s:concealends -exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`', '`') 'keepend contains=markdownLineStart' -exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`` \=', ' \=``') 'keepend contains=markdownLineStart' -syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*```.*$" end="^\s*```\ze\s*$" keepend +exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`\%(``\)\@!', '`') 'keepend contains=markdownLineStart' +exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`\@1.*$" end="^\s*```\ze\s*$" keepend contains=@markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g') + let s:type = matchstr(s:type,'[^=]*$') + let s:regionName = 'markdownHighlight'.substitute(s:type,'\..*','','') + exe 'syn region' s:regionName ' matchgroup=markdownCodeDelimiter start="\s*```\s*'.s:type.'\>.*$" end="^\s*```\ze\s*$" keepend contained contains=@markdownHighlight'.substitute(s:type,'\.','','g') + exe 'syn cluster markdownBlock add='.s:regionName endfor unlet! s:type + unlet! s:regionName endif syn match markdownEscape "\\[][\\`*_{}()#+.!-]" @@ -140,6 +145,7 @@ hi def link markdownBoldDelimiter markdownBold hi def link markdownBoldItalic htmlBoldItalic hi def link markdownBoldItalicDelimiter markdownBoldItalic hi def link markdownCodeDelimiter Delimiter +hi def link markdownCodeFence markdownCodeDelimiter hi def link markdownEscape Special hi def link markdownError Error From 34260fc2ba10cebf140af700ff11b038257045ba Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 13 Aug 2014 23:45:25 -0700 Subject: [PATCH 5/6] Don't require a space after a blockquote start (>) Even the original Markdown doesn't require the space here. --- syntax/markdown.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/markdown.vim b/syntax/markdown.vim index 45656d8..bb82e81 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -50,7 +50,7 @@ syn region markdownH4 matchgroup=markdownHeadingDelimiter start="#####\@!" end syn region markdownH5 matchgroup=markdownHeadingDelimiter start="######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained syn region markdownH6 matchgroup=markdownHeadingDelimiter start="#######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained -syn match markdownBlockquote ">\%(\s\|$\)" contained nextgroup=@markdownBlock +syn match markdownBlockquote ">\%(\s\=\|$\)" contained nextgroup=@markdownBlock syn region markdownCodeBlock start=" \|\t" end="$" contained From 8a712f46753918e012a57b9badac8c0b628681d9 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 26 Aug 2014 19:11:20 -0700 Subject: [PATCH 6/6] Better fenced code block matching Fenced code blocks can use either ``` or ~~~ as a delimiter, and can include more than 3 backticks/tildes in a row if they want (~~~~). It also appears that the start and end markers don't have to match. This was verified in both the redcloth Markdown implementation and GitHub-Flavored Markdown. --- syntax/markdown.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/syntax/markdown.vim b/syntax/markdown.vim index bb82e81..dba05c3 100644 --- a/syntax/markdown.vim +++ b/syntax/markdown.vim @@ -92,7 +92,9 @@ exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter' s:Inl exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`\%(``\)\@!', '`') 'keepend contains=markdownLineStart' exe 'syn region markdownCode matchgroup=markdownCodeDelimiter' s:InlineRegionPatterns('`\@1