tomo/examples/tomodeps/tomodeps.tm

118 lines
3.9 KiB
Plaintext
Raw Permalink Normal View History

2024-09-06 21:03:17 -07:00
# Show a Tomo dependency graph
2025-04-01 12:15:28 -07:00
use patterns
2024-09-16 10:54:47 -07:00
_USAGE := "Usage: tomodeps <files...>"
2024-09-06 21:03:17 -07:00
_HELP := "
2024-09-16 10:54:47 -07:00
tomodeps: Show a file dependency graph for Tomo source files.
$_USAGE
2024-09-06 21:03:17 -07:00
"
2024-09-09 13:54:20 -07:00
enum Dependency(File(path:Path), Module(name:Text))
2025-04-06 13:36:40 -07:00
func _get_file_dependencies(file:Path -> |Dependency|)
2025-04-06 13:07:23 -07:00
if not file.is_file()
2025-04-06 10:40:17 -07:00
say("Could not read file: $file")
2025-04-06 13:34:23 -07:00
return ||
2024-09-06 21:03:17 -07:00
deps : @|Dependency|
2025-04-06 13:07:23 -07:00
if lines := file.by_line()
for line in lines
if line.matches_pattern($Pat/use {..}.tm/)
file_import := Path.from_text(line.replace_pattern($Pat/use {..}/, "@1")).resolved(relative_to=file)
deps.add(Dependency.File(file_import))
2025-04-06 13:07:23 -07:00
else if line.matches_pattern($Pat/use {id}/)
module_name := line.replace_pattern($Pat/use {..}/, "@1")
deps.add(Dependency.Module(module_name))
return deps[]
2024-09-09 13:54:20 -07:00
2025-04-06 13:36:40 -07:00
func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency=|Dependency|})
return if dependencies.has(dep)
2024-09-09 13:54:20 -07:00
2025-04-06 13:34:23 -07:00
dependencies[dep] = || # Placeholder
2024-09-09 13:54:20 -07:00
2025-04-06 13:07:23 -07:00
dep_deps := when dep is File(path)
2024-09-09 13:54:20 -07:00
_get_file_dependencies(path)
2025-04-06 13:07:23 -07:00
is Module(module)
2024-09-16 10:54:47 -07:00
dir := (~/.local/share/tomo/installed/$module)
module_deps : @|Dependency|
visited : @|Path|
2025-04-06 13:34:23 -07:00
unvisited := @|f.resolved() for f in dir.files() if f.extension() == ".tm"|
2025-04-06 13:07:23 -07:00
while unvisited.length > 0
2024-09-09 13:54:20 -07:00
file := unvisited.items[-1]
unvisited.remove(file)
visited.add(file)
2024-09-09 13:54:20 -07:00
2025-04-06 13:07:23 -07:00
for file_dep in _get_file_dependencies(file)
when file_dep is File(f)
if not visited.has(f)
unvisited.add(f)
2025-04-06 13:07:23 -07:00
is Module(m)
module_deps.add(file_dep)
module_deps[]
2024-09-09 13:54:20 -07:00
2024-11-30 12:50:54 -08:00
dependencies[dep] = dep_deps
2024-09-09 13:54:20 -07:00
2025-04-06 13:07:23 -07:00
for dep2 in dep_deps
2024-09-09 13:54:20 -07:00
_build_dependency_graph(dep2, dependencies)
2025-04-06 13:36:40 -07:00
func get_dependency_graph(dep:Dependency -> {Dependency=|Dependency|})
graph : @{Dependency=|Dependency|}
2024-10-27 17:49:03 -07:00
_build_dependency_graph(dep, graph)
2024-09-09 13:54:20 -07:00
return graph
2025-04-06 13:07:23 -07:00
func _printable_name(dep:Dependency -> Text)
when dep is Module(module)
return "\[34;1]$module\[]"
2025-04-06 13:07:23 -07:00
is File(f)
f = f.relative_to((.))
2025-04-06 13:07:23 -07:00
if f.exists()
return Text(f)
2025-04-06 13:07:23 -07:00
else
return "\[31;1]$f (not found)\[]"
2024-09-09 13:54:20 -07:00
2025-04-06 13:36:40 -07:00
func _draw_tree(dep:Dependency, dependencies:{Dependency=|Dependency|}, already_printed:@|Dependency|, prefix="", is_last=yes)
2025-04-06 13:07:23 -07:00
if already_printed.has(dep)
say(prefix ++ (if is_last then "└── " else "├── ") ++ _printable_name(dep) ++ " \[2](recursive)\[]")
return
2025-04-06 13:49:40 -07:00
say(prefix ++ (if is_last then "└── " else "├── ") ++ _printable_name(dep))
already_printed.add(dep)
2025-04-06 13:49:40 -07:00
child_prefix := prefix ++ (if is_last then " " else "│ ")
2025-04-06 13:34:23 -07:00
children := dependencies[dep] or ||
2025-04-06 13:07:23 -07:00
for i,child in children.items
is_child_last := (i == children.length)
2024-09-06 21:33:07 -07:00
_draw_tree(child, dependencies, already_printed, child_prefix, is_child_last)
2025-04-06 13:36:40 -07:00
func draw_tree(dep:Dependency, dependencies:{Dependency=|Dependency|})
printed : @|Dependency|
2024-09-09 13:54:20 -07:00
say(_printable_name(dep))
printed.add(dep)
2025-04-06 13:34:23 -07:00
deps := dependencies[dep] or ||
2025-04-06 13:07:23 -07:00
for i,child in deps.items
2024-09-09 13:54:20 -07:00
is_child_last := (i == deps.length)
2024-10-27 17:49:03 -07:00
_draw_tree(child, dependencies, already_printed=printed, is_last=is_child_last)
2025-04-06 13:07:23 -07:00
func main(files:[Text])
if files.length == 0
2024-09-15 13:42:42 -07:00
exit("
2024-09-06 21:35:57 -07:00
Please provide at least one file!
$_USAGE
")
2025-04-06 13:07:23 -07:00
for arg in files
if arg.matches_pattern($Pat/{..}.tm/)
path := Path.from_text(arg).resolved()
2024-09-09 14:04:46 -07:00
dependencies := get_dependency_graph(File(path))
draw_tree(File(path), dependencies)
2025-04-06 13:07:23 -07:00
else if arg.matches_pattern($Pat/{id}/)
2024-09-09 14:04:46 -07:00
dependencies := get_dependency_graph(Module(arg))
draw_tree(Module(arg), dependencies)
2025-04-06 13:07:23 -07:00
else
say("\[2]Skipping $arg\[]")
skip