1111import threading
1212import unicodedata
1313from enum import Enum
14- from typing import Any , Callable , Iterable , List , Optional , TextIO , Union
14+ from typing import Any , Callable , Iterable , List , OrderedDict , Optional , TextIO , Union
1515
1616from . import constants
1717
@@ -696,7 +696,7 @@ def align_text(text: str, alignment: TextAlignment, *, fill_char: str = ' ',
696696 :param truncate: if True, then each line will be shortened to fit within the display width. The truncated
697697 portions are replaced by a '…' character. Defaults to False.
698698 :return: aligned text
699- :raises: TypeError if fill_char is more than one character
699+ :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
700700 ValueError if text or fill_char contains an unprintable character
701701 ValueError if width is less than 1
702702 """
@@ -716,7 +716,7 @@ def align_text(text: str, alignment: TextAlignment, *, fill_char: str = ' ',
716716 if fill_char == '\t ' :
717717 fill_char = ' '
718718
719- if len (fill_char ) != 1 :
719+ if len (ansi . strip_style ( fill_char ) ) != 1 :
720720 raise TypeError ("Fill character must be exactly one character long" )
721721
722722 fill_char_width = ansi .style_aware_wcswidth (fill_char )
@@ -788,7 +788,7 @@ def align_left(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
788788 :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
789789 replaced by a '…' character. Defaults to False.
790790 :return: left-aligned text
791- :raises: TypeError if fill_char is more than one character
791+ :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
792792 ValueError if text or fill_char contains an unprintable character
793793 ValueError if width is less than 1
794794 """
@@ -811,7 +811,7 @@ def align_center(text: str, *, fill_char: str = ' ', width: Optional[int] = None
811811 :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
812812 replaced by a '…' character. Defaults to False.
813813 :return: centered text
814- :raises: TypeError if fill_char is more than one character
814+ :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
815815 ValueError if text or fill_char contains an unprintable character
816816 ValueError if width is less than 1
817817 """
@@ -834,7 +834,7 @@ def align_right(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
834834 :param truncate: if True, then text will be shortened to fit within the display width. The truncated portion is
835835 replaced by a '…' character. Defaults to False.
836836 :return: right-aligned text
837- :raises: TypeError if fill_char is more than one character
837+ :raises: TypeError if fill_char is more than one character (not including ANSI style sequences)
838838 ValueError if text or fill_char contains an unprintable character
839839 ValueError if width is less than 1
840840 """
@@ -878,14 +878,7 @@ def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str:
878878 return line
879879
880880 # Find all style sequences in the line
881- start = 0
882- styles = collections .OrderedDict ()
883- while True :
884- match = ansi .ANSI_STYLE_RE .search (line , start )
885- if match is None :
886- break
887- styles [match .start ()] = match .group ()
888- start += len (match .group ())
881+ styles = get_styles_in_text (line )
889882
890883 # Add characters one by one and preserve all style sequences
891884 done = False
@@ -919,3 +912,30 @@ def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str:
919912 truncated_buf .write ('' .join (styles .values ()))
920913
921914 return truncated_buf .getvalue ()
915+
916+
917+ def get_styles_in_text (text : str ) -> OrderedDict [int , str ]:
918+ """
919+ Return an OrderedDict containing all ANSI style sequences found in a string
920+
921+ The structure of the dictionary is:
922+ key: index where sequences begins
923+ value: ANSI style sequence found at index in text
924+
925+ Keys are in ascending order
926+
927+ :param text: text to search for style sequences
928+ """
929+ from . import ansi
930+
931+ start = 0
932+ styles = collections .OrderedDict ()
933+
934+ while True :
935+ match = ansi .ANSI_STYLE_RE .search (text , start )
936+ if match is None :
937+ break
938+ styles [match .start ()] = match .group ()
939+ start += len (match .group ())
940+
941+ return styles
0 commit comments