'''
# py2markdown

Py2markdown converts a python file to a markdown file where all top level
level `""" comments """` are rendered as markdown, and everything else
is rendered as code blocks.

The `README.md` for this repo was generated by running:

    $ py2markdown py2markdown/__init__.py > README.md

To install:

1. Git clone this repository
2. Run `pip3 install .`

Here's the source code!

'''


import ast
import parser
import re
from pathlib import Path


def process_source(text):
    """ Convert and print some text """
    # Get the start/end line of each comment
    comment_lines = _get_comment_lines(text)

    lines = text.split("\n")

    # Add a 'comment' which starts on the last line and has no end
    comment_lines.append((len(lines), None))
    last = 0
    output = []

    # Render alternating code and comment blocks
    for (firstline, lastline) in comment_lines:
        code = "\n".join(lines[last:firstline])
        if code.strip():
            output.append(f"```py\n{code.strip()}\n```")
        if lastline:
            output.append(_extract_comment("\n".join(lines[firstline:lastline])))
        last = lastline
    return "\n".join(output)


def _extract_comment(comment):
    """ Extract the content of a comment from a quoted comment string """
    return re.match(
        r"^\s*(?:(?:\"(?:\"\")?)|(?:\'(?:\'\')?))(.*?)(?:(?:\"(?:\"\")?)|(?:\'(?:\'\')?))\s*$", comment, flags=re.DOTALL
    ).group(1)


"""
Py2markdown uses the _AST_ module to parse the file and get the start and end line
numbers for all top level string expressions, making it fairly robust to odd comments
such as lines ending with \"""
"""


def _get_comment_lines(text):
    """ Get the start and end line numbers of all top-level string nodes """
    st = ast.parse(text, "source.py")
    comment_lines = []

    # Loop over top-level nodes
    for node in st.body:
        # Find Expression -> String nodes
        if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str):
            comment = node.value.s
            # Add the first/last line of the comment to the results list
            lastline = node.lineno
            firstline = lastline - len(comment.split("\n"))
            comment_lines.append((firstline, lastline))

    return comment_lines

