{"action":"created","issue":{"url":"https://api.github.com/repos/elixir-lang/elixir/issues/4811","repository_url":"https://api.github.com/repos/elixir-lang/elixir","labels_url":"https://api.github.com/repos/elixir-lang/elixir/issues/4811/labels{/name}","comments_url":"https://api.github.com/repos/elixir-lang/elixir/issues/4811/comments","events_url":"https://api.github.com/repos/elixir-lang/elixir/issues/4811/events","html_url":"https://github.com/elixir-lang/elixir/pull/4811","id":159873991,"number":4811,"title":"mix xref graph","user":{"login":"antipax","id":56482,"avatar_url":"https://avatars.githubusercontent.com/u/56482?v=3","gravatar_id":"","url":"https://api.github.com/users/antipax","html_url":"https://github.com/antipax","followers_url":"https://api.github.com/users/antipax/followers","following_url":"https://api.github.com/users/antipax/following{/other_user}","gists_url":"https://api.github.com/users/antipax/gists{/gist_id}","starred_url":"https://api.github.com/users/antipax/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/antipax/subscriptions","organizations_url":"https://api.github.com/users/antipax/orgs","repos_url":"https://api.github.com/users/antipax/repos","events_url":"https://api.github.com/users/antipax/events{/privacy}","received_events_url":"https://api.github.com/users/antipax/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":14,"created_at":"2016-06-13T05:07:22Z","updated_at":"2016-06-15T15:34:46Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/elixir-lang/elixir/pulls/4811","html_url":"https://github.com/elixir-lang/elixir/pull/4811","diff_url":"https://github.com/elixir-lang/elixir/pull/4811.diff","patch_url":"https://github.com/elixir-lang/elixir/pull/4811.patch"},"body":"Implements #4810.\r\n\r\nMaybe it would be nice to also emit whether it's a runtime or compile time dep as edge_info in --format dot? I'm not sure it's worth the extra effort. Thoughts?\r\n\r\n/cc @josevalim @michalmuskala "},"comment":{"url":"https://api.github.com/repos/elixir-lang/elixir/issues/comments/226225835","html_url":"https://github.com/elixir-lang/elixir/pull/4811#issuecomment-226225835","issue_url":"https://api.github.com/repos/elixir-lang/elixir/issues/4811","id":226225835,"user":{"login":"josevalim","id":9582,"avatar_url":"https://avatars.githubusercontent.com/u/9582?v=3","gravatar_id":"","url":"https://api.github.com/users/josevalim","html_url":"https://github.com/josevalim","followers_url":"https://api.github.com/users/josevalim/followers","following_url":"https://api.github.com/users/josevalim/following{/other_user}","gists_url":"https://api.github.com/users/josevalim/gists{/gist_id}","starred_url":"https://api.github.com/users/josevalim/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josevalim/subscriptions","organizations_url":"https://api.github.com/users/josevalim/orgs","repos_url":"https://api.github.com/users/josevalim/repos","events_url":"https://api.github.com/users/josevalim/events{/privacy}","received_events_url":"https://api.github.com/users/josevalim/received_events","type":"User","site_admin":false},"created_at":"2016-06-15T15:34:46Z","updated_at":"2016-06-15T15:34:46Z","body":"@antipax after further thought, I think we should change both print_tree and write_dot_graph to accept a list of nodes instead of a single root node. Here is a diff I tried locally and it drastically improved the output for Phoenix, given we no longer have a root node connecting to everything else:\r\n\r\n```diff\r\ndiff --git a/lib/mix/lib/mix/tasks/xref.ex b/lib/mix/lib/mix/tasks/xref.ex\r\nindex b51208a..948d64d 100644\r\n--- a/lib/mix/lib/mix/tasks/xref.ex\r\n+++ b/lib/mix/lib/mix/tasks/xref.ex\r\n@@ -358,22 +358,21 @@ defmodule Mix.Tasks.Xref do\r\n   end\r\n \r\n   defp write_graph(file_references, excluded, opts) do\r\n-    app_label = \"#{Mix.Project.config[:app]} application\"\r\n     {root, file_references} =\r\n       case {opts[:source], opts[:sink]} do\r\n         {nil, nil} ->\r\n-          {{app_label, nil}, file_references}\r\n+          {Enum.map(file_references, &{elem(&1, 0), nil}) -- excluded, file_references}\r\n \r\n         {source, nil} ->\r\n           if file_references[source] do\r\n-            {{source, nil}, file_references}\r\n+            {[{source, nil}], file_references}\r\n           else\r\n             Mix.raise \"Source could not be found: #{source}\"\r\n           end\r\n \r\n         {nil, sink} ->\r\n           if file_references[sink] do\r\n-            {{sink, nil}, file_references |> invert_references()}\r\n+            {[{sink, nil}], file_references |> invert_references()}\r\n           else\r\n             Mix.raise \"Sink could not be found: #{sink}\"\r\n           end\r\n@@ -383,11 +382,7 @@ defmodule Mix.Tasks.Xref do\r\n       end\r\n \r\n     callback =\r\n-      fn\r\n-        {^app_label, nil} ->\r\n-          {{nil, nil}, Enum.map(file_references, &{elem(&1, 0), nil}) -- excluded}\r\n-\r\n-        {file, type} ->\r\n+      fn {file, type} ->\r\n           children = Map.get(file_references, file, [])\r\n           {{file, type}, children -- excluded}\r\n       end\r\ndiff --git a/lib/mix/lib/mix/utils.ex b/lib/mix/lib/mix/utils.ex\r\nindex ceb4a83..1216fc4 100644\r\n--- a/lib/mix/lib/mix/utils.ex\r\n+++ b/lib/mix/lib/mix/utils.ex\r\n@@ -150,15 +150,15 @@ defmodule Mix.Utils do\r\n   The callback will be invoked for each node and it\r\n   must return a `{printed, children}` tuple.\r\n   \"\"\"\r\n-  @spec print_tree(tree_node, (tree_node -> {tree_node, [tree_node]}), Keyword.t) :: :ok\r\n-  def print_tree(root, callback, opts \\\\ []) do\r\n+  @spec print_tree([tree_node], (tree_node -> {tree_node, [tree_node]}), Keyword.t) :: :ok\r\n+  def print_tree(nodes, callback, opts \\\\ []) do\r\n     pretty =\r\n       case Keyword.get(opts, :format) do\r\n         \"pretty\" -> true\r\n         \"plain\" -> false\r\n         _ -> elem(:os.type, 0) != :win32\r\n       end\r\n-    print_tree([root], [], nil, MapSet.new(), pretty, callback)\r\n+    print_tree(nodes, [], nil, MapSet.new(), pretty, callback)\r\n \r\n     :ok\r\n   end\r\n@@ -199,10 +199,9 @@ defmodule Mix.Utils do\r\n   The callback will be invoked for each node and it\r\n   must return a `{printed, children}` tuple.\r\n   \"\"\"\r\n-  @spec write_dot_graph!(Path.t, String.t, tree_node, (tree_node -> {tree_node, [tree_node]}), Keyword.t) :: :ok\r\n-  def write_dot_graph!(path, title, root, callback, _opts \\\\ []) do\r\n-    {{parent, _}, children} = callback.(root)\r\n-    {dot, _} = build_dot_graph(parent, children, MapSet.new(), callback)\r\n+  @spec write_dot_graph!(Path.t, String.t, [tree_node], (tree_node -> {tree_node, [tree_node]}), Keyword.t) :: :ok\r\n+  def write_dot_graph!(path, title, nodes, callback, _opts \\\\ []) do\r\n+    {dot, _} = build_dot_graph(make_ref(), nodes, MapSet.new(), callback)\r\n     File.write! path, \"digraph \\\"#{title}\\\" {\\n#{dot}}\\n\"\r\n   end\r\n \r\n@@ -215,7 +214,11 @@ defmodule Mix.Utils do\r\n       {\"\", seen}\r\n     else\r\n       seen = MapSet.put(seen, key)\r\n-      current = build_dot_current(parent, name, edge_info)\r\n+      current = if is_reference(parent) do\r\n+        \"\"\r\n+      else\r\n+        build_dot_current(parent, name, edge_info)\r\n+      end\r\n       {children, seen} = build_dot_graph(name, children, seen, callback)\r\n       {siblings, seen} = build_dot_graph(parent, nodes, seen, callback)\r\n       {current <> children <> siblings, seen}\r\n```\r\n\r\nFor the sink cases, we will just need to filter without inverting."}}
{"actor":{"display_login":"josevalim"}}