@@ -101,7 +101,7 @@ def build_inlinepatterns(md: Markdown, **kwargs: Any) -> util.Registry[InlinePro
101101NOIMG = r'(?<!\!)'
102102""" Match not an image. Partial regular expression which matches if not preceded by `!`. """
103103
104- BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)(`+)(.+?)(?<!`)\2(?!`) )'
104+ BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)` )'
105105""" Match backtick quoted string (`` `e=f()` `` or ``` ``e=f("`")`` ```). """
106106
107107ESCAPE_RE = r'\\(.)'
@@ -435,28 +435,78 @@ def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element, int,
435435
436436class BacktickInlineProcessor (InlineProcessor ):
437437 """ Return a `<code>` element containing the escaped matching text. """
438+
439+ RE_TICKS = re .compile (r'`+' )
440+
438441 def __init__ (self , pattern : str ):
439442 InlineProcessor .__init__ (self , pattern )
440443 self .ESCAPED_BSLASH = '{}{}{}' .format (util .STX , ord ('\\ ' ), util .ETX )
441444 self .tag = 'code'
442445 """ The tag of the rendered element. """
443446
444- def handleMatch (self , m : re .Match [str ], data : str ) -> tuple [etree .Element | str , int , int ]:
447+ def find_code_spans (self , start : int , text : str ) -> tuple [int , int ] | None :
448+ """Find code spans."""
449+
450+ # Get the maximum starting ticks
451+ m = self .RE_TICKS .match (text , start )
452+ if m is None : # pragma: no cover
453+ # This is not ever expected to happen.
454+ return None
455+ max_ticks = len (m .group (0 ))
456+
457+ start = m .end (0 )
458+ last = len (text )
459+ longest_span = 0
460+ end = 0
461+
462+ # Find an ending span of backticks that matches our opening
463+ i = start
464+ while i < last :
465+ m = self .RE_TICKS .match (text , i )
466+ if m is None :
467+ i += 1
468+ continue
469+
470+ # Did we find the end?
471+ i = m .end (0 )
472+ span_length = len (m .group (0 ))
473+ if max_ticks == span_length :
474+ return start , i - span_length
475+
476+ # Track the longest span of backticks we find as a fallback.
477+ if span_length > longest_span :
478+ longest_span = span_length
479+ end = i
480+
481+ # Since we didn't find an exact matching start and end,
482+ # adjust start to match the largest end we could calculate.
483+ if longest_span :
484+ return start - (max_ticks - longest_span ), end - longest_span
485+
486+ # We could not find a suitable pairing
487+ return None
488+
489+ def handleMatch (self , m : re .Match [str ], data : str ) -> tuple [etree .Element | str | None , int | None , int | None ]:
445490 """
446491 If the match contains `group(3)` of a pattern, then return a `code`
447492 [`Element`][xml.etree.ElementTree.Element] which contains HTML escaped text (with
448493 [`code_escape`][markdown.util.code_escape]) as an [`AtomicString`][markdown.util.AtomicString].
449494
450- If the match does not contain `group(3 )` then return the text of `group(1)` backslash escaped.
495+ If the match contains `group(1 )` then return the text of `group(1)` as backslash escaped.
451496
452497 """
453- if m .group (3 ):
454- el = etree .Element (self .tag )
455- el .text = util .AtomicString (util .code_escape (m .group (3 ).strip ()))
456- return el , m .start (0 ), m .end (0 )
457- else :
498+ if m .group (1 ):
458499 return m .group (1 ).replace ('\\ \\ ' , self .ESCAPED_BSLASH ), m .start (0 ), m .end (0 )
459500
501+ begin = m .start (0 )
502+ result = self .find_code_spans (begin , data )
503+ if result is not None :
504+ start , end = result
505+ el = etree .Element (self .tag )
506+ el .text = util .AtomicString (util .code_escape (data [start :end ].strip ()))
507+ return el , begin , result [1 ] + (start - begin )
508+ return None , None , None
509+
460510
461511class DoubleTagPattern (SimpleTagPattern ): # pragma: no cover
462512 """Return a ElementTree element nested in tag2 nested in tag1.
0 commit comments