Skip to content

Commit

Permalink
feat: Add tree formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed May 21, 2022
1 parent 74b9a95 commit 8096990
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 1 deletion.
74 changes: 74 additions & 0 deletions docs/usage/tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Tree

Markdown Exec provides a `tree` formatter that can be used
to render file-system trees easily:

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```tree
root1
file1
dir1
file
dir2
file1
file2
file2
file3
root2
file1
```
````

By default, the language used for syntax highlight is `bash`.
It means you can add comments with `#`:

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```tree
root1 # comment 1
file1
dir1
file
dir2
file1 # comment 2
file2 # comment 3
file2
file3
root2
file1
```
````

You can change the syntax highlight language with the `result` option:

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```tree result="javascript"
root1 // comment 1
file1
dir1
file
dir2
file1 // comment 2
file2 // comment 3
file2
file3
root2
file1
```
````

You can force an entry to be displayed as a directory instead of a regular file
by appending a trailing slash to the name:

````md exec="1" source="tabbed-left" tabs="Markdown|Rendered"
```tree
root1
dir1/
dir2/
dir3/
```
````

It is recommended to always append trailing slashes to directory anyway.

WARNING: **Limitation**
Spaces in file names are not supported when searching for a trailing slash.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ nav:
- usage/index.md
- Python: usage/python.md
- Shell: usage/shell.md
- Tree: usage/tree.md
- Gallery: gallery.md
# defer to gen-files + literate-nav
- Code Reference: reference/
Expand Down
4 changes: 3 additions & 1 deletion src/markdown_exec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from markdown_exec.formatters.pycon import _format_pycon # noqa: WPS450
from markdown_exec.formatters.python import _format_python # noqa: WPS450
from markdown_exec.formatters.sh import _format_sh # noqa: WPS450
from markdown_exec.formatters.tree import _format_tree # noqa: WPS450

__all__: list[str] = ["formatter", "validator"] # noqa: WPS410

Expand All @@ -33,6 +34,7 @@
"python": _format_python,
"pycon": _format_pycon,
"sh": _format_sh,
"tree": _format_tree,
}

# negative look behind: matches only if | (pipe) if not preceded by \ (backslash)
Expand All @@ -55,7 +57,7 @@ def validator(
Success or not.
"""
exec_value = _to_bool(inputs.pop("exec", "no"))
if not exec_value:
if language != "tree" and not exec_value:
return False
html_value = _to_bool(inputs.pop("html", "no"))
source_value = inputs.pop("source", "")
Expand Down
67 changes: 67 additions & 0 deletions src/markdown_exec/formatters/tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Formatter for file-system trees."""

from __future__ import annotations

from textwrap import dedent
from typing import Any

from markdown import Markdown

from markdown_exec.rendering import code_block, markdown


def _rec_build_tree(lines: list[str], parent: list, offset: int, base_indent: int):
while offset < len(lines):
line = lines[offset]
lstripped = line.lstrip()
indent = len(line) - len(lstripped)
if indent == base_indent:
parent.append((lstripped, []))
offset += 1
elif indent > base_indent:
offset = _rec_build_tree(lines, parent[-1][1], offset, indent)
else:
return offset
return offset


def _build_tree(code: str) -> list[tuple[str, list]]:
lines = dedent(code.strip()).split("\n")
root_layer: list[tuple[str, list]] = []
_rec_build_tree(lines, root_layer, 0, 0)
return root_layer


def _rec_format_tree(tree, root=True) -> list[str]: # noqa: WPS231
lines = []
n_items = len(tree)
for index, node in enumerate(tree):
last = index == n_items - 1
prefix = "" if root else f"{'└' if last else '├'}── " # noqa: WPS509
if node[1]:
lines.append(f"{prefix}📁 {node[0]}")
sublines = _rec_format_tree(node[1], root=False)
if root:
lines.extend(sublines)
else:
indent_char = " " if last else "│"
lines.extend([f"{indent_char} {line}" for line in sublines])
else:
name = node[0].split()[0]
icon = "📁" if name.endswith("/") else "📄"
lines.append(f"{prefix}{icon} {node[0]}")
return lines


def _format_tree( # noqa: WPS231
code: str,
md: Markdown,
html: bool,
source: str,
result: str,
tabs: tuple[str, str],
**options: Any,
) -> str:
markdown.setup(md)
output = "\n".join(_rec_format_tree(_build_tree(code)))
return markdown.convert(code_block(result or "bash", output))

0 comments on commit 8096990

Please sign in to comment.