Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
formatting.py302 linesDownload Raw Back to click
1from __future__ import annotations2 3import collections.abc as cabc4from contextlib import contextmanager5from gettext import gettext as _6 7from ._compat import term_len8from .parser import _split_opt9 10# Can force a width.  This is used by the test system11FORCED_WIDTH: int | None = None12 13 14def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]:15    widths: dict[int, int] = {}16 17    for row in rows:18        for idx, col in enumerate(row):19            widths[idx] = max(widths.get(idx, 0), term_len(col))20 21    return tuple(y for x, y in sorted(widths.items()))22 23 24def iter_rows(25    rows: cabc.Iterable[tuple[str, str]], col_count: int26) -> cabc.Iterator[tuple[str, ...]]:27    for row in rows:28        yield row + ("",) * (col_count - len(row))29 30 31def wrap_text(32    text: str,33    width: int = 78,34    initial_indent: str = "",35    subsequent_indent: str = "",36    preserve_paragraphs: bool = False,37) -> str:38    """A helper function that intelligently wraps text.  By default, it39    assumes that it operates on a single paragraph of text but if the40    `preserve_paragraphs` parameter is provided it will intelligently41    handle paragraphs (defined by two empty lines).42 43    If paragraphs are handled, a paragraph can be prefixed with an empty44    line containing the ``\\b`` character (``\\x08``) to indicate that45    no rewrapping should happen in that block.46 47    :param text: the text that should be rewrapped.48    :param width: the maximum width for the text.49    :param initial_indent: the initial indent that should be placed on the50                           first line as a string.51    :param subsequent_indent: the indent string that should be placed on52                              each consecutive line.53    :param preserve_paragraphs: if this flag is set then the wrapping will54                                intelligently handle paragraphs.55    """56    from ._textwrap import TextWrapper57 58    text = text.expandtabs()59    wrapper = TextWrapper(60        width,61        initial_indent=initial_indent,62        subsequent_indent=subsequent_indent,63        replace_whitespace=False,64    )65    if not preserve_paragraphs:66        return wrapper.fill(text)67 68    p: list[tuple[int, bool, str]] = []69    buf: list[str] = []70    indent = None71 72    def _flush_par() -> None:73        if not buf:74            return75        if buf[0].strip() == "\b":76            p.append((indent or 0, True, "\n".join(buf[1:])))77        else:78            p.append((indent or 0, False, " ".join(buf)))79        del buf[:]80 81    for line in text.splitlines():82        if not line:83            _flush_par()84            indent = None85        else:86            if indent is None:87                orig_len = term_len(line)88                line = line.lstrip()89                indent = orig_len - term_len(line)90            buf.append(line)91    _flush_par()92 93    rv = []94    for indent, raw, text in p:95        with wrapper.extra_indent(" " * indent):96            if raw:97                rv.append(wrapper.indent_only(text))98            else:99                rv.append(wrapper.fill(text))100 101    return "\n\n".join(rv)102 103 104class HelpFormatter:105    """This class helps with formatting text-based help pages.  It's106    usually just needed for very special internal cases, but it's also107    exposed so that developers can write their own fancy outputs.108 109    At present, it always writes into memory.110 111    :param indent_increment: the additional increment for each level.112    :param width: the width for the text.  This defaults to the terminal113                  width clamped to a maximum of 78.114    """115 116    def __init__(117        self,118        indent_increment: int = 2,119        width: int | None = None,120        max_width: int | None = None,121    ) -> None:122        self.indent_increment = indent_increment123        if max_width is None:124            max_width = 80125        if width is None:126            import shutil127 128            width = FORCED_WIDTH129            if width is None:130                width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50)131        self.width = width132        self.current_indent: int = 0133        self.buffer: list[str] = []134 135    def write(self, string: str) -> None:136        """Writes a unicode string into the internal buffer."""137        self.buffer.append(string)138 139    def indent(self) -> None:140        """Increases the indentation."""141        self.current_indent += self.indent_increment142 143    def dedent(self) -> None:144        """Decreases the indentation."""145        self.current_indent -= self.indent_increment146 147    def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None:148        """Writes a usage line into the buffer.149 150        :param prog: the program name.151        :param args: whitespace separated list of arguments.152        :param prefix: The prefix for the first line. Defaults to153            ``"Usage: "``.154        """155        if prefix is None:156            prefix = f"{_('Usage:')} "157 158        usage_prefix = f"{prefix:>{self.current_indent}}{prog} "159        text_width = self.width - self.current_indent160 161        if text_width >= (term_len(usage_prefix) + 20):162            # The arguments will fit to the right of the prefix.163            indent = " " * term_len(usage_prefix)164            self.write(165                wrap_text(166                    args,167                    text_width,168                    initial_indent=usage_prefix,169                    subsequent_indent=indent,170                )171            )172        else:173            # The prefix is too long, put the arguments on the next line.174            self.write(usage_prefix)175            self.write("\n")176            indent = " " * (max(self.current_indent, term_len(prefix)) + 4)177            self.write(178                wrap_text(179                    args, text_width, initial_indent=indent, subsequent_indent=indent180                )181            )182 183        self.write("\n")184 185    def write_heading(self, heading: str) -> None:186        """Writes a heading into the buffer."""187        self.write(f"{'':>{self.current_indent}}{heading}:\n")188 189    def write_paragraph(self) -> None:190        """Writes a paragraph into the buffer."""191        if self.buffer:192            self.write("\n")193 194    def write_text(self, text: str) -> None:195        """Writes re-indented text into the buffer.  This rewraps and196        preserves paragraphs.197        """198        indent = " " * self.current_indent199        self.write(200            wrap_text(201                text,202                self.width,203                initial_indent=indent,204                subsequent_indent=indent,205                preserve_paragraphs=True,206            )207        )208        self.write("\n")209 210    def write_dl(211        self,212        rows: cabc.Sequence[tuple[str, str]],213        col_max: int = 30,214        col_spacing: int = 2,215    ) -> None:216        """Writes a definition list into the buffer.  This is how options217        and commands are usually formatted.218 219        :param rows: a list of two item tuples for the terms and values.220        :param col_max: the maximum width of the first column.221        :param col_spacing: the number of spaces between the first and222                            second column.223        """224        rows = list(rows)225        widths = measure_table(rows)226        if len(widths) != 2:227            raise TypeError("Expected two columns for definition list")228 229        first_col = min(widths[0], col_max) + col_spacing230 231        for first, second in iter_rows(rows, len(widths)):232            self.write(f"{'':>{self.current_indent}}{first}")233            if not second:234                self.write("\n")235                continue236            if term_len(first) <= first_col - col_spacing:237                self.write(" " * (first_col - term_len(first)))238            else:239                self.write("\n")240                self.write(" " * (first_col + self.current_indent))241 242            text_width = max(self.width - first_col - 2, 10)243            wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True)244            lines = wrapped_text.splitlines()245 246            if lines:247                self.write(f"{lines[0]}\n")248 249                for line in lines[1:]:250                    self.write(f"{'':>{first_col + self.current_indent}}{line}\n")251            else:252                self.write("\n")253 254    @contextmanager255    def section(self, name: str) -> cabc.Iterator[None]:256        """Helpful context manager that writes a paragraph, a heading,257        and the indents.258 259        :param name: the section name that is written as heading.260        """261        self.write_paragraph()262        self.write_heading(name)263        self.indent()264        try:265            yield266        finally:267            self.dedent()268 269    @contextmanager270    def indentation(self) -> cabc.Iterator[None]:271        """A context manager that increases the indentation."""272        self.indent()273        try:274            yield275        finally:276            self.dedent()277 278    def getvalue(self) -> str:279        """Returns the buffer contents."""280        return "".join(self.buffer)281 282 283def join_options(options: cabc.Sequence[str]) -> tuple[str, bool]:284    """Given a list of option strings this joins them in the most appropriate285    way and returns them in the form ``(formatted_string,286    any_prefix_is_slash)`` where the second item in the tuple is a flag that287    indicates if any of the option prefixes was a slash.288    """289    rv = []290    any_prefix_is_slash = False291 292    for opt in options:293        prefix = _split_opt(opt)[0]294 295        if prefix == "/":296            any_prefix_is_slash = True297 298        rv.append((len(prefix), opt))299 300    rv.sort(key=lambda x: x[0])301    return ", ".join(x[1] for x in rv), any_prefix_is_slash302 
codekingpro/portable-devtools · Team Ai