A custom implementation of CPython's textwrap.TextWrapper. This supports both wide characters (Korea, Japanese, Chinese) - including mixed string. For the most part, the `_handle_long_word` and `_wrap_chunks` functions were copy pasted out of the CPython baseline, and updated with our c
| 2733 | |
| 2734 | |
| 2735 | class _CustomTextWrap(textwrap.TextWrapper): |
| 2736 | """A custom implementation of CPython's textwrap.TextWrapper. This supports |
| 2737 | both wide characters (Korea, Japanese, Chinese) - including mixed string. |
| 2738 | For the most part, the `_handle_long_word` and `_wrap_chunks` functions were |
| 2739 | copy pasted out of the CPython baseline, and updated with our custom length |
| 2740 | and line appending logic. |
| 2741 | """ |
| 2742 | |
| 2743 | def __init__(self, *args, **kwargs): |
| 2744 | self._active_codes = [] |
| 2745 | self.max_lines = None # For python2 compatibility |
| 2746 | textwrap.TextWrapper.__init__(self, *args, **kwargs) |
| 2747 | |
| 2748 | @staticmethod |
| 2749 | def _len(item): |
| 2750 | """Custom len that gets console column width for wide |
| 2751 | and non-wide characters as well as ignores color codes""" |
| 2752 | stripped = _strip_ansi(item) |
| 2753 | if wcwidth: |
| 2754 | return wcwidth.wcswidth(stripped) |
| 2755 | else: |
| 2756 | return len(stripped) |
| 2757 | |
| 2758 | def _update_lines(self, lines, new_line): |
| 2759 | """Adds a new line to the list of lines the text is being wrapped into |
| 2760 | This function will also track any ANSI color codes in this string as well |
| 2761 | as add any colors from previous lines order to preserve the same formatting |
| 2762 | as a single unwrapped string. |
| 2763 | """ |
| 2764 | code_matches = [x for x in _ansi_codes.finditer(new_line)] |
| 2765 | color_codes = [ |
| 2766 | code.string[code.span()[0] : code.span()[1]] for code in code_matches |
| 2767 | ] |
| 2768 | |
| 2769 | # Add color codes from earlier in the unwrapped line, and then track any new ones we add. |
| 2770 | new_line = "".join(self._active_codes) + new_line |
| 2771 | |
| 2772 | for code in color_codes: |
| 2773 | if code != _ansi_color_reset_code: |
| 2774 | self._active_codes.append(code) |
| 2775 | else: # A single reset code resets everything |
| 2776 | self._active_codes = [] |
| 2777 | |
| 2778 | # Always ensure each line is color terminated if any colors are |
| 2779 | # still active, otherwise colors will bleed into other cells on the console |
| 2780 | if len(self._active_codes) > 0: |
| 2781 | new_line = new_line + _ansi_color_reset_code |
| 2782 | |
| 2783 | lines.append(new_line) |
| 2784 | |
| 2785 | def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): |
| 2786 | """_handle_long_word(chunks : [string], |
| 2787 | cur_line : [string], |
| 2788 | cur_len : int, width : int) |
| 2789 | Handle a chunk of text (most likely a word, not whitespace) that |
| 2790 | is too long to fit in any line. |
| 2791 | """ |
| 2792 | # Figure out when indent is larger than the specified width, and make |
no outgoing calls
no test coverage detected