Exemplo n.º 1
0
def list_item_close(text: str, tokens: Sequence[Token], idx: int,
                    options: Mapping[str, Any], env: dict) -> str:
    """Return one list item as string.

    The string contains MARKERS.LIST_ITEMs, MARKERS.LIST_INDENTs and
    MARKERS.LIST_INDENT_FIRST_LINEs which have to be replaced in later
    processing.
    """
    text = removesuffix(text, MARKERS.BLOCK_SEPARATOR)
    if is_tight_list_item(tokens, idx):
        text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n")
    else:
        text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n\n")

    lines = text.splitlines()
    if not lines:
        return MARKERS.LIST_ITEM + MARKERS.BLOCK_SEPARATOR
    indented = []
    for i, line in enumerate(lines):
        if i == 0:
            indented.append(MARKERS.LIST_ITEM +
                            MARKERS.LIST_INDENT_FIRST_LINE +
                            line if line else MARKERS.LIST_ITEM)
        else:
            indented.append(MARKERS.LIST_INDENT + line if line else line)
    tabbed_str = "\n".join(indented) + MARKERS.BLOCK_SEPARATOR
    return tabbed_str
Exemplo n.º 2
0
def blockquote_close(text: str, tokens: Sequence[Token], idx: int,
                     options: Mapping[str, Any], env: dict) -> str:
    text = removesuffix(text, MARKERS.BLOCK_SEPARATOR)
    text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n\n")
    lines = text.splitlines()
    if not lines:
        return ">" + MARKERS.BLOCK_SEPARATOR
    quoted_lines = (f"> {line}" if line else ">" for line in lines)
    quoted_str = "\n".join(quoted_lines)
    return quoted_str + MARKERS.BLOCK_SEPARATOR
Exemplo n.º 3
0
def ordered_list_close(text: str, tokens: Sequence[Token], idx: int,
                       options: Mapping[str, Any], env: dict) -> str:
    marker_type = get_list_marker_type(tokens, idx)
    first_line_indent = " "

    text = removesuffix(text, MARKERS.BLOCK_SEPARATOR)
    if is_tight_list(tokens, idx):
        text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n")
    else:
        text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n\n")

    opening_token = find_opening_token(tokens, idx)
    starting_number = opening_token.attrGet("start")
    if starting_number is None:
        starting_number = 1

    if options.get("mdformat", {}).get(CONSECUTIVE_KEY):
        # Replace MARKERS.LIST_ITEM with consecutive numbering,
        # padded with zeros to make all markers of even length.
        # E.g.
        #   002. This is the first list item
        #   003. Second item
        #   ...
        #   112. Last item
        pad = len(str(text.count(MARKERS.LIST_ITEM) + starting_number - 1))
        indentation = " " * (pad + len(f"{marker_type}{first_line_indent}"))
        while MARKERS.LIST_ITEM in text:
            number = str(starting_number).rjust(pad, "0")
            text = text.replace(MARKERS.LIST_ITEM, f"{number}{marker_type}", 1)
            starting_number += 1
    else:
        # Replace first MARKERS.LIST_ITEM with the starting number of the list.
        # Replace following MARKERS.LIST_ITEMs with number one prefixed by zeros
        # to make the marker of even length with the first one.
        # E.g.
        #   5321. This is the first list item
        #   0001. Second item
        #   0001. Third item
        first_item_marker = f"{starting_number}{marker_type}"
        other_item_marker = "0" * (len(str(starting_number)) -
                                   1) + "1" + marker_type
        indentation = " " * len(first_item_marker + first_line_indent)
        text = text.replace(MARKERS.LIST_ITEM, first_item_marker, 1)
        text = text.replace(MARKERS.LIST_ITEM, other_item_marker)

    text = text.replace(MARKERS.LIST_INDENT_FIRST_LINE, first_line_indent)
    text = text.replace(MARKERS.LIST_INDENT, indentation)

    return text + MARKERS.BLOCK_SEPARATOR
Exemplo n.º 4
0
def bullet_list_close(text: str, tokens: Sequence[Token], idx: int,
                      options: Mapping[str, Any], env: dict) -> str:
    text = removesuffix(text, MARKERS.BLOCK_SEPARATOR)
    if is_tight_list(tokens, idx):
        text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n")
    else:
        text = text.replace(MARKERS.BLOCK_SEPARATOR, "\n\n")

    marker_type = get_list_marker_type(tokens, idx)
    first_line_indent = " "
    indent = " " * len(marker_type + first_line_indent)
    text = text.replace(MARKERS.LIST_ITEM, marker_type)
    text = text.replace(MARKERS.LIST_INDENT_FIRST_LINE, first_line_indent)
    text = text.replace(MARKERS.LIST_INDENT, indent)
    return text + MARKERS.BLOCK_SEPARATOR
Exemplo n.º 5
0
    def render(  # noqa: C901
        self,
        tokens: Sequence[Token],
        options: Mapping[str, Any],
        env: dict,
        *,
        start: int = 0,
        stop: Optional[int] = None,
        finalize: bool = True,
        _recursion_level: int = 0,
    ) -> str:
        """Takes token stream and generates Markdown.

        Args:
            tokens: A list of block tokens to render
            options: Params of parser instance
            env: Additional data from parsed input
            start: Start rendering from this index
            stop: Stop rendering before this index
            finalize: replace markers and write references
        """
        assert _recursion_level in {
            0,
            1,
        }, "There should be no more than one level of recursion in tokens"
        text_stack = [""]

        i = start - 1
        if stop is None:
            stop = len(tokens)
        while (i + 1) < stop:
            i += 1
            token = tokens[i]

            result = None

            # first check plugins
            for plugin in options.get("parser_extension", []):
                output = plugin.render_token(self, tokens, i, options, env)
                if output is not None:
                    result, i = output
                    break

            if result is not None:
                # if a plugin has handled the token,
                # we assume that it does not need to open or close a container
                text_stack[-1] = text_stack[-1] + result
                continue

            if token.type == "inline":
                # inline tokens require nested rendering
                result = self.render(
                    token.children,
                    options,
                    env,
                    finalize=finalize,
                    _recursion_level=_recursion_level + 1,
                )
            else:
                # otherwise use a built-in renderer
                tkn_renderer = getattr(_token_renderers, token.type,
                                       _token_renderers.default)
                result = tkn_renderer(tokens, i, options, env)

            # If the token opens a new container block, create a new item for
            # it in the text stack.
            if token.nesting == 1:
                text_stack.append(result)
            # If the token doesn't change nesting, write in the immediate container
            # block's stack item.
            elif token.nesting == 0:
                text_stack[-1] = text_stack[-1] + result
            # If the token ends a container block, pop the block's stack item,
            # format all markdown of that block, and append formatted markdown
            # to the block's container's stack item.
            else:  # token.nesting == -1
                container_result = text_stack.pop() + result
                container_renderer = getattr(_container_renderers, token.type,
                                             _container_renderers.default)
                container_result = container_renderer(container_result, tokens,
                                                      i, options, env)
                text_stack[-1] = text_stack[-1] + container_result

        rendered_content = text_stack.pop()
        assert not text_stack, "Text stack should be empty before returning"

        if finalize and not _recursion_level:
            rendered_content = removesuffix(rendered_content,
                                            MARKERS.BLOCK_SEPARATOR)
            rendered_content = rendered_content.replace(
                MARKERS.BLOCK_SEPARATOR, "\n\n")

            if env.get("used_refs"):
                rendered_content += "\n\n"
                rendered_content += self._write_references(env)

            rendered_content += "\n"

        return rendered_content