Skip to content

Commit ec67946

Browse files
garazdawiclaude
andauthored
Fix markdown backend code fence language and opaque type display (#2215)
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 ec67946

File tree

4 files changed

+30
-30
lines changed

4 files changed

+30
-30
lines changed

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)