o
    Œõ±jº3  ã                   @  s‚  d Z ddlmZ ddlmZ ddlmZ ddlmZm	Z	m
Z
 ddgZdd	lmZ dd
lmZmZmZmZmZmZmZmZmZmZmZmZmZmZ erPddlmZ dZG dd„ deƒZ edd�d<dd„ƒZ!edd�d=dd„ƒZ"edd�d=dd„ƒZ#edd�d=dd„ƒZ$edd�d=dd„ƒZ%G dd „ d e
ƒZ&edd�d>d$d%„ƒZ'd?d*d+„Z(		,d@dAd2d3„Z)dBd5d6„Z*dCd7d8„Z+		,d@dDd:d;„Z,e)Z-d,S )Ezú
Grapheme cluster segmentation following Unicode Standard Annex #29.

This module provides pure-Python implementation of the grapheme cluster boundary algorithm as
defined in UAX #29: Unicode Text Segmentation.

https://www.unicode.org/reports/tr29/
é    )Úannotations)ÚIntEnum)Ú	lru_cache)ÚTYPE_CHECKINGÚOptionalÚ
NamedTuplezwcwidth.bisearchzwcwidth.table_graphemeé   )Úbisearch)Ú
GRAPHEME_LÚ
GRAPHEME_TÚ
GRAPHEME_VÚGRAPHEME_LVÚINCB_EXTENDÚINCB_LINKERÚGRAPHEME_LVTÚINCB_CONSONANTÚGRAPHEME_EXTENDÚGRAPHEME_CONTROLÚGRAPHEME_PREPENDÚGRAPHEME_SPACINGMARKÚEXTENDED_PICTOGRAPHICÚGRAPHEME_REGIONAL_INDICATOR)ÚIteratoré    c                   @  sH   e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
ZdZdZdZdZdZdS )ÚGCBz'Grapheme Cluster Break property values.r   r   é   é   é   é   é   é   é   é	   é
   é   é   é   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚOTHERÚCRÚLFÚCONTROLÚEXTENDÚZWJÚREGIONAL_INDICATORÚPREPENDÚSPACING_MARKÚLÚVÚTÚLVÚLVT© r9   r9   úƒ/root/aizidognhua/tmp/workspace/projects/ec89d86c-575f-41c9-af57-ac45cbdbf775/venv/lib/python3.10/site-packages/wcwidth/grapheme.pyr   0   s     r   i   )ÚmaxsizeÚucsÚintÚreturnc                 C  sÐ   | dkrt jS | dkrt jS | dkrt jS t| tƒrt jS t| tƒr%t jS t| t	ƒr-t j
S t| tƒr5t jS t| tƒr=t jS t| tƒrEt jS t| tƒrMt jS t| tƒrUt jS t| tƒr]t jS t| tƒret jS t jS )z;Return the Grapheme_Cluster_Break property for a codepoint.r&   r#   i   )r   r,   r-   r0   Ú	_bisearchr   r.   r   r/   r   r1   r   r2   r   r3   r
   r4   r   r5   r   r6   r   r7   r   r8   r+   ©r<   r9   r9   r:   Ú_grapheme_cluster_breakF   s6   









rA   Úboolc                 C  ó   t t| tƒƒS )z6Check if codepoint has Extended_Pictographic property.)rB   r?   r   r@   r9   r9   r:   Ú_is_extended_pictographici   ó   rD   c                 C  rC   )z,Check if codepoint has InCB=Linker property.)rB   r?   r   r@   r9   r9   r:   Ú_is_incb_linkero   rE   rF   c                 C  rC   )z/Check if codepoint has InCB=Consonant property.)rB   r?   r   r@   r9   r9   r:   Ú_is_incb_consonantu   rE   rG   c                 C  rC   )z,Check if codepoint has InCB=Extend property.)rB   r?   r   r@   r9   r9   r:   Ú_is_incb_extend{   rE   rH   c                   @  s"   e Zd ZU dZded< ded< dS )ÚBreakResultz*Result of grapheme cluster break decision.rB   Úshould_breakr=   Úri_countN)r'   r(   r)   r*   Ú__annotations__r9   r9   r9   r:   rI   �   s   
 rI   Úprev_gcbÚcurr_gcbúOptional[BreakResult]c                 C  s&  | t jkr|t jkrtddd�S | t jt jt jfv r tddd�S |t jt jt jfv r0tddd�S | t jkrG|t jt jt jt jfv rGtddd�S | t jt jfv r]|t jt j	fv r]tddd�S | t jt j	fv rp|t j	krptddd�S |t j
kr{tddd�S |t jkr†tddd�S | t jkr‘tddd�S dS )zÚ
    Check simple GCB-pair-based break rules (cacheable).

    Returns BreakResult for rules that can be determined from GCB properties alone, or None if
    complex lookback rules (GB9c, GB11) need to be checked.
    Fr   ©rJ   rK   TN)r   r,   r-   rI   r.   r4   r5   r7   r8   r6   r/   r3   r2   )rM   rN   r9   r9   r:   Ú_simple_break_checkˆ   s&   	" 


rQ   ÚtextÚstrÚcurr_idxrK   c           
      C  sf  t | |ƒ}|dur|S |tjkrtddd�S t|| ƒ}t|ƒrR|d }|dkr@tt|| ƒƒr@|d8 }|dkr@tt|| ƒƒs0|dkrRtt|| ƒƒrRtddd�S | tjkr†t|ƒr†|d }|dkr†t|| ƒ}t	|ƒ}	|	tj
krw|d8 }nt|ƒr�tddd�S n|dksc| tjkr¤|tjkr¤|d dkržtd|d d�S tddd�S |tjkr«dnd}td|d�S )z�
    Determine if there should be a grapheme cluster break between prev and curr.

    Implements UAX #29 grapheme cluster boundary rules.
    NFr   rP   r   r   T)rQ   r   r0   rI   ÚordrG   rH   rF   rD   rA   r/   r1   )
rM   rN   rR   rT   rK   ÚresultÚcurr_ucsÚiÚprev_ucsÚ	prev_propr9   r9   r:   Ú_should_break¹   s<   

ÿ

ør[   NÚunistrÚstartÚendú
int | NoneúIterator[str]c           
      c  sÊ   � | sdS t | ƒ}|du r|}||ks||krdS t||ƒ}|}d}tt| | ƒƒ}|tjkr1d}t|d |ƒD ]#}tt| | ƒƒ}t||| ||ƒ}	|	j}|	j	rY| ||… V  |}|}q8| ||… V  dS )uI  
    Iterate over grapheme clusters by UAX #29 extended grapheme cluster rules.

    Grapheme clusters are "user-perceived characters" - what a user would
    consider a single character, which may consist of multiple Unicode
    codepoints (e.g., a base character with combining marks, emoji sequences).

    :param unistr: The Unicode string to segment.
    :param start: Starting index (default 0).
    :param end: Ending index (default len(unistr)).
    :yields: Grapheme cluster substrings.

    Example::

        >>> list(iter_graphemes('cafe\u0301'))
        ['c', 'a', 'f', 'eÌ�']
        >>> list(iter_graphemes('ok\U0001F468\u200D\U0001F469\u200D\U0001F467'))
        ['o', 'k', 'ðŸ‘¨\u200dðŸ‘©\u200dðŸ‘§']
        >>> list(iter_graphemes('ok\U0001F1FA\U0001F1F8'))
        ['o', 'k', 'ðŸ‡ºðŸ‡¸']

    .. versionadded:: 0.3.0
    Nr   r   )
ÚlenÚminrA   rU   r   r1   Úranger[   rK   rJ   )
r\   r]   r^   ÚlengthÚcluster_startrK   rM   ÚidxrN   rV   r9   r9   r:   Ú_iter_graphemes_pythonñ   s.   €

rg   Úposc                 C  sl  t | |d  ƒ}|dkr|dkr| |d  dkr|d S |dk rF|dkrB|dkrBt | |d  ƒ}|dkrBt|ƒtjkrBt| |d ƒS |d S |d }|dkr}|| tk r}t | | ƒ}d|  krddk rgn nnt|ƒtjkron|d8 }|dkr}|| tk sT|}tt | | ƒƒ}|tjkrŽdnd}t|d |ƒD ]}	tt | |	 ƒƒ}
t	||
| |	|ƒ}|j
}|jr±|	}|
}q—|S )a  
    Find the start of the grapheme cluster containing the character before pos.

    Scans backwards from pos to find a safe starting point, then iterates forward using standard
    break rules to find the actual cluster boundary.

    :param text: The Unicode string.
    :param pos: Position to search before (exclusive).
    :returns: Start position of the grapheme cluster.
    r   r#   r   úé€   r   r   )rU   rA   r   r2   Ú_find_cluster_startÚMAX_GRAPHEME_SCANr.   r1   rc   r[   rK   rJ   )rR   rh   Ú	target_cpÚprev_cpÚ
safe_startÚcpre   Úleft_gcbrK   rX   Ú	right_gcbrV   r9   r9   r:   rk   5  s:    ú	rk   c                 C  s    |dkrdS t | t|t| ƒƒƒS )aÌ  
    Find the grapheme cluster boundary immediately before a position.

    :param unistr: The Unicode string to search.
    :param pos: Position in the string (0 < pos <= len(unistr)).
    :returns: Start index of the grapheme cluster containing the character at pos-1.

    Example::

        >>> grapheme_boundary_before('Hello \U0001F44B\U0001F3FB', 8)
        6
        >>> grapheme_boundary_before('a\r\nb', 3)
        1

    .. versionadded:: 0.3.6
    r   )rk   rb   ra   )r\   rh   r9   r9   r:   Úgrapheme_boundary_beforei  s   rs   úOptional[int]c                 c  sŠ   � | sdS t | ƒ}|du r|nt||ƒ}t|dƒ}||ks!||kr#dS |}||krCt| |ƒ}||k r4dS | ||… V  |}||ks)dS dS )a¡  
    Iterate over grapheme clusters in reverse order (last to first).

    :param unistr: The Unicode string to segment.
    :param start: Starting index (default 0).
    :param end: Ending index (default len(unistr)).
    :yields: Grapheme cluster substrings in reverse order.

    Example::

        >>> list(iter_graphemes_reverse('cafe\u0301'))
        ['e\u0301', 'f', 'a', 'c']

    .. versionadded:: 0.3.6
    Nr   )ra   rb   Úmaxrk   )r\   r]   r^   rd   rh   re   r9   r9   r:   Úiter_graphemes_reverse  s    €

úrv   )r<   r=   r>   r   )r<   r=   r>   rB   )rM   r   rN   r   r>   rO   )rM   r   rN   r   rR   rS   rT   r=   rK   r=   r>   rI   )r   N)r\   rS   r]   r=   r^   r_   r>   r`   )rR   rS   rh   r=   r>   r=   )r\   rS   rh   r=   r>   r=   )r\   rS   r]   r=   r^   rt   r>   r`   ).r*   Ú
__future__r   Úenumr   Ú	functoolsr   Útypingr   r   r   Ú__lazy_modules__r	   r?   Útable_graphemer
   r   r   r   r   r   r   r   r   r   r   r   r   r   Úcollections.abcr   rl   r   rA   rD   rF   rG   rH   rI   rQ   r[   rg   rk   rs   rv   Úiter_graphemesr9   r9   r9   r:   Ú<module>   sJ    	þ@"
0:ý
D
4ý*