|
4 | 4 | import pathlib
|
5 | 5 | import sys
|
6 | 6 |
|
| 7 | +from collections import deque |
| 8 | + |
7 | 9 | PIPE = "│"
|
8 | 10 | ELBOW = "└──"
|
9 | 11 | TEE = "├──"
|
10 | 12 | PIPE_PREFIX = "│ "
|
| 13 | +SPACE_PREFIX = " " |
11 | 14 |
|
12 | 15 |
|
13 | 16 | class DirectoryTree:
|
14 |
| - def __init__(self, root_dir, output_file=sys.stdout, dir_only=False): |
| 17 | + def __init__(self, root_dir, dir_only=False, output_file=sys.stdout): |
15 | 18 | self._output_file = output_file
|
16 | 19 | self._generator = _TreeGenerator(root_dir, dir_only)
|
17 | 20 |
|
18 | 21 | def generate(self):
|
19 |
| - tree = self._generator.generate_tree() |
| 22 | + tree = self._generator.build_tree() |
20 | 23 | if self._output_file != sys.stdout:
|
| 24 | + # Wrap the tree in a markdown code block |
21 | 25 | tree.appendleft("```")
|
22 | 26 | tree.append("```")
|
23 | 27 | self._output_file = open(
|
24 | 28 | self._output_file, mode="w", encoding="UTF-8"
|
25 | 29 | )
|
26 | 30 | with self._output_file as stream:
|
27 |
| - for item in tree: |
28 |
| - print(item, file=stream) |
| 31 | + for entry in tree: |
| 32 | + print(entry, file=stream) |
29 | 33 |
|
30 | 34 |
|
31 | 35 | class _TreeGenerator:
|
32 |
| - def __init__(self, root, dir_only=False): |
33 |
| - self._root = pathlib.Path(root) |
34 |
| - self.dir_only = dir_only |
35 |
| - self._tree = [] |
| 36 | + def __init__(self, root_dir, dir_only=False): |
| 37 | + self._root_dir = pathlib.Path(root_dir) |
| 38 | + self._dir_only = dir_only |
| 39 | + self._tree = deque() |
36 | 40 |
|
37 |
| - def generate_tree(self): |
| 41 | + def build_tree(self): |
38 | 42 | self._tree_head()
|
39 |
| - self._tree_tail(self._root) |
| 43 | + self._tree_tail(self._root_dir) |
40 | 44 | return self._tree
|
41 | 45 |
|
42 | 46 | def _tree_head(self):
|
43 |
| - self._tree.append(f"{self._root}{os.sep}") |
| 47 | + self._tree.append(f"{self._root_dir}{os.sep}") |
44 | 48 | self._tree.append(PIPE)
|
45 | 49 |
|
46 |
| - def _tree_tail(self, dir, prefix=""): |
47 |
| - entries = self._prepare_entries(dir) |
| 50 | + def _tree_tail(self, directory, prefix=""): |
| 51 | + entries = self._prepare_entries(directory) |
48 | 52 | entries_count = len(entries)
|
49 | 53 | for index, entry in enumerate(entries):
|
50 | 54 | connector = ELBOW if index == entries_count - 1 else TEE
|
51 |
| - path = dir.joinpath(entry) |
52 |
| - if path.is_dir(): |
| 55 | + if entry.is_dir(): |
53 | 56 | self._add_directory(
|
54 | 57 | entry, index, entries_count, prefix, connector
|
55 | 58 | )
|
56 | 59 | else:
|
57 |
| - self._add_file(prefix, connector, path) |
| 60 | + self._add_file(entry, prefix, connector) |
58 | 61 |
|
59 |
| - def _prepare_entries(self, dir): |
60 |
| - entries = dir.iterdir() |
61 |
| - if self.dir_only: |
62 |
| - entries = [e for e in entries if dir.joinpath(e).is_dir()] |
| 62 | + def _prepare_entries(self, directory): |
| 63 | + entries = directory.iterdir() |
| 64 | + if self._dir_only: |
| 65 | + entries = [entry for entry in entries if entry.is_dir()] |
63 | 66 | return entries
|
64 |
| - entries = sorted(entries, key=lambda e: dir.joinpath(e).is_file()) |
| 67 | + entries = sorted(entries, key=lambda entry: entry.is_file()) |
65 | 68 | return entries
|
66 | 69 |
|
67 |
| - def _add_directory(self, entry, index, entries_count, prefix, connector): |
68 |
| - self._tree.append(f"{prefix}{connector}{entry.name}{os.sep}") |
| 70 | + def _add_directory( |
| 71 | + self, directory, index, entries_count, prefix, connector |
| 72 | + ): |
| 73 | + self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}") |
69 | 74 | if index != entries_count - 1:
|
70 | 75 | prefix += PIPE_PREFIX
|
| 76 | + else: |
| 77 | + prefix += SPACE_PREFIX |
71 | 78 | self._tree_tail(
|
72 |
| - dir=entry, |
| 79 | + directory=directory, |
73 | 80 | prefix=prefix,
|
74 | 81 | )
|
75 |
| - self._tree.append(prefix) |
| 82 | + self._tree.append(prefix.rstrip()) |
76 | 83 |
|
77 |
| - def _add_file(self, prefix, connector, path): |
78 |
| - self._tree.append(f"{prefix}{connector} {path.name}") |
| 84 | + def _add_file(self, file, prefix, connector): |
| 85 | + self._tree.append(f"{prefix}{connector} {file.name}") |
0 commit comments