2121 HTML_TAGNAME ,
2222 PUNCTUATION ,
2323 parse_link ,
24+ parse_link_with_end ,
2425 parse_link_label ,
2526 unescape_char ,
2627)
@@ -141,6 +142,9 @@ def parse_link(self, m: Match[str], state: InlineState) -> Optional[int]:
141142 if not is_image and pos <= state .no_link_before :
142143 state .append_token ({"type" : "text" , "raw" : marker })
143144 return pos
145+ if is_image and pos <= state .no_image_before :
146+ state .append_token ({"type" : "text" , "raw" : marker })
147+ return pos
144148
145149 text = None
146150 text_start = pos
@@ -175,22 +179,28 @@ def parse_link(self, m: Match[str], state: InlineState) -> Optional[int]:
175179 _mark_no_link_before (state , body_end_pos )
176180 return None
177181
178- rules = ["codespan" , "prec_auto_link" , "prec_inline_html" ]
179- prec_pos = self .precedence_scan (m , state , end_pos , rules )
180- if prec_pos :
181- return prec_pos
182+ if not is_image :
183+ rules = ["codespan" , "prec_auto_link" , "prec_inline_html" ]
184+ prec_pos = self .precedence_scan (m , state , end_pos , rules )
185+ if prec_pos :
186+ return prec_pos
182187
183188 if end_pos < len (state .src ):
184189 c = state .src [end_pos ]
185190 if c == "(" :
186191 # standard link [text](<url> "title")
187- attrs , pos2 = parse_link (state .src , end_pos + 1 )
192+ attrs , pos2 , scan_end = parse_link_with_end (state .src , end_pos + 1 )
188193 if pos2 :
189194 if text is None :
190195 text = state .src [text_start :text_end ]
191196 token = self .__parse_link_token (is_image , text , attrs , state )
192197 state .append_token (token )
193198 return pos2
199+ if scan_end > body_end_pos :
200+ if is_image :
201+ _mark_no_image_before (state , scan_end )
202+ else :
203+ _mark_no_link_before (state , scan_end )
194204
195205 elif c == "[" :
196206 # standard ref link [text][label]
@@ -557,6 +567,54 @@ class _Delimiter:
557567 #: original run length, used by the CommonMark multiple-of-3 rule even
558568 #: after the run has been partially consumed
559569 orig_length : int = 0
570+ order : int = 0
571+
572+
573+ class _DelimiterIndex :
574+ """Track part positions without rewriting every delimiter after a splice."""
575+
576+ def __init__ (self , delimiters : List [_Delimiter ], part_count : int ) -> None :
577+ self ._tree = [0 ] * (part_count + 1 )
578+ self ._next = list (range (len (delimiters ) + 1 ))
579+
580+ def current (self , delimiter : _Delimiter ) -> int :
581+ index = delimiter .index
582+ total = 0
583+ cursor = index + 1
584+ while cursor :
585+ total += self ._tree [cursor ]
586+ cursor -= cursor & - cursor
587+ return index - total
588+
589+ def collapse (self , closer : _Delimiter , removed : int ) -> None :
590+ if not removed :
591+ return
592+ cursor = closer .index + 1
593+ while cursor < len (self ._tree ):
594+ self ._tree [cursor ] += removed
595+ cursor += cursor & - cursor
596+
597+ def deactivate (self , order : int ) -> None :
598+ self ._next [order ] = self ._find (order + 1 )
599+
600+ def deactivate_range (
601+ self , delimiters : List [_Delimiter ], start : int , end : int
602+ ) -> None :
603+ order = self ._find (start )
604+ while order < end :
605+ delimiters [order ].length = 0
606+ self ._next [order ] = self ._find (order + 1 )
607+ order = self ._find (order )
608+
609+ def _find (self , order : int ) -> int :
610+ root = order
611+ while self ._next [root ] != root :
612+ root = self ._next [root ]
613+ while self ._next [order ] != order :
614+ parent = self ._next [order ]
615+ self ._next [order ] = root
616+ order = parent
617+ return root
560618
561619
562620def _finalize_emphasis_tokens (
@@ -711,7 +769,7 @@ def _split_text_token(
711769 index = len (parts )
712770 parts .append ({"type" : "text" , "raw" : text [pos :end ]})
713771 if can_open or can_close :
714- delimiters .append (_Delimiter (index , marker , length , can_open , can_close , length ))
772+ delimiters .append (_Delimiter (index , marker , length , can_open , can_close , length , len ( delimiters ) ))
715773 pos = end
716774
717775
@@ -726,6 +784,7 @@ def _process_emphasis_delimiters(
726784 delimiters : List [_Delimiter ],
727785 max_depth : int ,
728786) -> None :
787+ index_map = _DelimiterIndex (delimiters , len (parts ))
729788 closer_pos = 0
730789 openers_bottom : Dict [Tuple [str , int , bool ], int ] = {}
731790 while closer_pos < len (delimiters ):
@@ -755,26 +814,28 @@ def _process_emphasis_delimiters(
755814 closer_pos += 1
756815 continue
757816
817+ opener_index = index_map .current (opener )
818+ closer_index = index_map .current (closer )
758819 if opener .length >= 2 and closer .length >= 2 :
759820 use_length = 2
760821 else :
761822 use_length = 1
762- if use_length == 2 and not _has_strong_enabled (parts , opener , closer ):
823+ if use_length == 2 and not _has_strong_enabled_at (parts , opener_index , closer_index ):
763824 use_length = 1
764- if use_length == 1 and not _has_emphasis_enabled (parts , opener , closer ):
825+ if use_length == 1 and not _has_emphasis_enabled_at (parts , opener_index , closer_index ):
765826 closer_pos += 1
766827 continue
767- if not _has_emphasis_content (parts , opener . index + 1 , closer . index ):
828+ if not _has_emphasis_content (parts , opener_index + 1 , closer_index ):
768829 closer_pos += 1
769830 continue
770831
771- opener_text = parts [opener . index ]
772- closer_text = parts [closer . index ]
832+ opener_text = parts [opener_index ]
833+ closer_text = parts [closer_index ]
773834 if opener_text ["type" ] != "text" or closer_text ["type" ] != "text" :
774835 closer_pos += 1
775836 continue
776837
777- children = parts [opener . index + 1 : closer . index ]
838+ children = parts [opener_index + 1 : closer_index ]
778839 if max_depth > 0 and _emphasis_depth (children ) >= max_depth :
779840 closer_pos += 1
780841 continue
@@ -785,24 +846,22 @@ def _process_emphasis_delimiters(
785846 else :
786847 node = {"type" : "emphasis" , "children" : children }
787848
788- old_closer_index = closer . index
789- parts [opener . index + 1 : old_closer_index ] = [node ]
849+ old_closer_index = closer_index
850+ parts [opener_index + 1 : old_closer_index ] = [node ]
790851
791- removed = old_closer_index - opener .index - 2
792- closer .index = opener .index + 2
852+ removed = old_closer_index - opener_index - 2
793853 if removed :
794- for delimiter in delimiters :
795- if opener .index < delimiter .index < old_closer_index :
796- delimiter .length = 0
797- elif delimiter .index >= old_closer_index :
798- delimiter .index -= removed
854+ index_map .deactivate_range (delimiters , opener_pos + 1 , closer_pos )
855+ index_map .collapse (closer , removed )
799856
800857 opener .length -= use_length
801858 closer .length -= use_length
802859 if opener .length == 0 :
803860 opener .can_open = False
861+ index_map .deactivate (opener .order )
804862 if closer .length == 0 :
805863 closer .can_close = False
864+ index_map .deactivate (closer .order )
806865
807866 if opener .can_open or closer .can_close :
808867 closer_pos = max (opener_pos , openers_bottom .get (opener_key , 0 ))
@@ -826,11 +885,19 @@ def _emphasis_depth(tokens: List[Dict[str, Any]]) -> int:
826885
827886
828887def _has_strong_enabled (parts : List [Dict [str , Any ]], opener : _Delimiter , closer : _Delimiter ) -> bool :
829- return len ( _text_raw ( parts [ opener .index ])) >= 2 and len ( _text_raw ( parts [ closer .index ])) >= 2
888+ return _has_strong_enabled_at ( parts , opener .index , closer .index )
830889
831890
832891def _has_emphasis_enabled (parts : List [Dict [str , Any ]], opener : _Delimiter , closer : _Delimiter ) -> bool :
833- return bool (_text_raw (parts [opener .index ]) and _text_raw (parts [closer .index ]))
892+ return _has_emphasis_enabled_at (parts , opener .index , closer .index )
893+
894+
895+ def _has_strong_enabled_at (parts : List [Dict [str , Any ]], opener_index : int , closer_index : int ) -> bool :
896+ return len (_text_raw (parts [opener_index ])) >= 2 and len (_text_raw (parts [closer_index ])) >= 2
897+
898+
899+ def _has_emphasis_enabled_at (parts : List [Dict [str , Any ]], opener_index : int , closer_index : int ) -> bool :
900+ return bool (_text_raw (parts [opener_index ]) and _text_raw (parts [closer_index ]))
834901
835902
836903def _text_raw (token : Dict [str , Any ]) -> str :
@@ -915,6 +982,11 @@ def _mark_no_link_before(state: InlineState, end_pos: int) -> None:
915982 state .no_link_before = end_pos
916983
917984
985+ def _mark_no_image_before (state : InlineState , end_pos : int ) -> None :
986+ if end_pos > state .no_image_before :
987+ state .no_image_before = end_pos
988+
989+
918990def _find_closing_bracket (state : InlineState , pos : int ) -> Optional [int ]:
919991 return _get_closing_bracket_map (state ).get (pos )
920992
0 commit comments