From 505a50fbbcb5631f9aec95be22904c03e131494a Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Mon, 3 Jun 2024 23:12:38 +0200 Subject: [PATCH] add support for OTP 27 ex_unit autolinking add cheatmd --- .../language_server/dialyzer/supervisor.ex | 1 - .../lib/language_server/markdown_utils.ex | 67 ++++++++++++++++--- .../test/markdown_utils_test.exs | 45 +++++++++++-- 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/apps/language_server/lib/language_server/dialyzer/supervisor.ex b/apps/language_server/lib/language_server/dialyzer/supervisor.ex index 9c44b8edb..affe8bbdf 100644 --- a/apps/language_server/lib/language_server/dialyzer/supervisor.ex +++ b/apps/language_server/lib/language_server/dialyzer/supervisor.ex @@ -1,5 +1,4 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Supervisor do - alias ElixirLS.LanguageServer.{Dialyzer, DialyzerIncremental} use Supervisor def start_link(parent \\ self(), name \\ nil, root_path, dialyzer_module) do diff --git a/apps/language_server/lib/language_server/markdown_utils.ex b/apps/language_server/lib/language_server/markdown_utils.ex index 0aca8f2c5..429121650 100644 --- a/apps/language_server/lib/language_server/markdown_utils.ex +++ b/apps/language_server/lib/language_server/markdown_utils.ex @@ -95,10 +95,6 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do "**Since** #{text}" end - defp get_metadata_entry_md({:group, text}) when is_binary(text) do - "**Group** #{text}" - end - defp get_metadata_entry_md({:guard, true}) do "**Guard**" end @@ -249,13 +245,21 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do {key, url} end) + @erlang_ex_doc? System.otp_release() |> String.to_integer() >= 27 + def transform_ex_doc_link("t:" <> rest, current_module) do case @builtin_type_url[rest] do nil -> case get_module_fun_arity(rest) do {module, type, arity} -> if match?(":" <> _, rest) do - "https://www.erlang.org/doc/man/#{module}.html#type-#{type}" + if @erlang_ex_doc? do + # TODO not sure hos the docs will handle versions app/vsn does not work as of June 2024 + {app, _vsn} = DocLinks.get_app(module) + "https://www.erlang.org/doc/apps/#{app}/#{module}.html#t:#{type}/#{arity}" + else + "https://www.erlang.org/doc/man/#{module}.html#type-#{type}" + end else DocLinks.hex_docs_type_link(module || current_module, type, arity) end @@ -270,7 +274,13 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do case get_module_fun_arity(rest) do {module, callback, arity} -> if match?(":" <> _, rest) do - "https://www.erlang.org/doc/man/#{module}.html#Module:#{callback}-#{arity}" + if @erlang_ex_doc? do + # TODO not sure hos the docs will handle versions app/vsn does not work as of June 2024 + {app, _vsn} = DocLinks.get_app(module) + "https://www.erlang.org/doc/apps/#{app}/#{module}.html#c:#{callback}/#{arity}" + else + "https://www.erlang.org/doc/man/#{module}.html#Module:#{callback}-#{arity}" + end else DocLinks.hex_docs_callback_link(module || current_module, callback, arity) end @@ -280,6 +290,32 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do def transform_ex_doc_link("e:http://" <> rest, _current_module), do: "http://" <> rest def transform_ex_doc_link("e:https://" <> rest, _current_module), do: "https://" <> rest + otp_apps_dir = + :code.get_object_code(:erlang) |> elem(2) |> Path.join("../../..") |> Path.expand() + + @all_otp_apps otp_apps_dir + |> File.ls!() + |> Enum.map(&(&1 |> String.split("-") |> hd() |> String.to_atom())) + + if @erlang_ex_doc? do + def transform_ex_doc_link("e:system:" <> rest, _current_module) do + {page, anchor} = split(rest) + + page = + page + |> String.replace(~r/\.(md|livemd|cheatmd|txt)$/, ".html") + |> String.replace(" ", "-") + |> String.downcase() + + "https://www.erlang.org/doc/system/#{page}" <> + if anchor do + "#" <> anchor + else + "" + end + end + end + def transform_ex_doc_link("e:" <> rest, current_module) do {page, anchor} = split(rest) @@ -291,7 +327,7 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do page = page - |> String.replace(~r/\.(md|livemd|txt)$/, ".html") + |> String.replace(~r/\.(md|livemd|cheatmd|txt)$/, ".html") |> String.replace(" ", "-") |> String.downcase() @@ -321,7 +357,14 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do end if app_vsn do - DocLinks.hex_docs_extra_link(app_vsn, page) <> + {app, _vsn} = app_vsn + + if app in @all_otp_apps and @erlang_ex_doc? do + # TODO not sure hos the docs will handle versions app/vsn does not work as of June 2024 + "https://www.erlang.org/doc/apps/#{app}/#{page}" + else + DocLinks.hex_docs_extra_link(app_vsn, page) + end <> if anchor do "#" <> anchor else @@ -344,7 +387,13 @@ defmodule ElixirLS.LanguageServer.MarkdownUtils do {module, function, arity} -> if match?(":" <> _, prefix) and module != Kernel.SpecialForms do - "https://www.erlang.org/doc/man/#{module}.html##{function}-#{arity}" + if @erlang_ex_doc? do + # TODO not sure hos the docs will handle versions app/vsn does not work as of June 2024 + {app, _vsn} = DocLinks.get_app(module) + "https://www.erlang.org/doc/apps/#{app}/#{module}.html##{function}/#{arity}" + else + "https://www.erlang.org/doc/man/#{module}.html##{function}-#{arity}" + end else DocLinks.hex_docs_function_link(module || current_module, function, arity) end diff --git a/apps/language_server/test/markdown_utils_test.exs b/apps/language_server/test/markdown_utils_test.exs index a06911ceb..bc47aeea3 100644 --- a/apps/language_server/test/markdown_utils_test.exs +++ b/apps/language_server/test/markdown_utils_test.exs @@ -167,8 +167,14 @@ defmodule ElixirLS.LanguageServer.MarkdownUtilsTest do end test "erlang type" do - assert MarkdownUtils.transform_ex_doc_links("`t::array.array/0`") == - "[`:array.array/0`](https://www.erlang.org/doc/man/array.html#type-array)" + expected = + if System.otp_release() |> String.to_integer() >= 27 do + "[`:array.array/0`](https://www.erlang.org/doc/apps/stdlib/array.html#t:array/0)" + else + "[`:array.array/0`](https://www.erlang.org/doc/man/array.html#type-array)" + end + + assert MarkdownUtils.transform_ex_doc_links("`t::array.array/0`") == expected end test "elixir callback link with prefix" do @@ -177,8 +183,14 @@ defmodule ElixirLS.LanguageServer.MarkdownUtilsTest do end test "erlang callback" do - assert MarkdownUtils.transform_ex_doc_links("`c::gen_server.handle_call/3`") == - "[`:gen_server.handle_call/3`](https://www.erlang.org/doc/man/gen_server.html#Module:handle_call-3)" + expected = + if System.otp_release() |> String.to_integer() >= 27 do + "[`:gen_server.handle_call/3`](https://www.erlang.org/doc/apps/stdlib/gen_server.html#c:handle_call/3)" + else + "[`:gen_server.handle_call/3`](https://www.erlang.org/doc/man/gen_server.html#Module:handle_call-3)" + end + + assert MarkdownUtils.transform_ex_doc_links("`c::gen_server.handle_call/3`") == expected end test "elixir callback link without module" do @@ -237,8 +249,14 @@ defmodule ElixirLS.LanguageServer.MarkdownUtilsTest do end test "erlang stdlib function link" do - assert MarkdownUtils.transform_ex_doc_links("`:lists.all/2`") == - "[`:lists.all/2`](https://www.erlang.org/doc/man/lists.html#all-2)" + expected = + if System.otp_release() |> String.to_integer() >= 27 do + "[`:lists.all/2`](https://www.erlang.org/doc/apps/stdlib/lists.html#all/2)" + else + "[`:lists.all/2`](https://www.erlang.org/doc/man/lists.html#all-2)" + end + + assert MarkdownUtils.transform_ex_doc_links("`:lists.all/2`") == expected end test "extra page" do @@ -276,6 +294,21 @@ defmodule ElixirLS.LanguageServer.MarkdownUtilsTest do ) == "[Up and running](http://example.com/foo.md)" end + test "erlang extra page" do + assert MarkdownUtils.transform_ex_doc_links( + "[Up and running](e:erts_alloc.md)", + :erlang + ) == "[Up and running](https://www.erlang.org/doc/apps/erts/erts_alloc.html)" + end + + test "erlang extra page with app" do + assert MarkdownUtils.transform_ex_doc_links( + "[Up and running](e:system:expressions.md#term-comparisons)", + :lists + ) == + "[Up and running](https://www.erlang.org/doc/system/expressions.html#term-comparisons)" + end + test "expression" do assert MarkdownUtils.transform_ex_doc_links("`1 + 2`") == "`1 + 2`" end