Skip to content

Commit 0183a7f

Browse files
garazdawiclaude
andcommitted
Fix markdown backend code fence language and opaque type display
Use the module's language for code fences instead of hardcoded "elixir", and hide opaque type internals in Erlang's format_spec. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d10a4d7 commit 0183a7f

4 files changed

Lines changed: 30 additions & 30 deletions

File tree

lib/ex_doc/formatter/markdown/templates/detail_template.eex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<% end %>
77

88
<%= if node.source_specs != [] do %>
9-
```elixir
9+
```<%= module.language.highlight_info().language_name %>
1010
<%= for spec <- node.source_specs do %><%= module.language.format_spec_attribute(node) %> <%= module.language.format_spec(spec) %>
1111
<% end %>```
1212
<% end %>

lib/ex_doc/language/erlang.ex

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -207,19 +207,6 @@ defmodule ExDoc.Language.Erlang do
207207
nil
208208
end
209209

210-
def autolink_spec({:attribute, _, :opaque, ast}, _opts) do
211-
{name, _, args} = ast
212-
213-
args =
214-
for arg <- args do
215-
{:var, _, name} = arg
216-
Atom.to_string(name)
217-
end
218-
|> Enum.intersperse(", ")
219-
220-
IO.iodata_to_binary([Atom.to_string(name), "(", args, ")"])
221-
end
222-
223210
def autolink_spec(ast, %Autolink{} = config) do
224211
{name, anno, quoted} =
225212
case ast do
@@ -232,11 +219,7 @@ defmodule ExDoc.Language.Erlang do
232219

233220
{mn, anno, Enum.map(ast, &Code.Typespec.spec_to_quoted(name, &1))}
234221

235-
{:attribute, anno, :type, ast} ->
236-
{name, _, _} = ast
237-
{name, anno, Code.Typespec.type_to_quoted(ast)}
238-
239-
{:attribute, anno, :nominal, ast} ->
222+
{:attribute, anno, kind, ast} when kind in [:type, :opaque, :nominal] ->
240223
{name, _, _} = ast
241224
{name, anno, Code.Typespec.type_to_quoted(ast)}
242225
end
@@ -510,10 +493,17 @@ defmodule ExDoc.Language.Erlang do
510493

511494
options = [linewidth: 98 + offset]
512495

513-
:erl_pp.attribute(ast, options)
514-
|> IO.chardata_to_string()
515-
|> String.trim()
516-
|> String.trim_leading("-#{Atom.to_string(type)} ")
496+
spec =
497+
:erl_pp.attribute(ast, options)
498+
|> IO.chardata_to_string()
499+
|> String.trim()
500+
|> String.trim_leading("-#{Atom.to_string(type)} ")
501+
502+
if type == :opaque do
503+
String.replace(spec, ~r/ ::.*$/s, "")
504+
else
505+
spec
506+
end
517507
end
518508

519509
# Traverses quoted and formatted string of the typespec AST, replacing refs with links.

test/ex_doc/formatter/erlang_test.exs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ defmodule ExDoc.Formatter.ErlangTest do
5656
-module(bar).
5757
-moduledoc("bar module.").
5858
-export([bar/1, baz/0]).
59-
-export_type([t/0]).
59+
-export_type([t/0, o/0]).
6060
6161
-doc("bar/1 function.").
6262
-spec bar(t()) -> t().
@@ -68,6 +68,9 @@ defmodule ExDoc.Formatter.ErlangTest do
6868
6969
-doc("t/0 type.").
7070
-type t() :: atom().
71+
72+
-doc("o/0 opaque.").
73+
-opaque o() :: {atom(), integer()}.
7174
""")
7275

7376
generate(c)
@@ -104,13 +107,20 @@ defmodule ExDoc.Formatter.ErlangTest do
104107
assert markdown =~ "bar/1 function."
105108
assert markdown =~ "baz/0 function."
106109

107-
# Check specs in markdown
108-
assert markdown =~ "-spec bar(t()) -> t()."
109-
assert markdown =~ "-spec baz() -> atom()."
110+
# Check specs in markdown use erlang code fences
111+
assert markdown =~ "```erlang\n-spec bar(t()) -> t()."
112+
assert markdown =~ "```erlang\n-spec baz() -> atom()."
110113

111114
# Check type in markdown
112-
assert markdown =~ "-type t() :: atom()."
115+
assert markdown =~ "```erlang\n-type t() :: atom()."
113116
assert markdown =~ "t/0 type."
117+
118+
# Opaque types hide their internals in both HTML and markdown
119+
assert html =~ ~s|-opaque</span> o()|
120+
refute html =~ ~s|o() :: {|
121+
assert markdown =~ "```erlang\n-opaque o()\n```"
122+
refute markdown =~ "o() ::"
123+
assert markdown =~ "o/0 opaque."
114124
end
115125

116126
defp generate(c) do

test/ex_doc/retriever/erlang_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ defmodule ExDoc.Retriever.ErlangTest do
299299
assert opaque1.doc |> DocAST.to_html() =~ "opaque1/0 docs."
300300

301301
assert hd(opaque1.source_specs) |> format_spec() ==
302-
"opaque1() :: atom()."
302+
"opaque1()"
303303

304304
assert nominal1.id == "t:nominal1/0"
305305
assert nominal1.type == @nominal_type
@@ -514,7 +514,7 @@ defmodule ExDoc.Retriever.ErlangTest do
514514
assert opaque1.doc |> DocAST.to_html() =~ "opaque1/0 docs."
515515

516516
assert hd(opaque1.source_specs) |> format_spec() ==
517-
"opaque1() :: atom()."
517+
"opaque1()"
518518

519519
assert nominal1.id == "t:nominal1/0"
520520
assert nominal1.type == @nominal_type

0 commit comments

Comments
 (0)