@@ -228,6 +228,9 @@ export class MarkdownSerializerState {
228228 for ( let i = 0 ; i < lines . length ; i ++ ) {
229229 const startOfLine = this . atBlank ( ) || this . closed ;
230230 this . write ( ) ;
231+ // Escape ! before [ to prevent being parsed as image syntax
232+ if ( escape === false && lines [ i ] [ 0 ] === '[' && / ( ^ | [ ^ \\ ] ) ! $ / . test ( this . out ) )
233+ this . out = this . out . slice ( 0 , this . out . length - 1 ) + '\\!' ;
231234 let text = lines [ i ] ;
232235 if ( escape !== false && this . options . escape !== false ) text = this . esc ( text , startOfLine as any )
233236 if ( this . escapeWhitespace ) text = this . escWhitespace ( text ) ;
@@ -380,7 +383,7 @@ export class MarkdownSerializerState {
380383 // have special meaning only at the start of the line.
381384 esc ( str : string , startOfLine = false ) {
382385 // eslint-disable-next-line no-useless-escape
383- const defaultEsc = / [ ` \^ + * \\ \| ~ \[ \] \{ \} < > \$ _ ] / g;
386+ const defaultEsc = / [ ` \^ + * \\ \| ~ \[ \] \{ \} < > \$ ] / g;
384387 const extraChars = this . escapeCharacters ?. length ? this . escapeCharacters . map ( c => '\\' + c ) . join ( '' ) : '' ;
385388 const escRegexp = this . options ?. commonEscape ||
386389 // Compose the escape regexp from default, options, and extra characters
@@ -389,6 +392,12 @@ export class MarkdownSerializerState {
389392 const startOfLineEscRegexp = this . options ?. startOfLineEscape || / ^ [: # \- * + > ] / ;
390393
391394 str = str . replace ( escRegexp , '\\$&' ) ;
395+ // Smart underscore: don't escape _ between word characters (e.g. foo_bar)
396+ str = str . replace ( / _ / g, ( m , i ) =>
397+ i > 0 && i + 1 < str . length && / \w / . test ( str [ i - 1 ] ) && / \w / . test ( str [ i + 1 ] )
398+ ? m
399+ : '\\' + m
400+ ) ;
392401 if ( startOfLine ) str = str . replace ( startOfLineEscRegexp , '\\$&' ) . replace ( / ^ ( \s * \d + ) \. / , '$1\\.' ) ;
393402 return str ;
394403 }
0 commit comments