"""A collection of string formatting helpers.""" import functools import textwrap from typing import Final SIG_END_MARKER: Final = "--" def docstring_for_c_string(docstring: str) -> str: lines = [] # Turn docstring into a properly quoted C string. for line in docstring.split("\n"): lines.append('"') lines.append(_quoted_for_c_string(line)) lines.append('\\n"\n') if lines[-2] == SIG_END_MARKER: # If we only have a signature, add the blank line that the # __text_signature__ getter expects to be there. lines.append('"\\n"') else: lines.pop() lines.append('"') return "".join(lines) def _quoted_for_c_string(text: str) -> str: """Helper for docstring_for_c_string().""" for old, new in ( ("\\", "\\\\"), # must be first! ('"', '\\"'), ("'", "\\'"), ): text = text.replace(old, new) return text def c_repr(text: str) -> str: return '"' + text + '"' def wrapped_c_string_literal( text: str, *, width: int = 72, suffix: str = "", initial_indent: int = 0, subsequent_indent: int = 4 ) -> str: wrapped = textwrap.wrap( text, width=width, replace_whitespace=False, drop_whitespace=False, break_on_hyphens=False, ) separator = c_repr(suffix + "\n" + subsequent_indent * " ") return initial_indent * " " + c_repr(separator.join(wrapped)) def _add_prefix_and_suffix(text: str, *, prefix: str = "", suffix: str = "") -> str: """Return 'text' with 'prefix' prepended and 'suffix' appended to all lines. If the last line is empty, it remains unchanged. If text is blank, return text unchanged. (textwrap.indent only adds to non-blank lines.) """ *split, last = text.split("\n") lines = [prefix + line + suffix + "\n" for line in split] if last: lines.append(prefix + last + suffix) return "".join(lines) def indent_all_lines(text: str, prefix: str) -> str: return _add_prefix_and_suffix(text, prefix=prefix) def suffix_all_lines(text: str, suffix: str) -> str: return _add_prefix_and_suffix(text, suffix=suffix) def pprint_words(items: list[str]) -> str: if len(items) <= 2: return " and ".join(items) return ", ".join(items[:-1]) + " and " + items[-1] def _strip_leading_and_trailing_blank_lines(text: str) -> str: lines = text.rstrip().split("\n") while lines: line = lines[0] if line.strip(): break del lines[0] return "\n".join(lines) @functools.lru_cache() def normalize_snippet(text: str, *, indent: int = 0) -> str: """ Reformats 'text': * removes leading and trailing blank lines * ensures that it does not end with a newline * dedents so the first nonwhite character on any line is at column "indent" """ text = _strip_leading_and_trailing_blank_lines(text) text = textwrap.dedent(text) if indent: text = textwrap.indent(text, " " * indent) return text def format_escape(text: str) -> str: # double up curly-braces, this string will be used # as part of a format_map() template later text = text.replace("{", "{{") text = text.replace("}", "}}") return text def wrap_declarations(text: str, length: int = 78) -> str: """ A simple-minded text wrapper for C function declarations. It views a declaration line as looking like this: xxxxxxxx(xxxxxxxxx,xxxxxxxxx) If called with length=30, it would wrap that line into xxxxxxxx(xxxxxxxxx, xxxxxxxxx) (If the declaration has zero or one parameters, this function won't wrap it.) If this doesn't work properly, it's probably better to start from scratch with a more sophisticated algorithm, rather than try and improve/debug this dumb little function. """ lines = [] for line in text.split("\n"): prefix, _, after_l_paren = line.partition("(") if not after_l_paren: lines.append(line) continue in_paren, _, after_r_paren = after_l_paren.partition(")") if not _: lines.append(line) continue if "," not in in_paren: lines.append(line) continue parameters = [x.strip() + ", " for x in in_paren.split(",")] prefix += "(" if len(prefix) < length: spaces = " " * len(prefix) else: spaces = " " * 4 while parameters: line = prefix first = True while parameters: if not first and (len(line) + len(parameters[0]) > length): break line += parameters.pop(0) first = False if not parameters: line = line.rstrip(", ") + ")" + after_r_paren lines.append(line.rstrip()) prefix = spaces return "\n".join(lines)