@@ -20,32 +20,41 @@ const MAX_BASE_CANDIDATES = 20;
2020/**
2121 * Splits markdown into fenced code blocks and the text between them. A fence opens with three or more
2222 * backticks or tildes, and closes with the same character, at least as many, on a line of its own.
23- * A fence that never closes runs to the end, as in CommonMark.
23+ * Fences inside block quotes count, and end with their quote. A fence that never closes runs to the
24+ * end, as in CommonMark.
2425 * @returns {{ code: boolean, text: string }[] }
2526 */
2627function splitFences ( text ) {
2728 const parts = [ ] ;
2829 let buffer = '' ;
2930 let fence = null ;
3031 for ( const line of text . split ( / (?< = \n ) / ) ) {
31- if ( ! fence ) {
32- const open = line . match ( / ^ { 0 , 3 } ( ` { 3 , } | ~ { 3 , } ) / ) ;
33- // A backtick fence's info string can't contain a backtick.
34- if ( open && ! ( open [ 1 ] [ 0 ] === '`' && line . slice ( open [ 0 ] . length ) . includes ( '`' ) ) ) {
35- parts . push ( { code : false , text : buffer } ) ;
36- buffer = line ;
37- fence = open [ 1 ] ;
38- } else {
39- buffer += line ;
40- }
41- } else {
32+ const quote = line . match ( / ^ (?: { 0 , 3 } > ? ) * / ) [ 0 ] ;
33+ const depth = ( quote . match ( / > / g) || [ ] ) . length ;
34+ const rest = line . slice ( quote . length ) ;
35+ if ( fence && depth < fence . depth ) {
36+ parts . push ( { code : true , text : buffer } ) ;
37+ buffer = '' ;
38+ fence = null ;
39+ }
40+ if ( fence ) {
4241 buffer += line ;
43- const close = line . match ( / ^ { 0 , 3 } ( ` { 3 , } | ~ { 3 , } ) [ \t ] * \r ? \n ? $ / ) ;
44- if ( close && close [ 1 ] [ 0 ] === fence [ 0 ] && close [ 1 ] . length >= fence . length ) {
42+ const close = rest . match ( / ^ { 0 , 3 } ( ` { 3 , } | ~ { 3 , } ) [ \t ] * \r ? \n ? $ / ) ;
43+ if ( close && close [ 1 ] [ 0 ] === fence . marker [ 0 ] && close [ 1 ] . length >= fence . marker . length ) {
4544 parts . push ( { code : true , text : buffer } ) ;
4645 buffer = '' ;
4746 fence = null ;
4847 }
48+ continue ;
49+ }
50+ const open = rest . match ( / ^ { 0 , 3 } ( ` { 3 , } | ~ { 3 , } ) / ) ;
51+ // A backtick fence's info string can't contain a backtick.
52+ if ( open && ! ( open [ 1 ] [ 0 ] === '`' && rest . slice ( open [ 0 ] . length ) . includes ( '`' ) ) ) {
53+ parts . push ( { code : false , text : buffer } ) ;
54+ buffer = line ;
55+ fence = { marker : open [ 1 ] , depth } ;
56+ } else {
57+ buffer += line ;
4958 }
5059 }
5160 parts . push ( { code : fence !== null , text : buffer } ) ;
@@ -55,30 +64,29 @@ function splitFences(text) {
5564/**
5665 * Writes every GitHub reference out as a full link. On GitHub, "#12" is a PR or issue of the same repo;
5766 * Linear matches a bare "#12" against every repo it knows, so it can link a PR from another repo.
58- * Code (fenced or inline), existing links and URLs are left alone. GitHub redirects /pull/N to
59- * /issues/N when N is an issue.
67+ * Code (fenced, or inline with any number of backticks ), existing links and URLs are left alone.
68+ * GitHub redirects /pull/N to /issues/N when N is an issue.
6069 */
6170function linkReferences ( text , owner , repo ) {
62- const untouched = / ( ` [ ^ ` \n ] * ` | \[ [ ^ \] \n ] * \] \( [ ^ ) \n ] * \) | < [ ^ > \n ] * > | h t t p s ? : \/ \/ [ ^ \s ) ] + ) / ;
63- const link = ( prose ) =>
64- prose
65- . split ( untouched )
66- . map ( ( part , i ) =>
67- i % 2
68- ? part
69- : part
70- . replace (
71- / ( ^ | [ ^ \w / # & ] ) ( [ \w . - ] + ) \/ ( [ \w . - ] + ) # ( \d + ) \b / g,
72- ( _ , pre , o , r , n ) => `${ pre } [${ o } /${ r } #${ n } ](https://github.com/${ o } /${ r } /pull/${ n } )` ,
73- )
74- . replace (
75- / ( ^ | [ ^ \w / # & [ ] | \b P R ? ) # ( \d + ) \b / g,
76- ( _ , pre , n ) => `${ pre } [#${ n } ](https://github.com/${ owner } /${ repo } /pull/${ n } )` ,
77- ) ,
71+ const untouched = / (?< ! ` ) ( ` + ) (? ! ` ) [ \s \S ] * ?(?< ! ` ) \1(? ! ` ) | \[ [ ^ \] \n ] * \] \( [ ^ ) \n ] * \) | < [ ^ > \n ] * > | h t t p s ? : \/ \/ [ ^ \s ) ] + / g;
72+ const linkPlain = ( plain ) =>
73+ plain
74+ . replace (
75+ / ( ^ | [ ^ \w / # & ] ) ( [ \w . - ] + ) \/ ( [ \w . - ] + ) # ( \d + ) \b / g,
76+ ( _ , pre , o , r , n ) => `${ pre } [${ o } /${ r } #${ n } ](https://github.com/${ o } /${ r } /pull/${ n } )` ,
7877 )
79- . join ( '' ) ;
78+ . replace ( / ( ^ | [ ^ \w / # & [ ] | \b P R ? ) # ( \d + ) \b / g, ( _ , pre , n ) => `${ pre } [#${ n } ](https://github.com/${ owner } /${ repo } /pull/${ n } )` ) ;
79+ const linkProse = ( prose ) => {
80+ let out = '' ;
81+ let last = 0 ;
82+ for ( const match of prose . matchAll ( untouched ) ) {
83+ out += linkPlain ( prose . slice ( last , match . index ) ) + match [ 0 ] ;
84+ last = match . index + match [ 0 ] . length ;
85+ }
86+ return out + linkPlain ( prose . slice ( last ) ) ;
87+ } ;
8088 return splitFences ( text )
81- . map ( ( part ) => ( part . code ? part . text : link ( part . text ) ) )
89+ . map ( ( part ) => ( part . code ? part . text : linkProse ( part . text ) ) )
8290 . join ( '' ) ;
8391}
8492
0 commit comments