Skip to content

Display description text in docs groups - #2113

Merged
josevalim merged 14 commits into
elixir-lang:mainfrom
lud:group-header
Jun 11, 2025
Merged

Display description text in docs groups#2113
josevalim merged 14 commits into
elixir-lang:mainfrom
lud:group-header

Conversation

@lud

@lud lud commented Apr 9, 2025

Copy link
Copy Markdown
Contributor

Related to #2104

Still a work in progress. If we go in this direction we should add docs as well.

There are some limits with this solution:

  • Since we want the description from default_group_for_docs, it means that each doc node can return a different description for a given group. I made the choice to ignore those descriptions based on the group title.
  • As we want to keep the group order defined in moduledoc, the retriever will fetch those, possibly containing descriptions. I made the choice to not replace descriptions from moduledoc by descriptions returned by the config callback.
  • Node groups will temporarily contain a title/description map, only to be replaced with just the title once the groups are built in the modulenode. This avoids a lot of "when is_binary() ... when is_map()" stuff and avoids to refactor many tests. This also allows to have each group rendered only once. A drawback is that in the module_summary function we are reconciliating the groups once again where each doc node could already bear the full group. Not a big deal imho.
  • Since those groups are rendered, they must behave like most nodes. But they are not nodes for now. I guess we could add a struct for those, but it's not the real problem. What is more important is that I had to hardcode "text/markdown" and wrap the text in %{"en" => text }, as module and doc nodes are extracted from documentation. I could have written a specialized doc_ast render function though.
  • I added some classes on the html elements which may not be needed. Same for the IDs. This was mostly to test the rendering.

Besides that, it works.

Comment thread lib/ex_doc/formatter/html/templates.ex Outdated
def module_summary(module_node) do
# TODO: Maybe it should be moved to retriever and it already returned grouped metadata
ExDoc.GroupMatcher.group_by(module_node.docs_groups, module_node.docs, & &1.group)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The TODO above is precisely about moving this logic to the retriever. Given we are already changing the group structure, maybe now is a good time to go ahead and do it? Basically, by adding a group column to the groups upfront?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah I saw this TODO and in my first attempt I wanted to provide docs_groups with all the docs inside.

But to avoid having duplicate data, to me it means that we basically remove the :docs key from the ModuleNode struct. And that means refactoring a lot of tests to look for expected data in a list of groups instead of just matching on the content of the docs key. Lot more code to change and review. But I can do it if you think it's alright.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ah sorry I missed the part about a group column. Should be simple enough.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Alright, please see that last commit: aed460e

(There were other commits before since you last replied).

I'm not sure this is what you expect. The retriever is computing the groups, so the renderer part is only fetching that, which is what we wanted.

But,

  • There is duplicated data. The ModuleNode still has its :docs key with the DocNode list because many tests look into that for assertions. We can update those tests to look into the groups instead, with a lot of Enum.find calls :) and then delete the ModuleNode :docs key.
  • Those DocNodes in the :docs key have nil :rendered_doc because we only render the copy of the doc nodes that are inside group nodes.

Or maybe it is something else you had in mind?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The code looks great, thank you ! 🤩

There is duplicated data. The ModuleNode still has its :docs key with the DocNode list because many tests look into that for assertions. We can update those tests to look into the groups instead, with a lot of Enum.find calls :) and then delete the ModuleNode :docs key.

I think it is worth giving this a try! Perhaps we could have a helper function in tests, called find_doc(...) or find_group(...) to help make life easier. Could you please try that as well? I understand it is a lot of work though, but this is some of the refactoring we have been wanting to do for quite some time (basically do less work on the formatters and more on the retrievers).

Comment thread lib/ex_doc/nodes.ex Outdated
Comment thread lib/ex_doc/retriever.ex Outdated
Comment thread lib/ex_doc/retriever.ex
@lud
lud force-pushed the group-header branch 2 times, most recently from 882493f to 8db2c39 Compare April 17, 2025 05:43
@lud

lud commented Apr 28, 2025

Copy link
Copy Markdown
Contributor Author

Hey @josevalim , have you had time to review my comments?

@josevalim

Copy link
Copy Markdown
Member

Sorry for the delay, I am on holidays so I have limited time to stay on top of my inbox. I am reviewing this now but probably merge it only when I am back. :)

@lud

lud commented May 5, 2025

Copy link
Copy Markdown
Contributor Author

Hello @josevalim no problem, it was just to be sure this was not forgotten. Take your time. We probably need to add some docs before mergeing.

@garazdawi if you have time can you tell me if it works as you would expect?

Thank you both.

@garazdawi

Copy link
Copy Markdown
Contributor

I did a quick test and it works as expected. I'm wondering if maybe something could be done about the styling to make it more obvious that the text belongs to the entire group of functions... not sure what that would be though.

image

@lud

lud commented May 10, 2025

Copy link
Copy Markdown
Contributor Author

@garazdawi thank you !

I think we can just add some whitespace between the description and the first function signature block. The same as between the title and the description. I guess that would be enough to show that the text belongs to the section.

@josevalim I see a lot have changed about the heading and html ID generation. I have rebased my branch and made sure that the new groups descriptions go through add_fancy_anchors. I also updated the templates to use the new render_doc helper instead of having a .rendered_doc key in the nodes.

I'll tackle removing the ModuleNode.docs key and updating the tests soon.

@lud

lud commented May 10, 2025

Copy link
Copy Markdown
Contributor Author

@garazdawi I think whitespace is enough, what do you think?

image

@josevalim

Copy link
Copy Markdown
Member

@lud, apologies for the large changes that had happened. But we had bugs and a lot of duplication, and the new version should make things more accessible and easier to follow.

@lud

lud commented May 10, 2025

Copy link
Copy Markdown
Contributor Author

@josevalim No worries :)

@lud
lud marked this pull request as ready for review May 14, 2025 10:42
@lud

lud commented May 14, 2025

Copy link
Copy Markdown
Contributor Author

Hello,

I removed the .docs field from the ModuleNode struct and fixed all the tests accordingly. It was actually easy since most tests generate only one group. Other tests were straightforward too.

So I left the helper function directly in elixir_test.exs since this is the only place it is used.

A test failed because this function was receiving a list of 2 elements (and it is matching on a list of 1 element only)

defp extract_filename!([{location, _}]), do: location

I may have done something wrong when building the assets when changing whitespace for the groups.

I ran the build command (npm run --prefix assets build) once more and that fixed it, only a single file was present. I guess it was just because of the git rebase.

@lud

lud commented May 14, 2025

Copy link
Copy Markdown
Contributor Author

@josevalim if it is ok to merge, would you like me to squash the branch ?

Also to prevent further rebasing issues I suggest I'd write the docs in another PR.

@lud

lud commented May 28, 2025

Copy link
Copy Markdown
Contributor Author

@josevalim if it is ok to merge, would you like me to squash the branch ?

Also to prevent further rebasing issues I suggest I'd write the docs in another PR.

Hello,

I just rebased the branch on the latest release. Do you think we can merge or are there some remaining things to fix?

@josevalim

Copy link
Copy Markdown
Member

My plan is to review it this week as I am finally getting caught up with work :)

@lud

lud commented May 28, 2025

Copy link
Copy Markdown
Contributor Author

Perfect, thank you!

Comment thread lib/ex_doc/formatter/html/assets.ex Outdated
|> Enum.map(fn path ->
Module.put_attribute(__CALLER__.module, :external_resource, path)
{Path.basename(path), File.read!(path)}
{Path.basename(path), "File.read!(#{path})"}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems to be a mistake? :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gosh ... fortunately you spotted that. Yeah I was debugging assets generation at some point.

Comment thread lib/ex_doc/formatter/html/templates.ex Outdated
# TODO: Maybe it should be moved to retriever and it already returned grouped metadata
ExDoc.GroupMatcher.group_by(module_node.docs_groups, module_node.docs, & &1.group)
end
def module_summary(module_node), do: module_node.docs_groups

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's remove this function and update its callers?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done,

I took the liberty to update the templates to directly use module.docs_groups in there, I think it's clearer that we are dealing directly with the content of %ModuleNode{}.docs_groups`.

@josevalim josevalim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am sorry for the delay! I have added one nit and one comment, and we can ship it! ❤️

@lud

lud commented Jun 11, 2025

Copy link
Copy Markdown
Contributor Author

Hello,

I fixed your last comments, thank you for the review :)

@github-actions

Copy link
Copy Markdown

@josevalim
josevalim merged commit e85d853 into elixir-lang:main Jun 11, 2025
@josevalim

Copy link
Copy Markdown
Member

💚 💙 💜 💛 ❤️

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.

3 participants