@@ -61,7 +61,8 @@ def condition(node):
6161 return isinstance (node , (nodes .literal_block , nodes .literal ))
6262
6363 for node in self .document .traverse (condition ):
64- if not node .get (_SUBSTITUTION_OPTION_NAME ):
64+ # Guard: Only process Element nodes with a truthy substitution option
65+ if not (isinstance (node , nodes .Element ) and node .attributes .get (_SUBSTITUTION_OPTION_NAME )):
6566 continue
6667
6768 # Some nodes don't have a direct document property, so walk up until we find it
@@ -74,15 +75,20 @@ def condition(node):
7475 substitution_defs = document .substitution_defs
7576 for child in node .children :
7677 old_child = child
77- for name , value in substitution_defs .items ():
78- replacement = value .astext ()
79- if isinstance (child , nodes .Text ):
80- child = nodes .Text (child .replace (f"|{ name } |" , replacement ))
81- if isinstance (node , nodes .Element ):
82- node .replace (old_child , child )
78+ # Only substitute for Text nodes
79+ if isinstance (child , nodes .Text ):
80+ new_text = str (child )
81+ for name , value in substitution_defs .items ():
82+ replacement = value .astext ()
83+ new_text = new_text .replace (f"|{ name } |" , replacement )
84+ # Only replace if the text actually changed
85+ if new_text != str (child ):
86+ child = nodes .Text (new_text )
87+ node .replace (old_child , child )
88+ # For non-Text nodes, do not replace
8389
8490 # The highlighter checks this -- without this, it will refuse to apply highlighting
85- node .rawsource = node .astext () # type: ignore[attr-defined]
91+ node .rawsource = node .astext ()
8692
8793
8894def substitution_code_role (* args , ** kwargs ) -> tuple [list , list [Any ]]:
0 commit comments