diff --git a/.gitignore b/.gitignore index a588dbd..3f5f01c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__/ target/ dist/ build/ +uml-viewer-log.txt diff --git a/examples/crapper.edn b/examples/crapper.edn new file mode 100644 index 0000000..9c44219 --- /dev/null +++ b/examples/crapper.edn @@ -0,0 +1,314 @@ +;; Generated by clj -M:ir from the policy file. Do not edit. +{:hierarchical true, + :title "Crapper", + :prefix "crapper", + :edge-kinds {}, + :omit-edges [], + :levels + [[:model :discover :crapper] + [:crap :coverage :metrics :report :languages] + [:analyze :runners] + [:cli] + [:__main__]], + :policy-file "examples/crapper.policy.edn", + :order + [:model + :discover + :crapper + :crap + :coverage + :metrics + :report + :languages + :analyze + :runners + :cli + :__main__], + :omit [], + :edges + [{:from :__main__, :to :cli, :kind :dependency} + {:from :languages.python, :to :languages.treesitter, :kind :dependency} + {:from :languages, :to :languages.typescript, :kind :dependency} + {:from :languages.rust, :to :languages.treesitter, :kind :dependency} + {:from :languages.java, :to :languages.treesitter, :kind :dependency} + {:from :crap, :to :model, :kind :implements} + {:from :languages.rust, :to :pathlib, :kind :dependency} + {:from :analyze, :to :pathlib, :kind :dependency} + {:from :languages.java, :to :re, :kind :dependency} + {:from :runners, :to :shlex, :kind :dependency} + {:from :coverage, :to :model, :kind :dependency} + {:from :languages.typescript, :to :languages.treesitter, :kind :dependency} + {:from :runners, :to :json, :kind :dependency} + {:from :languages.golang, :to :languages.language, :kind :inheritance} + {:from :cli, :to :subprocess, :kind :dependency} + {:from :languages.treesitter, :to :tree_sitter_language_pack, :kind :dependency} + {:from :languages.typescript, :to :model, :kind :dependency} + {:from :languages.golang, :to :pathlib, :kind :dependency} + {:from :coverage, :to :urllib, :kind :dependency} + {:from :analyze, :to :languages, :kind :dependency} + {:from :languages, :to :languages.clojure, :kind :dependency} + {:from :languages.python, :to :languages.language, :kind :inheritance} + {:from :report, :to :model, :kind :dependency} + {:from :languages.golang, :to :languages.treesitter, :kind :dependency} + {:from :languages, :to :languages.java, :kind :dependency} + {:from :coverage, :to :xml, :kind :dependency} + {:from :languages, :to :languages.python, :kind :dependency} + {:from :cli, :to :sys, :kind :dependency} + {:from :cli, :to :pathlib, :kind :dependency} + {:from :analyze, :to :model, :kind :dependency} + {:from :languages.clojure, :to :re, :kind :dependency} + {:from :languages.treesitter, :to :functools, :kind :dependency} + {:from :discover, :to :os, :kind :dependency} + {:from :analyze, :to :discover, :kind :dependency} + {:from :coverage, :to :pathlib, :kind :dependency} + {:from :languages.language, :to :model, :kind :dependency} + {:from :languages, :to :model, :kind :dependency} + {:from :analyze, :to :crap, :kind :dependency} + {:from :runners, :to :subprocess, :kind :dependency} + {:from :cli, :to :coverage, :kind :dependency} + {:from :languages, :to :languages.rust, :kind :dependency} + {:from :languages.typescript, :to :languages.language, :kind :inheritance} + {:from :cli, :to :report, :kind :dependency} + {:from :metrics, :to :pathlib, :kind :dependency} + {:from :runners, :to :shutil, :kind :dependency} + {:from :model, :to :dataclasses, :kind :dependency} + {:from :languages.rust, :to :languages.language, :kind :inheritance} + {:from :languages.java, :to :model, :kind :dependency} + {:from :languages.rust, :to :re, :kind :dependency} + {:from :cli, :to :dataclasses, :kind :dependency} + {:from :discover, :to :pathlib, :kind :dependency} + {:from :coverage, :to :dataclasses, :kind :dependency} + {:from :languages, :to :languages.language, :kind :dependency} + {:from :cli, :to :runners, :kind :dependency} + {:from :languages.clojure, :to :languages.language, :kind :inheritance} + {:from :languages.rust, :to :model, :kind :dependency} + {:from :runners, :to :sys, :kind :dependency} + {:from :languages.clojure, :to :model, :kind :dependency} + {:from :languages.golang, :to :model, :kind :dependency} + {:from :languages.java, :to :languages.language, :kind :inheritance} + {:from :languages.python, :to :model, :kind :dependency} + {:from :analyze, :to :coverage, :kind :dependency} + {:from :cli, :to :metrics, :kind :dependency} + {:from :runners, :to :discover, :kind :dependency} + {:from :languages, :to :languages.golang, :kind :dependency} + {:from :cli, :to :analyze, :kind :dependency} + {:from :coverage, :to :re, :kind :dependency} + {:from :metrics, :to :model, :kind :dependency} + {:from :cli, :to :discover, :kind :dependency} + {:from :runners, :to :pathlib, :kind :dependency}], + :classes + [{:ns "crapper", + :name "Crapper", + :file "src/crapper/__init__.py", + :level 0, + :lang :python, + :id :crapper} + {:ns "crapper.__main__", + :name "__main__", + :file "src/crapper/__main__.py", + :level 4, + :lang :python, + :id :__main__} + {:ns "crapper.analyze", + :name "Analyze", + :file "src/crapper/analyze.py", + :ops [{:name "analyze_files", :text "analyze_files"}], + :level 2, + :lang :python, + :id :analyze} + {:ns "crapper.cli", + :name "Cli", + :file "src/crapper/cli.py", + :ops + [{:name "Options", :text "Options"} + {:name "parse_args", :text "parse_args"} + {:name "select_files", :text "select_files"} + {:name "run", :text "run"} + {:name "main", :text "main"}], + :level 3, + :lang :python, + :id :cli} + {:ns "crapper.coverage", + :name "Coverage", + :file "src/crapper/coverage.py", + :ops + [{:name "normalize_path", :text "normalize_path"} + {:name "suffix_match", :text "suffix_match"} + {:name "coverage_percent", :text "coverage_percent"} + {:name "percent_for_range", :text "percent_for_range"} + {:name "parse_form_coverage", :text "parse_form_coverage"} + {:name "parse_lcov", :text "parse_lcov"} + {:name "parse_go_profile", :text "parse_go_profile"} + {:name "go_percent", :text "go_percent"} + {:name "JacocoMethod", :text "JacocoMethod"} + {:name "parse_jacoco_index", :text "parse_jacoco_index"} + {:name "CoverageBundle", :text "CoverageBundle"} + {:name "load_bundle", :text "load_bundle"}], + :level 1, + :lang :python, + :id :coverage} + {:ns "crapper.crap", + :name "Crap", + :file "src/crapper/crap.py", + :ops + [{:name "crap_score", :text "crap_score"} + {:name "round_metric", :text "round_metric"} + {:name "make_entry", :text "make_entry"} + {:name "sort_entries", :text "sort_entries"}], + :level 1, + :lang :python, + :id :crap} + {:ns "crapper.discover", + :name "Discover", + :file "src/crapper/discover.py", + :ops + [{:name "language_of", :text "language_of"} + {:name "is_test_file", :text "is_test_file"} + {:name "iter_source_files", :text "iter_source_files"}], + :level 0, + :lang :python, + :id :discover} + {:ns "crapper.languages", + :name "Languages", + :file "src/crapper/languages/__init__.py", + :ops [{:name "functions_in_file", :text "functions_in_file"}], + :level 1, + :lang :python, + :id :languages} + {:ns "crapper.languages.clojure", + :name "Clojure", + :file "src/crapper/languages/clojure.py", + :ops + [{:name "without_strings_and_comments", :text "without_strings_and_comments"} + {:name "cyclomatic_complexity", :text "cyclomatic_complexity"} + {:name "extract_functions", :text "extract_functions"} + {:name "declared_namespace", :text "declared_namespace"} + {:name "namespace_from_path", :text "namespace_from_path"} + {:name "functions_in_source", :text "functions_in_source"} + {:name "Clojure", :text "Clojure"}], + :level 1, + :lang :python, + :id :languages.clojure} + {:ns "crapper.languages.golang", + :name "Golang", + :file "src/crapper/languages/golang.py", + :ops + [{:name "package_namespace", :text "package_namespace"} + {:name "functions_in_source", :text "functions_in_source"} + {:name "Go", :text "Go"}], + :level 1, + :lang :python, + :id :languages.golang} + {:ns "crapper.languages.java", + :name "Java", + :file "src/crapper/languages/java.py", + :ops + [{:name "functions_in_source", :text "functions_in_source"} + {:name "Java", :text "Java"}], + :level 1, + :lang :python, + :id :languages.java} + {:ns "crapper.languages.language", + :name "Language", + :file "src/crapper/languages/language.py", + :ops [{:name "Language", :text "Language"} {:name "Unknown", :text "Unknown"}], + :level 1, + :lang :python, + :id :languages.language} + {:ns "crapper.languages.python", + :name "Python", + :file "src/crapper/languages/python.py", + :ops + [{:name "functions_in_source", :text "functions_in_source"} + {:name "Python", :text "Python"}], + :level 1, + :lang :python, + :id :languages.python} + {:ns "crapper.languages.rust", + :name "Rust", + :file "src/crapper/languages/rust.py", + :ops + [{:name "functions_in_source", :text "functions_in_source"} + {:name "Rust", :text "Rust"}], + :level 1, + :lang :python, + :id :languages.rust} + {:ns "crapper.languages.treesitter", + :name "Treesitter", + :file "src/crapper/languages/treesitter.py", + :ops + [{:name "parser_for", :text "parser_for"} + {:name "parse", :text "parse"} + {:name "node_text", :text "node_text"} + {:name "start_line", :text "start_line"} + {:name "end_line", :text "end_line"} + {:name "child_of_type", :text "child_of_type"} + {:name "descendants", :text "descendants"} + {:name "complexity", :text "complexity"} + {:name "binary_logic", :text "binary_logic"}], + :level 1, + :lang :python, + :id :languages.treesitter} + {:ns "crapper.languages.typescript", + :name "Typescript", + :file "src/crapper/languages/typescript.py", + :ops + [{:name "functions_in_source", :text "functions_in_source"} + {:name "TypeScript", :text "TypeScript"}], + :level 1, + :lang :python, + :id :languages.typescript} + {:ns "crapper.metrics", + :name "Metrics", + :file "src/crapper/metrics.py", + :ops + [{:name "metrics_path", :text "metrics_path"} + {:name "render_edn", :text "render_edn"} + {:name "write_metrics", :text "write_metrics"}], + :level 1, + :lang :python, + :id :metrics} + {:ns "crapper.model", + :name "Model", + :file "src/crapper/model.py", + :ops [{:name "Function", :text "Function"} {:name "Entry", :text "Entry"}], + :level 0, + :lang :python, + :id :model} + {:ns "crapper.report", + :name "Report", + :file "src/crapper/report.py", + :ops [{:name "format_report", :text "format_report"}], + :level 1, + :lang :python, + :id :report} + {:ns "crapper.runners", + :name "Runners", + :file "src/crapper/runners.py", + :ops + [{:name "run_shell", :text "run_shell"} + {:name "typescript_packages", :text "typescript_packages"} + {:name "typescript_command", :text "typescript_command"} + {:name "rust_modules", :text "rust_modules"} + {:name "python_roots", :text "python_roots"} + {:name "uses_pytest", :text "uses_pytest"} + {:name "python_sources", :text "python_sources"} + {:name "python_coverage_commands", :text "python_coverage_commands"} + {:name "rust_coverage_command", :text "rust_coverage_command"} + {:name "run_coverage", :text "run_coverage"}], + :level 2, + :lang :python, + :id :runners} + {:id :dataclasses, :name "dataclasses", :foreign true} + {:id :functools, :name "functools", :foreign true} + {:id :json, :name "json", :foreign true} + {:id :os, :name "os", :foreign true} + {:id :pathlib, :name "pathlib", :foreign true} + {:id :re, :name "re", :foreign true} + {:id :shlex, :name "shlex", :foreign true} + {:id :shutil, :name "shutil", :foreign true} + {:id :subprocess, :name "subprocess", :foreign true} + {:id :sys, :name "sys", :foreign true} + {:id :tree_sitter_language_pack, :name "tree_sitter_language_pack", :foreign true} + {:id :urllib, :name "urllib", :foreign true} + {:id :xml, :name "xml", :foreign true}]} diff --git a/examples/crapper.policy.edn b/examples/crapper.policy.edn new file mode 100644 index 0000000..20c6ed6 --- /dev/null +++ b/examples/crapper.policy.edn @@ -0,0 +1,18 @@ +{:title "Crapper" + :prefix "crapper" + :lang :python + :src "src" + :out "examples/crapper.edn" + :hierarchical true + :foreign [dataclasses functools json os pathlib re shlex shutil subprocess sys + tree_sitter_language_pack urllib xml] + :order [model discover crapper + crap coverage metrics report languages + analyze runners + cli + __main__] + :levels [[model discover crapper] + [crap coverage metrics report languages] + [analyze runners] + [cli] + [__main__]]} diff --git a/uml b/uml new file mode 100755 index 0000000..391e8d9 --- /dev/null +++ b/uml @@ -0,0 +1,61 @@ +#!/bin/zsh +# Loader for this project. Points at the local uml-viewer checkout so the +# Python scanner is on the classpath. +set -euo pipefail +ROOT="$(cd "$(dirname "$0")" && pwd)" +cd "$ROOT" + +DEPS='{:deps {uml-viewer/uml-viewer {:local/root "/Users/unclebob/projects/uml-viewer"} quil/quil {:mvn/version "4.3.1563"}}}' + +edn_path() { + local name base f + name="$(basename "$ROOT")" + if [[ -f "examples/${name}.edn" ]]; then + echo "examples/${name}.edn" + return + fi + if [[ -d examples ]]; then + for f in examples/*.edn(N); do + [[ -f "$f" ]] || continue + base="${f:t}" + [[ "$base" == *.policy.edn ]] && continue + echo "$f" + return + done + fi +} + +LOG="uml-viewer-log.txt" + +launch_viewer() { + { + printf '----- %s starting uml-viewer' "$(date '+%Y-%m-%d %H:%M:%S')" + [[ $# -gt 0 ]] && printf ' %s' "$@" + printf '\n' + } >> "$LOG" + # `clj` is rlwrap on Homebrew; a background rlwrap stops (SIGTTOU) and + # never starts the JVM. `clojure` is the same CLI without the tty wrapper. + nohup clojure -Sdeps "$DEPS" -M -m uml-viewer.main.uml-viewer "$@" >> "$LOG" 2>&1 &2 + echo " (no args) fresh window; companion writes the diagram" >&2 + echo " --restart new JVM, same companion; restores last view" >&2 + echo " The viewer detaches; exceptions go to $LOG." >&2 + exit 0 +fi + +if [[ "${1:-}" == "--restart" ]]; then + edn="$(edn_path)" + if [[ -n "$edn" ]]; then + launch_viewer --restart "$edn" + else + launch_viewer --restart + fi + exit 0 +fi + +launch_viewer "$@"