Current section

42 Versions

Jump to

Compare versions

6 files changed
+170 additions
-8 deletions
  @@ -5,6 +5,15 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
5 5
6 6 <!-- changelog -->
7 7
8 + ## v0.1.20 (2025-07-17)
9 +
10 +
11 +
12 +
13 + ### Improvements:
14 +
15 + * add `mix usage_rules.docs` by Zach Daniel
16 +
8 17 ## v0.1.19 (2025-07-17)
  @@ -6,7 +6,7 @@
6 6 {<<"GitHub">>,<<"https://github.com/ash-project/usage_rules">>},
7 7 {<<"Website">>,<<"https://ash-hq.org">>}]}.
8 8 {<<"name">>,<<"usage_rules">>}.
9 - {<<"version">>,<<"0.1.19">>}.
9 + {<<"version">>,<<"0.1.20">>}.
10 10 {<<"description">>,
11 11 <<"A dev tool for Elixir projects to gather LLM usage rules from dependencies">>}.
12 12 {<<"elixir">>,<<"~> 1.18">>}.
  @@ -14,6 +14,7 @@
14 14 [<<"lib">>,<<"lib/mix">>,<<"lib/mix/tasks">>,
15 15 <<"lib/mix/tasks/usage_rules.search_docs.ex">>,
16 16 <<"lib/mix/tasks/usage_rules.install.ex">>,
17 + <<"lib/mix/tasks/usage_rules.docs.ex">>,
17 18 <<"lib/mix/tasks/usage_rules.sync.ex">>,<<"lib/usage_rules.ex">>,
18 19 <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>,
19 20 <<"CHANGELOG.md">>,<<"usage-rules">>,<<"usage-rules/elixir.md">>,
  @@ -0,0 +1,92 @@
1 + defmodule Mix.Tasks.UsageRules.Docs do
2 + use Mix.Task
3 +
4 + @shortdoc "Shows documentation for Elixir modules and functions"
5 +
6 + @moduledoc """
7 + Shows documentation for Elixir modules and functions in markdown format.
8 +
9 + ## Examples
10 +
11 + Show documentation for a module:
12 + $ mix usage_rules.docs Enum
13 +
14 + Show documentation for a function:
15 + $ mix usage_rules.docs Enum.map/2
16 +
17 + Show documentation for a nested module:
18 + $ mix usage_rules.docs MyApp.User.Helpers
19 + """
20 +
21 + require Logger
22 +
23 + @impl true
24 + def run([module = <<first, _::binary>>]) when first in ?A..?Z or first == ?: do
25 + loadpaths!()
26 +
27 + iex_colors = Application.get_env(:iex, :colors, [])
28 + mix_colors = Application.get_env(:mix, :colors, [])
29 +
30 + try do
31 + Application.put_env(:iex, :colors, mix_colors)
32 +
33 + quoted =
34 + try do
35 + module
36 + |> Code.string_to_quoted!()
37 + rescue
38 + _ ->
39 + nil
40 + end
41 +
42 + quoted
43 + |> IEx.Introspection.decompose(__ENV__)
44 + |> case do
45 + :error ->
46 + Mix.raise("Invalid expression: #{module}")
47 +
48 + _decomposition ->
49 + Code.eval_quoted(
50 + quote do
51 + require IEx.Helpers
52 +
53 + IEx.Helpers.h(unquote(quoted))
54 + end
55 + )
56 + end
57 + after
58 + Application.put_env(:iex, :colors, iex_colors)
59 + end
60 + end
61 +
62 + def run([]) do
63 + raise_bad_args!()
64 + end
65 +
66 + def run([term | _]) do
67 + Mix.raise("Invalid module or function: #{term}")
68 + end
69 +
70 + # Loadpaths without checks because tasks may be defined in deps.
71 + defp loadpaths! do
72 + args = [
73 + "--no-elixir-version-check",
74 + "--no-deps-check",
75 + "--no-archives-check",
76 + "--no-listeners"
77 + ]
78 +
79 + Mix.Task.run("loadpaths", args)
80 + Mix.Task.reenable("loadpaths")
81 + Mix.Task.reenable("deps.loadpaths")
82 + end
83 +
84 + @spec raise_bad_args!() :: no_return()
85 + defp raise_bad_args! do
86 + Mix.raise("""
87 + Must provide a module or function. For example:
88 + $ mix usage_rules.docs Enum
89 + $ mix usage_rules.docs Enum.map/2
90 + """)
91 + end
92 + end
  @@ -65,6 +65,9 @@ defmodule Mix.Tasks.UsageRules.SearchDocs do
65 65 opts[:package] not in [nil, []] ->
66 66 filter_from_packages(opts[:package])
67 67
68 + !Mix.Project.config()[:app] ->
69 + nil
70 +
68 71 true ->
69 72 filter_from_mix_lock()
70 73 end
  @@ -139,6 +142,8 @@ defmodule Mix.Tasks.UsageRules.SearchDocs do
139 142
140 143 #{format_navigation_help_markdown(term, current_page, total_pages, opts)}
141 144
145 + #{format_search_scope_info(opts)}
146 +
142 147 ---
143 148
144 149 """
  @@ -154,7 +159,28 @@ defmodule Mix.Tasks.UsageRules.SearchDocs do
154 159 format_search_result_markdown(hit, actual_index, current_page, per_page)
155 160 end)
156 161
157 - header <> results
162 + docs_hint =
163 + try do
164 + term
165 + |> Code.string_to_quoted!()
166 + rescue
167 + _ ->
168 + nil
169 + end
170 + |> IEx.Introspection.decompose(__ENV__)
171 + |> case do
172 + :error ->
173 + ""
174 +
175 + _ ->
176 + """
177 + For module or function docs in the current app, run:
178 +
179 + mix usage_rules.docs #{term}
180 + """
181 + end
182 +
183 + header <> results <> docs_hint
158 184 end
159 185
160 186 defp format_search_result_markdown(hit, index, current_page, per_page) do
  @@ -204,7 +230,7 @@ defmodule Mix.Tasks.UsageRules.SearchDocs do
204 230
205 231 footer = """
206 232
207 - 📖 **View full documentation:**
233 + **full docs:**
208 234 https://hexdocs.pm/#{extract_package_name(package)}/#{ref}
209 235
210 236 ---
  @@ -222,6 +248,23 @@ defmodule Mix.Tasks.UsageRules.SearchDocs do
222 248 |> String.replace("</mark>", "\e[0m")
223 249 end
224 250
251 + defp format_search_scope_info(opts) do
252 + cond do
253 + opts[:everywhere] ->
254 + "**Searching:** All packages on hex.pm"
255 +
256 + opts[:package] not in [nil, []] ->
257 + packages = opts[:package] |> Enum.join(", ")
258 + "**Searching:** #{packages}"
259 +
260 + !Mix.Project.config()[:app] ->
261 + "**Searching:** All packages on hex.pm"
262 +
263 + true ->
264 + "**Searching:** Dependencies from current mix project"
265 + end
266 + end
267 +
225 268 defp format_navigation_help_markdown(term, current_page, total_pages, opts) do
226 269 base_cmd = build_base_command(term, opts)
227 270
  @@ -295,6 +338,8 @@ defmodule Mix.Tasks.UsageRules.SearchDocs do
295 338 end
296 339
297 340 defp filter_from_mix_lock do
341 + Mix.Task.run("compile")
342 +
298 343 apps =
299 344 if apps_paths = Mix.Project.apps_paths() do
300 345 Enum.filter(Mix.Project.deps_apps(), &is_map_key(apps_paths, &1))
  @@ -1,7 +1,7 @@
1 1 defmodule UsageRules.MixProject do
2 2 use Mix.Project
3 3
4 - @version "0.1.19"
4 + @version "0.1.20"
5 5 @description """
6 6 A dev tool for Elixir projects to gather LLM usage rules from dependencies
7 7 """
Loading more files…