defmodule Mix.Tasks.UsageRules.Docs do use Mix.Task @shortdoc "Shows documentation for Elixir modules and functions" @moduledoc """ Shows documentation for Elixir modules and functions in markdown format. ## Examples Show documentation for a module: $ mix usage_rules.docs Enum Show documentation for a function: $ mix usage_rules.docs Enum.map/2 Show documentation for a nested module: $ mix usage_rules.docs MyApp.User.Helpers """ require Logger @impl true def run([module = <>]) when first in ?A..?Z or first == ?: do loadpaths!() iex_colors = Application.get_env(:iex, :colors, []) mix_colors = Application.get_env(:mix, :colors, []) try do Application.put_env(:iex, :colors, mix_colors) quoted = try do module |> Code.string_to_quoted!() rescue _ -> nil end quoted |> IEx.Introspection.decompose(__ENV__) |> case do :error -> Mix.raise("Invalid expression: #{module}") _decomposition -> Code.eval_quoted( quote do require IEx.Helpers IEx.Helpers.h(unquote(quoted)) end ) end after Application.put_env(:iex, :colors, iex_colors) end end def run([]) do raise_bad_args!() end def run([term | _]) do Mix.raise("Invalid module or function: #{term}") end # Loadpaths without checks because tasks may be defined in deps. defp loadpaths! do args = [ "--no-elixir-version-check", "--no-deps-check", "--no-archives-check", "--no-listeners" ] Mix.Task.run("loadpaths", args) Mix.Task.reenable("loadpaths") Mix.Task.reenable("deps.loadpaths") end @spec raise_bad_args!() :: no_return() defp raise_bad_args! do Mix.raise(""" Must provide a module or function. For example: $ mix usage_rules.docs Enum $ mix usage_rules.docs Enum.map/2 """) end end