Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
formatting.py302 linesDownload Raw Back to click
1import typing as t2from contextlib import contextmanager3from gettext import gettext as _4 5from ._compat import term_len6from .parser import split_opt7 8# Can force a width.  This is used by the test system9FORCED_WIDTH: t.Optional[int] = None10 11 12def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]:13    widths: t.Dict[int, int] = {}14 15    for row in rows:16        for idx, col in enumerate(row):17            widths[idx] = max(widths.get(idx, 0), term_len(col))18 19    return tuple(y for x, y in sorted(widths.items()))20 21 22def iter_rows(23    rows: t.Iterable[t.Tuple[str, str]], col_count: int24) -> t.Iterator[t.Tuple[str, ...]]:25    for row in rows:26        yield row + ("",) * (col_count - len(row))27 28 29def wrap_text(30    text: str,31    width: int = 78,32    initial_indent: str = "",33    subsequent_indent: str = "",34    preserve_paragraphs: bool = False,35) -> str:36    """A helper function that intelligently wraps text.  By default, it37    assumes that it operates on a single paragraph of text but if the38    `preserve_paragraphs` parameter is provided it will intelligently39    handle paragraphs (defined by two empty lines).40 41    If paragraphs are handled, a paragraph can be prefixed with an empty42    line containing the ``\\b`` character (``\\x08``) to indicate that43    no rewrapping should happen in that block.44 45    :param text: the text that should be rewrapped.46    :param width: the maximum width for the text.47    :param initial_indent: the initial indent that should be placed on the48                           first line as a string.49    :param subsequent_indent: the indent string that should be placed on50                              each consecutive line.51    :param preserve_paragraphs: if this flag is set then the wrapping will52                                intelligently handle paragraphs.53    """54    from ._textwrap import TextWrapper55 56    text = text.expandtabs()57    wrapper = TextWrapper(58        width,59        initial_indent=initial_indent,60        subsequent_indent=subsequent_indent,61        replace_whitespace=False,62    )63    if not preserve_paragraphs:64        return wrapper.fill(text)65 66    p: t.List[t.Tuple[int, bool, str]] = []67    buf: t.List[str] = []68    indent = None69 70    def _flush_par() -> None:71        if not buf:72            return73        if buf[0].strip() == "\b":74            p.append((indent or 0, True, "\n".join(buf[1:])))75        else:76            p.append((indent or 0, False, " ".join(buf)))77        del buf[:]78 79    for line in text.splitlines():80        if not line:81            _flush_par()82            indent = None83        else:84            if indent is None:85                orig_len = term_len(line)86                line = line.lstrip()87                indent = orig_len - term_len(line)88            buf.append(line)89    _flush_par()90 91    rv = []92    for indent, raw, text in p:93        with wrapper.extra_indent(" " * indent):94            if raw:95                rv.append(wrapper.indent_only(text))96            else:97                rv.append(wrapper.fill(text))98 99    return "\n\n".join(rv)100 101 102class HelpFormatter:103    """This class helps with formatting text-based help pages.  It's104    usually just needed for very special internal cases, but it's also105    exposed so that developers can write their own fancy outputs.106 107    At present, it always writes into memory.108 109    :param indent_increment: the additional increment for each level.110    :param width: the width for the text.  This defaults to the terminal111                  width clamped to a maximum of 78.112    """113 114    def __init__(115        self,116        indent_increment: int = 2,117        width: t.Optional[int] = None,118        max_width: t.Optional[int] = None,119    ) -> None:120        import shutil121 122        self.indent_increment = indent_increment123        if max_width is None:124            max_width = 80125        if width is None:126            width = FORCED_WIDTH127            if width is None:128                width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50)129        self.width = width130        self.current_indent = 0131        self.buffer: t.List[str] = []132 133    def write(self, string: str) -> None:134        """Writes a unicode string into the internal buffer."""135        self.buffer.append(string)136 137    def indent(self) -> None:138        """Increases the indentation."""139        self.current_indent += self.indent_increment140 141    def dedent(self) -> None:142        """Decreases the indentation."""143        self.current_indent -= self.indent_increment144 145    def write_usage(146        self, prog: str, args: str = "", prefix: t.Optional[str] = None147    ) -> 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: t.Sequence[t.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) -> t.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) -> t.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: t.Sequence[str]) -> t.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