§
    ‰eîi<h  ã                   óf  — d Z ddlZddlmZmZmZmZ ddlmZ ddddddd	d
dœZ	de
de
fd„Z	 d:de
de
de
dedee
eee
         ee
         f         f
d„Zde
deeeef                  de
de
dee
         f
d„Zde
deeeef                  de
de
fd„Zde
de
deeeef                  fd„Zde
de
deeeef                  fd„Zde
de
deeeef                  fd„Zde
de
deeeef                  fd„Zde
de
deeeef                  fd„Zde
de
deeeef                  fd„Zde
dee         fd „Zd!ee         d"eeeef                  deeeef                  fd#„Zde
de
deeeef                  fd$„Zde
de
deeeef                  fd%„Zde
de
deeeef                  fd&„Zd'ee
         d(ed)ed*edeeef         f
d+„Zde
d'ee
         d,ee
         de
d-e
deeeef                  fd.„Zde
d/e
d0eeeef                  deeeef                  fd1„Zd;de
de
d4ed5ede
f
d6„Zd7ee
         d8ede
de
de
f
d9„Z dS )<aP  
Fuzzy Matching Module for File Operations

Implements a multi-strategy matching chain to robustly find and replace text,
accommodating variations in whitespace, indentation, and escaping common
in LLM-generated code.

The 8-strategy chain (inspired by OpenCode), tried in order:
1. Exact match - Direct string comparison
2. Line-trimmed - Strip leading/trailing whitespace per line
3. Whitespace normalized - Collapse multiple spaces/tabs to single space
4. Indentation flexible - Ignore indentation differences entirely
5. Escape normalized - Convert \n literals to actual newlines
6. Trimmed boundary - Trim first/last line whitespace only
7. Block anchor - Match first+last lines, use similarity for middle
8. Context-aware - 50% line similarity threshold

Multi-occurrence matching is handled via the replace_all flag.

Usage:
    from tools.fuzzy_match import fuzzy_find_and_replace
    
    new_content, match_count, strategy, error = fuzzy_find_and_replace(
        content="def foo():\n    pass",
        old_string="def foo():",
        new_string="def bar():",
        replace_all=False
    )
é    N)ÚTupleÚOptionalÚListÚCallable)ÚSequenceMatcherú"ú'z--Ú-z...Ú )u   â€œu   â€�u   â€˜u   â€™u   â€”u   â€“u   â€¦õ   Â ÚtextÚreturnc                 óp   — t                                ¦   «         D ]\  }}|                      ||¦  «        } Œ| S )zBNormalizes Unicode characters to their standard ASCII equivalents.)ÚUNICODE_MAPÚitemsÚreplace)r   ÚcharÚrepls      ú3/home/div/.hermes/hermes-agent/tools/fuzzy_match.pyÚ_unicode_normalizer   +   s;   € å!×'Ò'Ñ)Ô)ð (ð (‰
ˆˆdØ�|Š|˜D $Ñ'Ô'ˆˆØ€Kó    FÚcontentÚ
old_stringÚ
new_stringÚreplace_allc           
      óâ  — |s| dddfS ||k    r| dddfS dt           fdt          fdt          fdt          fd	t          fd
t
          fdt          fdt          fdt          fg	}|D ]‰\  }} || |¦  «        }|rvt          |¦  «        dk    r|s| dddt          |¦  «        › d�fc S |dk    rt          | |||¦  «        }|r| dd|fc S t          | ||¦  «        }	|	t          |¦  «        |dfc S ŒŠ| dddfS )a)  
    Find and replace text using a chain of increasingly fuzzy matching strategies.

    Args:
        content: The file content to search in
        old_string: The text to find
        new_string: The replacement text
        replace_all: If True, replace all occurrences; if False, require uniqueness

    Returns:
        Tuple of (new_content, match_count, strategy_name, error_message)
        - If successful: (modified_content, number_of_replacements, strategy_used, None)
        - If failed: (original_content, 0, None, error_description)
    r   Nzold_string cannot be emptyz'old_string and new_string are identicalÚexactÚline_trimmedÚwhitespace_normalizedÚindentation_flexibleÚescape_normalizedÚtrimmed_boundaryÚunicode_normalizedÚblock_anchorÚcontext_awareé   zFound zY matches for old_string. Provide more context to make it unique, or use replace_all=True.z1Could not find a match for old_string in the file)Ú_strategy_exactÚ_strategy_line_trimmedÚ_strategy_whitespace_normalizedÚ_strategy_indentation_flexibleÚ_strategy_escape_normalizedÚ_strategy_trimmed_boundaryÚ_strategy_unicode_normalizedÚ_strategy_block_anchorÚ_strategy_context_awareÚlenÚ_detect_escape_driftÚ_apply_replacements)
r   r   r   r   Ú
strategiesÚstrategy_nameÚstrategy_fnÚmatchesÚ	drift_errÚnew_contents
             r   Úfuzzy_find_and_replacer9   2   s–  € ð  ð >Ø˜˜4Ð!=Ð=Ð=à�ZÒÐØ˜˜4Ð!JÐJÐJð 
•/Ð"Ø	Õ/Ð0Ø	 Õ"AÐBØ	Õ!?Ð@Ø	Õ9Ð:Ø	Õ7Ð8Ø	Õ;Ð<Ø	Õ/Ð0Ø	Õ1Ð2ð
.€Jð '1ð Bð BÑ"ˆ�{Ø�+˜g zÑ2Ô2ˆàð 	Bå�7‰|Œ|˜aÒÐ¨ÐØ  4ðX�S ™\œ\ð Xð Xð Xðð ð ð ð  Ò'Ð'Ý0°¸'À:ÈzÑZÔZ�	Øð 7Ø" A t¨YÐ6Ð6Ð6Ð6õ .¨g°wÀ
ÑKÔKˆKØ¥ G¡¤¨m¸TÐAÐAÐAÐAð3	Bð8 �A�tÐPÐPÐPr   r6   c                 ó¦   ‡ — d|vrd|vrdS d                      ˆ fd„|D ¦   «         ¦  «        }dD ]!}||v r||v r||vr|d         }d|›d	|›d
�c S Œ"dS )u'  Detect tool-call escape-drift artifacts in new_string.

    Looks for ``\'`` or ``\"`` sequences that are present in both
    old_string and new_string (i.e. the model copy-pasted them as "context"
    it intended to preserve) but don't exist in the matched region of the
    file. That pattern indicates the transport layer inserted spurious
    shell-style escapes around apostrophes or quotes â€” writing new_string
    verbatim would literally insert ``\'`` into source code.

    Returns an error string if drift is detected, None otherwise.
    ú\'ú\"NÚ c              3   ó2   •K  — | ]\  }}‰||…         V — Œd S ©N© )Ú.0ÚstartÚendr   s      €r   ú	<genexpr>z'_detect_escape_drift.<locals>.<genexpr>Ž   s0   øè è € ÐKÐK±Z°U¸C˜g e¨C iÔ0ÐKÐKÐKÐKÐKÐKr   )r;   r<   r&   zNEscape-drift detected: old_string and new_string contain the literal sequence a   but the matched region of the file does not. This is almost always a tool-call serialization artifact where an apostrophe or quote got prefixed with a spurious backslash. Re-read the file with read_file and pass old_string/new_string without backslash-escaping z characters.)Újoin)r   r6   r   r   Úmatched_regionsÚsuspectÚplains   `      r   r1   r1   w   s»   ø€ ð  �JÐÐ 5°
Ð#:Ð#:Øˆtð —g’gÐKÐKÐKÐKÀ7ÐKÑKÔKÑKÔK€Oà!ð ð ˆØ�jÐ Ð  W°
Ð%:Ð%:¸wÈoÐ?]Ð?]Ø˜A”JˆEð<Ø(/ð<ð <ð ',ð<ð <ð <ðð ð øð ˆ4r   c                 óp   — t          |d„ d¬¦  «        }| }|D ]\  }}|d|…         |z   ||d…         z   }Œ|S )a  
    Apply replacements at the given positions.
    
    Args:
        content: Original content
        matches: List of (start, end) positions to replace
        new_string: Replacement text
    
    Returns:
        Content with replacements applied
    c                 ó   — | d         S ©Nr   r@   ©Úxs    r   ú<lambda>z%_apply_replacements.<locals>.<lambda>­   s
   € °1°Q´4€ r   T)ÚkeyÚreverseN)Úsorted)r   r6   r   Úsorted_matchesÚresultrB   rC   s          r   r2   r2   Ÿ   sX   € õ ˜G¨¨ÀÐFÑFÔF€Nà€FØ$ð <ð <‰
ˆˆsØ˜˜˜” *Ñ,¨v°c°d°d¬|Ñ;ˆˆà€Mr   Úpatternc                 ó¤   — g }d}	 |                       ||¦  «        }|dk    rn-|                     ||t          |¦  «        z   f¦  «         |dz   }ŒJ|S )zStrategy 1: Exact string match.r   Téÿÿÿÿr&   )ÚfindÚappendr0   )r   rT   r6   rB   Úposs        r   r'   r'   º   sg   € à€GØ€EðØ�lŠl˜7 EÑ*Ô*ˆØ�"Š9ˆ9ØØ�Š˜˜S¥3 w¡<¤<Ñ/Ð0Ñ1Ô1Ð1Ø�a‘ˆðð €Nr   c                 óÒ   — d„ |                      d¦  «        D ¦   «         }d                     |¦  «        }|                       d¦  «        }d„ |D ¦   «         }t          | ||||¦  «        S )zŽ
    Strategy 2: Match with line-by-line whitespace trimming.
    
    Strips leading/trailing whitespace from each line before matching.
    c                 ó6   — g | ]}|                      ¦   «         ‘ŒS r@   ©Ústrip©rA   Úlines     r   ú
<listcomp>z*_strategy_line_trimmed.<locals>.<listcomp>Î   s    € ÐBÐBÐB d�T—Z’Z‘\”\ÐBÐBÐBr   Ú
c                 ó6   — g | ]}|                      ¦   «         ‘ŒS r@   r\   r^   s     r   r`   z*_strategy_line_trimmed.<locals>.<listcomp>Ò   s    € ÐGÐGÐG° §
¢
¡¤ÐGÐGÐGr   )ÚsplitrE   Ú_find_normalized_matches)r   rT   Úpattern_linesÚpattern_normalizedÚcontent_linesÚcontent_normalized_liness         r   r(   r(   Ç   s}   € ð CÐB¨g¯mªm¸DÑ.AÔ.AÐBÑBÔB€MØŸš =Ñ1Ô1Ðà—M’M $Ñ'Ô'€MØGÐG¸ÐGÑGÔGÐõ $Ø�Ð 8ØÐ#ñô ð r   c                 ó~   — d„ } ||¦  «        } || ¦  «        }t          ||¦  «        }|sg S t          | ||¦  «        S )zC
    Strategy 3: Collapse multiple whitespace to single space.
    c                 ó.   — t          j        dd| ¦  «        S )Nz[ \t]+r   )ÚreÚsub©Úss    r   Ú	normalizez2_strategy_whitespace_normalized.<locals>.normalizeß   s   € åŒv�i  aÑ(Ô(Ð(r   )r'   Ú_map_normalized_positions)r   rT   ro   rf   Úcontent_normalizedÚmatches_in_normalizeds         r   r)   r)   Û   sj   € ð)ð )ð )ð #˜ 7Ñ+Ô+ÐØ"˜ 7Ñ+Ô+Ðõ ,Ð,>Ð@RÑSÔSÐà ð Øˆ	õ % WÐ.@ÐBWÑXÔXÐXr   c           	      óÎ   — |                       d¦  «        }d„ |D ¦   «         }d„ |                      d¦  «        D ¦   «         }t          | |||d                     |¦  «        ¦  «        S )z�
    Strategy 4: Ignore indentation differences entirely.
    
    Strips all leading whitespace from lines before matching.
    ra   c                 ó6   — g | ]}|                      ¦   «         ‘ŒS r@   ©Úlstripr^   s     r   r`   z2_strategy_indentation_flexible.<locals>.<listcomp>÷   s    € ÐFÐFÐF°˜dŸkšk™mœmÐFÐFÐFr   c                 ó6   — g | ]}|                      ¦   «         ‘ŒS r@   ru   r^   s     r   r`   z2_strategy_indentation_flexible.<locals>.<listcomp>ø   s    € ÐCÐCÐC t�T—[’[‘]”]ÐCÐCÐCr   )rc   rd   rE   )r   rT   rg   Úcontent_stripped_linesre   s        r   r*   r*   ð   ss   € ð —M’M $Ñ'Ô'€MØFÐF¸ÐFÑFÔFÐØCÐC¨w¯}ª}¸TÑ/BÔ/BÐCÑCÔC€Må#Ø�Ð 6Ø�—’˜=Ñ)Ô)ñô ð r   c                 óN   — d„ } ||¦  «        }||k    rg S t          | |¦  «        S )zt
    Strategy 5: Convert escape sequences to actual characters.
    
    Handles \n -> newline, \t -> tab, etc.
    c                 ó~   — |                       dd¦  «                              dd¦  «                              dd¦  «        S )Nz\nra   z\tú	z\rú)r   rm   s    r   Úunescapez-_strategy_escape_normalized.<locals>.unescape  s6   € à�yŠy˜ Ñ%Ô%×-Ò-¨e°TÑ:Ô:×BÒBÀ5È$ÑOÔOÐOr   )r'   )r   rT   r}   Úpattern_unescapeds       r   r+   r+      sG   € ðPð Pð Pð !˜ Ñ)Ô)Ðà˜GÒ#Ð#àˆ	å˜7Ð$5Ñ6Ô6Ð6r   c           	      ó  — |                      d¦  «        }|sg S |d                              ¦   «         |d<   t          |¦  «        dk    r|d                              ¦   «         |d<   d                     |¦  «        }|                       d¦  «        }g }t          |¦  «        }t	          t          |¦  «        |z
  dz   ¦  «        D ]Å}||||z   …         }|                     ¦   «         }	|	d                              ¦   «         |	d<   t          |	¦  «        dk    r|	d                              ¦   «         |	d<   d                     |	¦  «        |k    r<t          ||||z   t          | ¦  «        ¦  «        \  }
}|                     |
|f¦  «         ŒÆ|S )zŽ
    Strategy 6: Trim whitespace from first and last lines only.
    
    Useful when the pattern boundaries have whitespace differences.
    ra   r   r&   rV   )rc   r]   r0   rE   ÚrangeÚcopyÚ_calculate_line_positionsrX   )r   rT   re   Úmodified_patternrg   r6   Úpattern_line_countÚiÚblock_linesÚcheck_linesÚ	start_posÚend_poss               r   r,   r,     sœ  € ð —M’M $Ñ'Ô'€MØð Øˆ	ð % QÔ'×-Ò-Ñ/Ô/€M�!ÑÝ
ˆ=ÑÔ˜AÒÐØ)¨"Ô-×3Ò3Ñ5Ô5ˆ�bÑà—y’y Ñ/Ô/Ðà—M’M $Ñ'Ô'€Mð €GÝ˜]Ñ+Ô+Ðå•3�}Ñ%Ô%Ð(:Ñ:¸QÑ>Ñ?Ô?ð 1ð 1ˆØ# A aÐ*<Ñ&<Ð$<Ô=ˆð "×&Ò&Ñ(Ô(ˆØ$ Qœ×-Ò-Ñ/Ô/ˆ�A‰Ýˆ{ÑÔ˜aÒÐØ)¨"œo×3Ò3Ñ5Ô5ˆK˜‰Oà�9Š9�[Ñ!Ô!Ð%5Ò5Ð5å!:Ø˜q !Ð&8Ñ"8½#¸g¹,¼,ñ"ô "ÑˆI�wð �NŠN˜I wÐ/Ñ0Ô0Ð0øà€Nr   Úoriginalc                 óÌ   — g }d}| D ]G}|                      |¦  «         t                               |¦  «        }||�t          |¦  «        ndz  }ŒH|                      |¦  «         |S )uï  Build a list mapping each original character index to its normalized index.

    Because UNICODE_MAP replacements may expand characters (e.g. em-dash â†’ '--',
    ellipsis â†’ '...'), the normalised string can be longer than the original.
    This map lets us convert positions in the normalised string back to the
    corresponding positions in the original string.

    Returns a list of length ``len(original) + 1``; entry ``i`` is the
    normalised index that character ``i`` maps to.
    r   Nr&   )rX   r   Úgetr0   )rŠ   rS   Únorm_posr   r   s        r   Ú_build_orig_to_norm_maprŽ   =  sr   € ð €FØ€HØð 9ð 9ˆØ�Š�hÑÔÐÝ�Š˜tÑ$Ô$ˆØ Ð!1•C˜‘I”I�I°qÑ8ˆˆØ
‡M‚M�(ÑÔÐØ€Mr   Úorig_to_normÚnorm_matchesc                 ó,  — i }t          | dd…         ¦  «        D ]\  }}||vr|||<   Œg }t          | ¦  «        dz
  }|D ]T\  }}||vrŒ
||         }	|	}
|
|k     r#| |
         |k     r|
dz  }
|
|k     r| |
         |k     °|                     |	|
f¦  «         ŒU|S )zNConvert (start, end) positions in the normalised string to original positions.NrV   r&   )Ú	enumerater0   rX   )r�   r�   Únorm_to_orig_startÚorig_posr�   ÚresultsÚorig_lenÚ
norm_startÚnorm_endÚ
orig_startÚorig_ends              r   Ú_map_positions_norm_to_origr›   R  sò   € ð *,ÐÝ'¨°S°b°SÔ(9Ñ:Ô:ð 4ð 4Ñˆ�(ØÐ-Ð-Ð-Ø+3Ð˜xÑ(øà%'€GÝ�<Ñ Ô  1Ñ$€Hà ,ð 
/ð 
/Ñˆ
�HØÐ/Ð/Ð/ØØ'¨
Ô3ˆ
ð ˆØ˜Ò!Ð! l°8Ô&<¸xÒ&GÐ&GØ˜‰MˆHð ˜Ò!Ð! l°8Ô&<¸xÒ&GÐ&Gð 	�Š˜
 HÐ-Ñ.Ô.Ð.Ð.à€Nr   c                 óä   — t          |¦  «        }t          | ¦  «        }|| k    r||k    rg S t          ||¦  «        }|st          ||¦  «        }|sg S t          | ¦  «        }t	          ||¦  «        S )uø  Strategy 7: Unicode normalisation.

    Normalises smart quotes, em/en-dashes, ellipsis, and non-breaking spaces
    to their ASCII equivalents in both *content* and *pattern*, then runs
    exact and line_trimmed matching on the normalised copies.

    Positions are mapped back to the *original* string via
    ``_build_orig_to_norm_map`` â€” necessary because some UNICODE_MAP
    replacements expand a single character into multiple ASCII characters,
    making a naÃ¯ve position copy incorrect.
    )r   r'   r(   rŽ   r›   )r   rT   Únorm_patternÚnorm_contentr�   r�   s         r   r-   r-   o  s‰   € õ & gÑ.Ô.€LÝ% gÑ.Ô.€LØ�wÒÐ <°7Ò#:Ð#:Øˆ	å" <°Ñ>Ô>€LØð JÝ-¨l¸LÑIÔIˆàð Øˆ	å*¨7Ñ3Ô3€LÝ& |°\ÑBÔBÐBr   c           	      ó  — t          |¦  «        }t          | ¦  «        }|                     d¦  «        }t          |¦  «        dk     rg S |d                              ¦   «         }|d                              ¦   «         }|                     d¦  «        }|                      d¦  «        }t          |¦  «        }	g }
t	          t          |¦  «        |	z
  dz   ¦  «        D ]Y}||                              ¦   «         |k    r9|||	z   dz
                                ¦   «         |k    r|
                     |¦  «         ŒZg }t          |
¦  «        }|dk    rdnd}|
D ]³}|	dk    rd}nfd                     ||dz   ||	z   dz
  …         ¦  «        }d                     |dd…         ¦  «        }t          d	||¦  «                             ¦   «         }||k    r<t          ||||	z   t          | ¦  «        ¦  «        \  }}|                     ||f¦  «         Œ´|S )
z„
    Strategy 8: Match by anchoring on first and last lines.
    Adjusted with permissive thresholds and unicode normalization.
    ra   é   r   rV   r&   ç      à?gffffffæ?g      ð?N)
r   rc   r0   r]   r€   rX   rE   r   Úratior‚   )r   rT   r�   rž   re   Ú
first_lineÚ	last_lineÚnorm_content_linesÚorig_content_linesr„   Úpotential_matchesr…   r6   Úcandidate_countÚ	thresholdÚ
similarityÚcontent_middleÚpattern_middlerˆ   r‰   s                       r   r.   r.   Ž  s5  € õ & gÑ.Ô.€LÝ% gÑ.Ô.€Là ×&Ò& tÑ,Ô,€MÝ
ˆ=ÑÔ˜AÒÐØˆ	à˜qÔ!×'Ò'Ñ)Ô)€JØ˜bÔ!×'Ò'Ñ)Ô)€Ið &×+Ò+¨DÑ1Ô1Ðà Ÿš tÑ,Ô,Ðå˜]Ñ+Ô+ÐàÐÝ•3Ð)Ñ*Ô*Ð-?Ñ?À!ÑCÑDÔDð (ð (ˆØ˜qÔ!×'Ò'Ñ)Ô)¨ZÒ7Ð7Ø˜qÐ#5Ñ5¸Ñ9Ô:×@Ò@ÑBÔBÀiÒOÐOØ×$Ò$ QÑ'Ô'Ð'øà€GÝÐ+Ñ,Ô,€Oð
 (¨1Ò,Ð,��°$€Iàð 1ð 1ˆØ Ò"Ð"ØˆJˆJð "ŸYšYÐ'9¸!¸A¹#¸aÐ@RÑ>RÐSTÑ>TÐ:TÔ'UÑVÔVˆNØ!ŸYšY }°Q°r°TÔ':Ñ;Ô;ˆNÝ(¨¨~¸~ÑNÔN×TÒTÑVÔVˆJà˜Ò"Ð"å!:Ø" A qÐ+=Ñ'=½sÀ7¹|¼|ñ"ô "ÑˆI�wð �NŠN˜I wÐ/Ñ0Ô0Ð0øà€Nr   c           	      ó`  — |                      d¦  «        }|                       d¦  «        }|sg S g }t          |¦  «        }t          t          |¦  «        |z
  dz   ¦  «        D ]Ë}||||z   …         }d}t          ||¦  «        D ]W\  }	}
t	          d|	                     ¦   «         |
                     ¦   «         ¦  «                             ¦   «         }|dk    r|dz  }ŒX|t          |¦  «        dz  k    r<t          ||||z   t          | ¦  «        ¦  «        \  }}|                     ||f¦  «         ŒÌ|S )z‰
    Strategy 9: Line-by-line similarity with 50% threshold.
    
    Finds blocks where at least 50% of lines have high similarity.
    ra   r&   r   Ngš™™™™™é?r¡   )	rc   r0   r€   Úzipr   r]   r¢   r‚   rX   )r   rT   re   rg   r6   r„   r…   r†   Úhigh_similarity_countÚp_lineÚc_lineÚsimrˆ   r‰   s                 r   r/   r/   Æ  sN  € ð —M’M $Ñ'Ô'€MØ—M’M $Ñ'Ô'€Màð Øˆ	à€GÝ˜]Ñ+Ô+Ðå•3�}Ñ%Ô%Ð(:Ñ:¸QÑ>Ñ?Ô?ð 1ð 1ˆØ# A aÐ*<Ñ&<Ð$<Ô=ˆð !"ÐÝ! -°Ñ=Ô=ð 	+ð 	+‰NˆF�FÝ! $¨¯ª©¬¸¿º¹¼ÑGÔG×MÒMÑOÔOˆCØ�dŠ{ˆ{Ø%¨Ñ*Ð%øð !¥C¨Ñ$6Ô$6¸Ñ$<Ò<Ð<Ý!:Ø˜q !Ð&8Ñ"8½#¸g¹,¼,ñ"ô "ÑˆI�wð �NŠN˜I wÐ/Ñ0Ô0Ð0øà€Nr   rg   Ú
start_lineÚend_lineÚcontent_lengthc                 ó¤   — t          d„ | d|…         D ¦   «         ¦  «        }t          d„ | d|…         D ¦   «         ¦  «        dz
  }||k    r|}||fS )a�  Calculate start and end character positions from line indices.

    Args:
        content_lines: List of lines (without newlines)
        start_line: Starting line index (0-based)
        end_line: Ending line index (exclusive, 0-based)
        content_length: Total length of the original content string

    Returns:
        Tuple of (start_pos, end_pos) in the original content
    c              3   ó:   K  — | ]}t          |¦  «        d z   V — ŒdS ©r&   N©r0   r^   s     r   rD   z,_calculate_line_positions.<locals>.<genexpr>ú  s,   è è € ÐIÐI d•C˜‘I”I ‘MÐIÐIÐIÐIÐIÐIr   Nc              3   ó:   K  — | ]}t          |¦  «        d z   V — ŒdS r¸   r¹   r^   s     r   rD   z,_calculate_line_positions.<locals>.<genexpr>û  s,   è è € ÐEÐE D•#�d‘)”)˜a‘-ÐEÐEÐEÐEÐEÐEr   r&   )Úsum)rg   r³   r´   rµ   rˆ   r‰   s         r   r‚   r‚   í  sn   € õ ÐIÐI¨m¸K¸Z¸KÔ.HÐIÑIÔIÑIÔI€IÝÐEÐE¨M¸)¸8¸)Ô,DÐEÑEÔEÑEÔEÈÑI€GØ�.Ò Ð Ø ˆØ�gÐÐr   rh   rf   c           	      ó`  — |                      d¦  «        }t          |¦  «        }g }t          t          |¦  «        |z
  dz   ¦  «        D ]d}d                     ||||z   …         ¦  «        }	|	|k    r<t	          ||||z   t          | ¦  «        ¦  «        \  }
}|                     |
|f¦  «         Œe|S )aœ  
    Find matches in normalized content and map back to original positions.
    
    Args:
        content: Original content string
        content_lines: Original content split by lines
        content_normalized_lines: Normalized content lines
        pattern: Original pattern
        pattern_normalized: Normalized pattern
    
    Returns:
        List of (start, end) positions in the original content
    ra   r&   )rc   r0   r€   rE   r‚   rX   )r   rg   rh   rT   rf   Úpattern_norm_linesÚnum_pattern_linesr6   r…   Úblockrˆ   r‰   s               r   rd   rd     sÍ   € ð  ,×1Ò1°$Ñ7Ô7ÐÝÐ.Ñ/Ô/Ðà€Gå•3Ð/Ñ0Ô0Ð3DÑDÀqÑHÑIÔIð 	1ð 	1ˆà—	’	Ð2°1°QÐ9JÑ5JÐ3JÔKÑLÔLˆàÐ&Ò&Ð&å!:Ø˜q !Ð&7Ñ"7½¸W¹¼ñ"ô "ÑˆI�wð �NŠN˜I wÐ/Ñ0Ô0Ð0øà€Nr   Ú
normalizedÚnormalized_matchesc           
      óÀ  ‡— |sg S g }d}d}|t          | ¦  «        k     rý|t          |¦  «        k     rê| |         ||         k    r |                     |¦  «         |dz  }|dz  }n’| |         dv rI||         dk    r=|                     |¦  «         |dz  }|t          | ¦  «        k     r| |         dvr|dz  }n?| |         dv r|                     |¦  «         |dz  }n|                     |¦  «         |dz  }|t          | ¦  «        k     r|t          |¦  «        k     °ê|t          | ¦  «        k     r:|                     t          |¦  «        ¦  «         |dz  }|t          | ¦  «        k     °:i }i }t          |¦  «        D ]\  }}	|	|vr|||	<   |||	<   Œg }
|D ]É\  Š}‰|v r	|‰         }n(t          ˆfd„t          |¦  «        D ¦   «         ¦  «        }|dz
  |v r||dz
           dz   }n||‰z
  z   }|t          | ¦  «        k     r,| |         dv r"|dz  }|t          | ¦  «        k     r
| |         dv °"|
                     |t          |t          | ¦  «        ¦  «        f¦  «         ŒÊ|
S )z�
    Map positions from normalized string back to original.
    
    This is a best-effort mapping that works for whitespace normalization.
    r   r&   z 	r   c              3   ó.   •K  — | ]\  }}|‰k    ¯|V — Œd S r?   r@   )rA   r…   Únr—   s      €r   rD   z,_map_normalized_positions.<locals>.<genexpr>_  s+   øè è € ÐVÐV¡4 1 aÀaÈ:ÂoÀo˜QÀoÀoÀoÀoÐVÐVr   )r0   rX   r’   Úmin)rŠ   rÀ   rÁ   r�   Úorig_idxÚnorm_idxr“   Únorm_to_orig_endr”   r�   Úoriginal_matchesr˜   r™   rš   r—   s                 @r   rp   rp   $  s	  ø€ ð ð Øˆ	ð €Là€HØ€Hà
•S˜‘]”]Ò
"Ð
" xµ#°j±/´/Ò'AÐ'AØ�HÔ ¨HÔ!5Ò5Ð5Ø×Ò Ñ)Ô)Ð)Ø˜‰MˆHØ˜‰MˆHˆHØ�hÔ 5Ð(Ð(¨Z¸Ô-AÀSÒ-HÐ-Hà×Ò Ñ)Ô)Ð)Ø˜‰MˆHà�#˜h™-œ-Ò'Ð'¨H°XÔ,>ÀeÐ,KÐ,KØ˜A‘�øØ�hÔ 5Ð(Ð(à×Ò Ñ)Ô)Ð)Ø˜‰MˆHˆHð ×Ò Ñ)Ô)Ð)Ø˜‰MˆHð' •S˜‘]”]Ò
"Ð
" xµ#°j±/´/Ò'AÐ'Að, •S˜‘]”]Ò
"Ð
"Ø×Ò�C 
™OœOÑ,Ô,Ð,Ø�A‰ˆð •S˜‘]”]Ò
"Ð
"ð
 ÐØÐå'¨Ñ5Ô5ð .ð .Ñˆ�(ØÐ-Ð-Ð-Ø+3Ð˜xÑ(Ø%-Ð˜Ñ"Ð"ð ÐØ 2ð Lð LÑˆ
�HàÐ+Ð+Ð+Ø+¨JÔ7ˆJˆJõ ÐVÐVÐVÐV­9°\Ñ+BÔ+BÐVÑVÔVÑVÔVˆJð �a‰<Ð+Ð+Ð+Ø'¨°1©Ô5¸Ñ9ˆHˆHà! X°
Ñ%:Ñ;ˆHð �˜X™œÒ&Ð&¨8°HÔ+=ÀÐ+FÐ+FØ˜‰MˆHð �˜X™œÒ&Ð&¨8°HÔ+=ÀÐ+FÐ+Fð 	×Ò ­S°½3¸x¹=¼=Ñ-IÔ-IÐ JÑKÔKÐKÐKàÐr   r    é   Úcontext_linesÚmax_resultsc                 ó®  ‡‡— | r|sdS |                       ¦   «         }|                      ¦   «         Š|r‰sdS |d                              ¦   «         }|sd„ |D ¦   «         }|sdS |d         }g }t          ‰¦  «        D ]\\  }}	|	                     ¦   «         }
|
sŒt          d||
¦  «                             ¦   «         }|dk    r|                     ||f¦  «         Œ]|sdS |                     d„ ¬¦  «         |d|…         }g }t          ¦   «         }|D ]­\  }}t          d||z
  ¦  «        Št          t          ‰¦  «        |t          |¦  «        z   |z   ¦  «        }‰|f}||v rŒQ|                     |¦  «         d                     ˆˆfd	„t          |‰z
  ¦  «        D ¦   «         ¦  «        }|                     |¦  «         Œ®|sdS d
                     |¦  «        S )zÕFind lines in content most similar to old_string for "did you mean?" feedback.

    Returns a formatted string showing the closest matching lines with context,
    or empty string if no useful match is found.
    r=   r   c                 ó^   — g | ]*}|                      ¦   «         ¯|                      ¦   «         ‘Œ+S r@   r\   )rA   Úls     r   r`   z&find_closest_lines.<locals>.<listcomp>ƒ  s-   € Ð@Ð@Ð@ A°a·g²g±i´iÐ@�a—g’g‘i”iÐ@Ð@Ð@r   Ng333333Ó?c                 ó   — | d          S rK   r@   rL   s    r   rN   z$find_closest_lines.<locals>.<lambda>–  s   € ˜q œt˜e€ r   )rO   ra   c              3   óF   •K  — | ]}‰|z   d z   d›d‰‰|z            › �V — ŒdS )r&   Ú4dz| Nr@   )rA   Újrg   rB   s     €€r   rD   z%find_closest_lines.<locals>.<genexpr>¢  sY   øè è € ð 
ð 
àð �q‰y˜1‰}Ð=Ð=Ð= =°¸±Ô#;Ð=Ð=ð
ð 
ð 
ð 
ð 
ð 
r   z
---
)Ú
splitlinesr]   r’   r   r¢   rX   ÚsortÚsetÚmaxrÅ   r0   ÚaddrE   r€   )r   r   rË   rÌ   Ú	old_linesÚanchorÚ
candidatesÚscoredr…   r_   Ústrippedr¢   ÚtopÚpartsÚseen_rangesÚ_Úline_idxrC   rO   Úsnippetrg   rB   s                       @@r   Úfind_closest_linesrä   p  sG  øø€ ð ð ˜Wð Øˆrà×%Ò%Ñ'Ô'€IØ×&Ò&Ñ(Ô(€Màð ˜Mð Øˆrð �qŒ\×ÒÑ!Ô!€FØð à@Ð@¨Ð@Ñ@Ô@ˆ
Øð 	Ø�2Ø˜A”ˆð €FÝ˜]Ñ+Ô+ð &ð &‰ˆˆ4Ø—:’:‘<”<ˆØð 	ØÝ  f¨hÑ7Ô7×=Ò=Ñ?Ô?ˆØ�3Š;ˆ;Ø�MŠM˜5 !˜*Ñ%Ô%Ð%øàð Øˆrð ‡K‚K�O�O€KÑ$Ô$Ð$Ø
��+�Ô
€Cà€EÝ‘%”%€KØð ð ‰ˆˆ8Ý�A�x -Ñ/Ñ0Ô0ˆÝ•#�mÑ$Ô$ hµ°Y±´Ñ&?À-Ñ&OÑPÔPˆØ�cˆlˆØ�+ÐÐØØ�Š˜ÑÔÐØ—)’)ð 
ð 
ð 
ð 
ð 
å˜3 ™;Ñ'Ô'ð
ñ 
ô 
ñ 
ô 
ˆð 	�Š�WÑÔÐÐàð Øˆrà�>Š>˜%Ñ Ô Ð r   ÚerrorÚmatch_countc                 óv   — |dk    rdS | r|                       d¦  «        sdS t          ||¦  «        }|sdS d|z   S )u¬  Return a '\n\nDid you mean...' snippet for plain no-match errors.

    Gated so the hint only fires for actual "old_string not found" failures.
    Ambiguous-match ("Found N matches"), escape-drift, and identical-strings
    errors all have ``match_count == 0`` but a "did you mean?" snippet would
    be misleading â€” those failed for unrelated reasons.

    Returns an empty string when there's nothing useful to append.
    r   r=   zCould not findz&

Did you mean one of these sections?
)Ú
startswithrä   )rå   ræ   r   r   Úhints        r   Úformat_no_match_hintrê   ®  s^   € ð �aÒÐØˆrØð ˜×(Ò(Ð)9Ñ:Ô:ð ØˆrÝ˜j¨'Ñ2Ô2€DØð ØˆrØ6¸Ñ=Ð=r   )F)r    rÊ   )!Ú__doc__rk   Útypingr   r   r   r   Údifflibr   r   Ústrr   ÚboolÚintr9   r1   r2   r'   r(   r)   r*   r+   r,   rŽ   r›   r-   r.   r/   r‚   rd   rp   rä   rê   r@   r   r   ú<module>rñ      s€  ððð ð< 
€	€	€	Ø 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ð 2Ø #Ð #Ð #Ð #Ð #Ð #ð ˜SØ˜SØ˜cØ˜sð	ð €ð˜Sð  Sð ð ð ð ð 16ðBQð BQ Cð BQ°Sð BQÀcð BQØ)-ðBQØ:?ÀÀSÈ(ÐSVÌ-ÐYaÐbeÔYfÐ@fÔ:gðBQð BQð BQð BQðJ% #ð %°°U¸3À¸8´_Ô0Eð %Ø%(ð%Ø69ð%Ø>FÀs¼mð%ð %ð %ð %ðP ð ¨t°E¸#¸s¸(´OÔ/Dð ÐRUð ÐZ]ð ð ð ð ð6
˜Sð 
¨3ð 
°4¸¸cÀ3¸h¼Ô3Hð 
ð 
ð 
ð 
ð Cð °#ð ¸$¸uÀSÈ#ÀX¼Ô:Oð ð ð ð ð(Y¨Sð Y¸3ð YÀ4ÈÈcÐSVÈhÌÔCXð Yð Yð Yð Yð*¨Cð ¸#ð À$ÀuÈSÐRUÈXÄÔBWð ð ð ð ð 7¨ð 7°sð 7¸tÀEÈ#ÈsÈ(ÄOÔ?Tð 7ð 7ð 7ð 7ð&'¨ð '°cð '¸dÀ5ÈÈcÈÄ?Ô>Sð 'ð 'ð 'ð 'ðT cð ¨d°3¬ið ð ð ð ð*Ø�s”)ðà�u˜S #˜X”Ô'ðð 
ˆ%��S�Œ/Ôðð ð ð ð:C¨#ð C¸ð CÀÀUÈ3ÐPSÈ8Ä_Ô@Uð Cð Cð Cð Cð>5 Cð 5°#ð 5¸$¸uÀSÈ#ÀX¼Ô:Oð 5ð 5ð 5ð 5ðp  Sð  °3ð  ¸4ÀÀcÈ3ÀhÄÔ;Pð  ð  ð  ð  ðN¨T°#¬Yð ÀCð Ø(+ðØ=@ðØEJÈ3ÐPSÈ8Ä_ðð ð ð ð(  cð  ¸$¸s¼)ð  Ø8<¸S¼	ð à'*ð à@Cð àHLÈUÐSVÐX[ÐS[Ì_ÔH]ð ð  ð  ð  ðFI¨ð I¸ð IØ37¸¸cÀ3¸h¼Ô3HðIØMQÐRWÐX[Ð]`ÐX`ÔRaÔMbðIð Ið Ið IðX;!ð ;! 3ð ;!°ð ;!ÀSð ;!Ð[^ð ;!Ðgjð ;!ð ;!ð ;!ð ;!ð|> ¨¤ð >¸Cð >Ø%(ð>Ø36ð>Ø;>ð>ð >ð >ð >ð >ð >r   