Skip to content

Conversation

@yordis
Copy link
Contributor

@yordis yordis commented Oct 2, 2025

Signed-off-by: Yordis Prieto yordis.prieto@gmail.com

@github-actions
Copy link

github-actions bot commented Oct 2, 2025

@yordis
Copy link
Contributor Author

yordis commented Oct 6, 2025

@josevalim any thoughts here thus far?

@josevalim
Copy link
Member

Sorry, a bit busy with Elixir v1.19-rc and today I was out of focus. It is in my inbox and I will review it as soon as I can.

Copy link
Contributor

@leandrocp leandrocp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @yordis that's amazing! Let me share some ideas that could be useful.

I was inspecting the generated Markdown files and thinking if we could apply some changes. See the differences here https://gist.github.com/leandrocp/ee4f0ba8325b410b8650ccd26b9b2351 (CompiledWithDocs.md vs CompiledWithDocs_PROPOSAL.md)

  • Use frontmatter block to describe global values/notes
  • Use the format "Summary / Functions" similar to HTML pages (so Functions become a level 2 heading)
  • Include source links
  • Reorganize metadata, for eg: add (deprecated) and doc in summary
  • Remove links to hexdocs.pm because 1) it breaks the content and I guess that would make it harder for LLMs to parse; 2) I'm not sure it should link to html pages

You can see some examples on https://shopify.dev/docs/api/liquid/basics.md and https://vercel.com/docs/rest-api/reference/sdk.md

@yordis yordis force-pushed the yordis/add-markdown-formatter branch from e1c6f8f to a6870e3 Compare October 8, 2025 19:10
@yordis yordis marked this pull request as ready for review October 8, 2025 19:12
@yordis
Copy link
Contributor Author

yordis commented Oct 8, 2025

@leandrocp thanks for the help, about the formatting, I do not have any strong opinions of the final output, I will leave it to @josevalim and you to decide on that front, I can adjust it

@yordis yordis requested a review from josevalim October 8, 2025 19:14
@yordis yordis force-pushed the yordis/add-markdown-formatter branch from a6870e3 to 5131f1a Compare October 8, 2025 19:16
@josevalim
Copy link
Member

Just a heads up I am on holidays. This is still on top of my list but I cannot promise anything until I am back (next Wednesday). Sorry!

@leandrocp
Copy link
Contributor

Hey @yordis no strong opinions either, just trying to figure out a format that presents all the info.

Regarding links, just to be clear it should contain links eventually but I believe that HexDocs must define how to serve Markdown pages first, for eg something like https://hexdocs.pm/elixir/1.18.4/Enum.md

@josevalim
Copy link
Member

Some additional context: ExDoc is not tied to HexDocs and vice versa. We should design links with the assumptions it is served by any static host. I am thinking just using Enum.md with an anchor, same as html, is fine though? The anchor won’t actually work, but at least it is some context?

@leandrocp
Copy link
Contributor

leandrocp commented Oct 9, 2025

just using Enum.md with an anchor, same as html, is fine though?

Yeah it seems appropriate.


Shopify docs links to "canonical" URLs, for eg in https://shopify.dev/docs/api/liquid/tags/form you can see a bunch of anchor links, but in the .md docs (https://shopify.dev/docs/api/liquid/tags/form.md) they are rendered as [`cart`](https://shopify.dev/docs/api/liquid/tags/form#form-cart)

Vercel is not much different, they include relative links inside .md docs, for eg [Deployments Automation](/examples/deployments-automation)

Claude use relative links with anchors, for eg in https://docs.claude.com/en/docs/build-with-claude/prompt-caching you can see a link <a href="#pricing" class="link">at additional cost</a> which is rendered as [at additional cost](#pricing) in the .md docs (https://docs.claude.com/en/docs/build-with-claude/prompt-caching.md)

@josevalim
Copy link
Member

I am back. My goal is to release the new hexdocs.pm, review #2055, and then focus on this.

@yordis
Copy link
Contributor Author

yordis commented Oct 17, 2025

I am around, once you folks aligned, please share a action-driven feedback, I am can put the work on

@yordis yordis force-pushed the yordis/add-markdown-formatter branch from 5131f1a to 9770ce7 Compare October 29, 2025 16:49
@yordis
Copy link
Contributor Author

yordis commented Oct 29, 2025

@josevalim could you give a second pass to the code review 🙏🏻

@yordis yordis requested a review from josevalim October 29, 2025 17:35
@yordis yordis requested a review from eksperimental October 29, 2025 17:35
@@ -0,0 +1,177 @@
defmodule ExDoc.Formatter.MARKDOWN do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't follow the naming convention.

Suggested change
defmodule ExDoc.Formatter.MARKDOWN do
defmodule ExDoc.Formatter.Markdown do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eksperimental I had the same reaction, but there is code path that assume the module as all uppercase, so I had to rollback to this. I will fix at some point

Comment on lines +4 to +7
<%= if deprecated = node.deprecated do %>
> This <%= node.type %> is deprecated. <%= h(deprecated) %>.
<% end %>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<%= if deprecated = node.deprecated do %>
> This <%= node.type %> is deprecated. <%= h(deprecated) %>.
<% end %>
<%= if deprecated = node.deprecated do %>
> This <%= node.type %> is deprecated. <%= h(deprecated) %>.
<% end %>

Comment on lines +5 to +8
<%= if deprecated = module.deprecated do %>
> This <%= module.type %> is deprecated. <%=h deprecated %>.
<% end %>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<%= if deprecated = module.deprecated do %>
> This <%= module.type %> is deprecated. <%=h deprecated %>.
<% end %>
<%= if deprecated = module.deprecated do %>
> This <%= module.type %> is deprecated. <%=h deprecated %>.
<% end %>

Comment on lines +20 to +22
<%= for {name, nodes} <- summary, _key = text_to_id(name) do %>

### <%=h to_string(name) %>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<%= for {name, nodes} <- summary, _key = text_to_id(name) do %>
### <%=h to_string(name) %>
<%= for {name, nodes} <- summary, _key = text_to_id(name) do %>
### <%=h to_string(name) %>

Comment on lines +7 to +10
<%= if deprecated = node.deprecated do %>
> <%= h(deprecated) %>
<% end %>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<%= if deprecated = node.deprecated do %>
> <%= h(deprecated) %>
<% end %>
<%= if deprecated = node.deprecated do %>
> <%= h(deprecated) %>
<% end %>


def node_synopsis(_), do: nil

# Extract synopsis as DocAST (similar to ExDoc.DocAST.synopsis but returns AST instead of HTML string)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Extract synopsis as DocAST (similar to ExDoc.DocAST.synopsis but returns AST instead of HTML string)
# Extract synopsis as DocAST (similar to ExDoc.DocAST.synopsis but returns an AST instead of an HTML string).

@spec synopsis(nil) :: nil
def synopsis(doc) when is_binary(doc) do
case :binary.split(doc, "\n\n") do
[left, _] -> String.trim_trailing(left, ": ") <> "\n\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String.trim_trailing/2 does not strip every character in the second argument.
Also note that String.trim_trailing/1 removes all Unicode white-spaces, so it's better to use that. There's an edgecase that will not be covered though "foo : : :"

Suggested change
[left, _] -> String.trim_trailing(left, ": ") <> "\n\n"
[left, _] -> String.trim_trailing(left) |> String.trim_trailing(left, ":") <> "\n\n"

Comment on lines +161 to +163
@heading_regex ~r/^(\#{1,6})\s+(.*)/m
defp rewrite_headings(content) when is_binary(content) do
@heading_regex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing regular expressions in module attributes has been deprecated in Elixir v1.19.

Suggested change
@heading_regex ~r/^(\#{1,6})\s+(.*)/m
defp rewrite_headings(content) when is_binary(content) do
@heading_regex
defp rewrite_headings(content) when is_binary(content) do
~r/^(\#{1,6})\s+(.*)/m

@@ -0,0 +1,9 @@
# <%= config.project %> v<%= config.version %> - Documentation - Table of contents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# <%= config.project %> v<%= config.version %> - Documentation - Table of contents
# <%= config.project %> v<%= config.version %> - Documentation - Table of Contents

generate_docs(doc_config(context))

content = File.read!(tmp_dir <> "/markdown/index.md")
assert content =~ ~r{^# Elixir v1\.0\.1 - Documentation - Table of contents$}m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert content =~ ~r{^# Elixir v1\.0\.1 - Documentation - Table of contents$}m
assert content =~ ~r{^# Elixir v1\.0\.1 - Documentation - Table of Contents$}m

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants