٢iEq2dZddlmZddlZddlZddlZddlmZmZddl m Z ddl m Z ddl mZdd lmZdd lmZerdd lmZmZGd d eZejdZd9dZd:dZd;dZGddejZdz Sequence-aware text wrapping functions. This module provides functions for wrapping text that may contain terminal escape sequences, with proper handling of Unicode grapheme clusters and character display widths. ) annotationsN) TYPE_CHECKING NamedTuple)width)iter_sequences)iter_graphemes) propagate_sgr)ZERO_WIDTH_PATTERN)AnyLiteralc2eZdZUdZded<ded<ded<dS)_HyperlinkStatez>State for tracking an open OSC 8 hyperlink across line breaks.strurlparams terminatorN)__name__ __module__ __qualname____doc____annotations__`/home/jenkins/workspace/simtester-sanitize/venv/lib/python3.11/site-packages/wcwidth/textwrap.pyrrs1HH HHHKKKOOOOOrrz*\x1b]8;([^;]*);([^\x07\x1b]*)(\x07|\x1b\\)seqrreturn_HyperlinkState | Nonect|x}rKt|d|d|dSdS)z0Parse OSC 8 open sequence, return state or None.rrrrN)_HYPERLINK_OPEN_REmatchrgroup)rms r_parse_hyperlink_openr''sW  % %c * **Y1771::aggajjQWWUVZZXXXX 4rrrrcd|d||S)zGenerate OSC 8 open sequence.z]8;;rr"s r_make_hyperlink_openr*.s /V / /c /: / //rc d|S)zGenerate OSC 8 close sequence.z]8;;r)rs r_make_hyperlink_closer,3s "j " ""rceZdZdZd)ddddd*fdZed+dZd,dZd-dZd-dZ d.dZ d/dZ d0dZ d1d"Z d2d$Zd3d&Zd,d'Zd-d(ZxZS)4SequenceTextWrappera Sequence-aware text wrapper extending :class:`textwrap.TextWrapper`. This wrapper properly handles terminal escape sequences and Unicode grapheme clusters when calculating text width for wrapping. This implementation is based on the SequenceTextWrapper from the 'blessed' library, with contributions from Avram Lubkin and grayjk. The key difference from the blessed implementation is the addition of grapheme cluster support via :func:`~.iter_graphemes`, providing width calculation for ZWJ emoji sequences, VS-16 emojis and variations, regional indicator flags, and combining characters. OSC 8 hyperlinks are handled specially: when a hyperlink must span multiple lines, each line receives complete open/close sequences with a shared ``id`` parameter, ensuring terminals treat the fragments as a single hyperlink for hover underlining. If the original hyperlink already has an ``id`` parameter, it is preserved; otherwise, one is generated. Fparser control_codestabsizeambiguous_widthrintr3$Literal['parse', 'strict', 'ignore']r4r5kwargsr rNonec htjdd|i|||_||_||_dS)a Initialize the wrapper. :param width: Maximum line width in display cells. :param control_codes: How to handle control sequences (see :func:`~.width`). :param tabsize: Tab stop width for tab expansion. :param ambiguous_width: Width to use for East Asian Ambiguous (A) characters. :param kwargs: Additional arguments passed to :class:`textwrap.TextWrapper`. rNr)super__init__r3r4r5)selfrr3r4r5r8 __class__s rr<zSequenceTextWrapper.__init__LsD //u////* .rrc*tjdS)z7Generate unique hyperlink id as 8-character hex string.)secrets token_hexrrr_next_hyperlink_idz&SequenceTextWrapper._next_hyperlink_id_s ###rtextcFt||j|j|jS)z,Measure text width accounting for sequences.r2)_widthr3r4r5r=rDs rrFzSequenceTextWrapper._widthds*d$*>> wrap('foo \x1b]8;;https://example.com\x07link\x1b]8;;\x07 bar', 6) ['foo', '\x1b]8;;https://example.com\x07link\x1b]8;;\x07', 'bar'] Both BEL (``\x07``) and ST (``\x1b\\``) terminators are supported. rIrF rz]8;;\z]8;;z]) risspacerK startswithlentextwrap TextWrapper_split enumerate)r=rDchar_end stripped_text original_posprev_was_hyperlink_closerOrPcharis_hyperlink_closestripped_chunksrN stripped_pos num_chunksidxchunk chunk_len start_origend_origs rr^zSequenceTextWrapper._splitysX0!  #( -d33 > >OGV >+22 @R@R@T@T2!S(MOOL111#**D A%LOOL111!T)MM+0((%,%7%78Z%[%[" &&w//6M6%b)11336-6%,   555G , +=((  %%%#.55dMJJ 4 6M )) #O44 & &JCE I+a//XlQ>N5OJj1n$$#B<#L9$|j r d|d |j }nd|}|t3|j z }d|j vrRt|j |j |j }t|j ||j }|||d}t7|j ||j }nd}d}|jr|}|||zd }nX||j}|r||d}|rn||z| kred|}|||}||t3|j z }|||z|jzn|||dz}|d=||rJ||d}|||z|jkr||jz|d<n3|||jzn|C|S) a Wrap chunks into lines using sequence-aware width. Override TextWrapper._wrap_chunks to use _width instead of len. Follows stdlib's algorithm: greedily fill lines, handle long words. Also handle OSC hyperlink processing. When hyperlinks span multiple lines, each line gets complete open/close sequences with matching id parameters for hover underlining continuity per OSC 8 spec. Nrz#placeholder too large for max widthTrrXrIzid=:F) max_linessubsequent_indentinitial_indentrF placeholderlstripr ValueErrorlistreversedr*rrrrQdrop_whitespacestriprSrKpop_handle_long_wordrLr[_track_hyperlink_staterCr,replacerrstrip_rstrip_visible)r=rnindentlines is_first_linehyperlink_statecurrent_hyperlink_id current_line current_width line_widthopen_seqstripped sequencesrj chunk_width stripped_lastno_more_content line_content new_stateold_opennew_open placeholder_w last_text prev_lines r _wrap_chunksz SequenceTextWrapper._wrap_chunkss I > %~!!/, F##kk$"2"9"9";";<<=j!!!!FGGG 26+/hv&&''M &(LM-:UT((t?UFdkk&&9&99J*/#')?A[]]%r 2r ,,VBZ88H# 8 8( 88>>CSCS 8 33F2J?? 2J88!*VBZ!7F2J r "kk%00  ;.*<< '' 555![0MM  #$++fRj11J>>&&L-!% BGGL,A,A B B #VBZ#r #VBZ# HT[D11,r2BCCCY[M$ D D! D*7*=*=*?*? D 33L4DEE \"-=!>!>>  $DD'3B'7)'CL$T  JA(AKK1$A--fQi88>>@@@  N*E Q77'8)Z77#%77<#8#8L!% ; ;L/ Z ZI!,/7$ (8887@7G 4 4!*!1Y%Y$*A*A*C*C$X$XiFV$X$X!5 48YT=T=T=V=V7X7X 4$(=i>R(S(SS ! (888'; ) y/?AU(W(WH'; ) /CYEY([([H+7+?+?(TU+V+VL+:%M+?AU+W+W+//3,+='3':':'<'< LL,!6777$)MM%)KK0@$A$AM&I$($9$9,r:J$K$K %OO-- "$1M$AZ$O$O+-77<+@+@L(,(C(C ,o)?)?I(4 ,0E$-$81:1:!: !LL,)>AQ)QRRR!%\"5E)F)FF (,'I!&(,(<(.E.E.G.G%GHHH[M ^ rstaterct|D]9\}}|r2t|}| |jr|}"|drd}:|S)z Track hyperlink state through text. :param text: Text to scan for hyperlink sequences. :param state: Current state or None if outside hyperlink. :returns: Updated state after processing text. NrW)rr'rrZ)r=rDrrOrP parsed_links rr}z*SequenceTextWrapper._track_hyperlink_statesj .d33 ! !OGV !3G<< *{*'EE''(JKK! E rreversed_chunkscur_linecur_lencv|dkrd}n||z }|d}|jrd}d}|jr||} t| |krY| dd|} | dkrz8SequenceTextWrapper._handle_long_word..s&-V-V1a3h-V-V-V-V-V-VrNT) break_long_wordsbreak_on_hyphensrQr[rfindany_map_stripped_pos_to_original_find_break_position_find_first_grapheme_endrKr{) r=rrrr space_leftrjbreak_at_hyphen hyphen_endr hyphen_pos actual_ends rr|z%SequenceTextWrapper._handle_long_words 199JJJ#   3#OJ$ /0077x==:--!)Q !C!CJ!A~~#-V-V*@U-V-V-V*V*V~%)%G%Gz\]~%^%^ *. F' !66ujII ??8?!%!>!>u!E!EJ OOE+:+. / / /"' "4OB    3 OOO//11 2 2 2 2 2 3 3rrgcd}d}t|D]^\}}|r|t|z }|t|z|kr |||z zcS|t|z }|t|z }_|S)z?Map a position in stripped text back to original text position.r)rr[)r=rDrg stripped_idx original_idxrOrPs rrz1SequenceTextWrapper._map_stripped_pos_to_originals  -d33 - -OGV -G , G ,|;;#|l'BCCCCG , G , r max_widthcxd}d}|t|kr||}|dkr,tj||}|r|}Mt t ||}||}||z|kr|S||z }|t|z }|t|k|S)z;Find string index in text that fits within max_width cells.r)start)r[r r$endnextr rF) r=rDrri width_so_farrdr$graphemegrapheme_widths rrz(SequenceTextWrapper._find_break_positions CIIoo9Dv~~*0s;;))++CN4s;;;<>2$-h$7$7 ' ' A 8|| g&&&&h gnn..//// ' g&&&wwvrr/) rr6r3r7r4r6r5r6r8r rr9)rr)rDrrr6)rDrrr)rDrrrT)rnrTrrT)rDrrrrr) rrTrrTrr6rr6rr9)rDrrgr6rr6)rDrrr6rr6)rrrrr< staticmethodrCrFrQrSr^rr}r|rrrr __classcell__)r>s@rr.r.8sf&/GN !()////////&$$$\$<<<< \\\\|ssssj&/3/3/3/3b&:////rr.r/r0r1TrIFz [...])r3r4 expand_tabsreplace_whitespacer5rsrrfix_sentence_endingsrrryrqrtr rDrr6r3r7r4rboolrr5rsrrrrrryrq int | Nonertr rTct||||||||| | | | | |}||}|rt|}|S)u Wrap text to fit within given width, returning a list of wrapped lines. Like :func:`textwrap.wrap`, but measures width in display cells rather than characters, correctly handling wide characters, combining marks, and terminal escape sequences. :param text: Text to wrap, may contain terminal sequences. :param width: Maximum line width in display cells. :param control_codes: How to handle terminal sequences (see :func:`~.width`). :param tabsize: Tab stop width for tab expansion. :param expand_tabs: If True (default), tab characters are expanded to spaces using ``tabsize``. :param replace_whitespace: If True (default), each whitespace character is replaced with a single space after tab expansion. When False, control whitespace like ``\n`` has zero display width (unlike :func:`textwrap.wrap` which counts ``len()``), so wrap points may differ from stdlib for non-space whitespace characters. :param ambiguous_width: Width to use for East Asian Ambiguous (A) characters. Default is ``1`` (narrow). Set to ``2`` for CJK contexts. :param initial_indent: String prepended to first line. :param subsequent_indent: String prepended to subsequent lines. :param fix_sentence_endings: If True, ensure sentences are always separated by exactly two spaces. :param break_long_words: If True, break words longer than width. :param break_on_hyphens: If True, allow breaking at hyphens. :param drop_whitespace: If True (default), whitespace at the beginning and end of each line (after wrapping but before indenting) is dropped. Set to False to preserve whitespace. :param max_lines: If set, output contains at most this many lines, with ``placeholder`` appended to the last line if the text was truncated. :param placeholder: String appended to the last line when text is truncated by ``max_lines``. Default is ``' [...]'``. :param propagate_sgr: If True (default), SGR (terminal styling) sequences are propagated across wrapped lines. Each line ends with a reset sequence and the next line begins with the active style restored. :returns: List of wrapped lines without trailing newlines. SGR (terminal styling) sequences are propagated across wrapped lines by default. Each line ends with a reset sequence and the next line begins with the active style restored:: >>> wrap('\x1b[1;34mHello world\x1b[0m', width=6) ['\x1b[1;34mHello\x1b[0m', '\x1b[1;34mworld\x1b[0m'] Set ``propagate_sgr=False`` to disable this behavior. Like :func:`textwrap.wrap`, newlines in the input text are treated as whitespace and collapsed. To preserve paragraph breaks, wrap each paragraph separately:: >>> text = 'First line.\nSecond line.' >>> wrap(text, 40) # newline collapsed to space ['First line. Second line.'] >>> [line for para in text.split('\n') ... for line in (wrap(para, 40) if para else [''])] ['First line.', 'Second line.'] .. seealso:: :func:`textwrap.wrap`, :class:`textwrap.TextWrapper` Standard library text wrapping (character-based). :class:`.SequenceTextWrapper` Class interface for advanced wrapping options. .. versionadded:: 0.3.0 .. versionchanged:: 0.5.0 Added ``propagate_sgr`` parameter (default True). .. versionchanged:: 0.6.0 Added ``expand_tabs``, ``replace_whitespace``, ``fix_sentence_endings``, ``drop_whitespace``, ``max_lines``, and ``placeholder`` parameters. Example:: >>> from wcwidth import wrap >>> wrap('hello world', 5) ['hello', 'world'] >>> wrap('中文字符', 4) # CJK characters (2 cells each) ['中文', '字符'] )rr3r4rrr5rsrrrrrryrqrt)r.wrap_propagate_sgr)rDrr3r4rrr5rsrrrrrryrqrtr wrapperrs rrrspF"#-'%+1))'G LL  E&u%% Lr)rrrr)rrrrrrrr)rrrrr)"rDrrr6r3r7r4r6rrrrr5r6rsrrrrrrrrrrryrrqrrtrr rrrT)r __future__rrerAr\typingrrwcwidthrrFrrr sgr_stater rescape_sequencesr r r rcompiler#r'r*r,r]r.rrrrrs #""""" ,,,,,,,,%$$$$$######$$$$$$666666000000$########j RZ MNN0000 #### ]]]]](.]]]@x?F!$( !!"$&+"&"&!%!%$#xxxxxxxxr