-
Notifications
You must be signed in to change notification settings - Fork 356
Markdown formatter refactor #2181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| defmodule ExDoc.Formatter.MARKDOWN do | ||
| @moduledoc false | ||
|
|
||
| alias __MODULE__.{Templates} | ||
| alias __MODULE__.Templates | ||
| alias ExDoc.Formatter | ||
| alias ExDoc.Utils | ||
|
|
||
|
|
@@ -20,8 +20,7 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
| extras = Formatter.build_extras(config, ".md") | ||
|
|
||
| project_nodes = | ||
| project_nodes | ||
| |> Formatter.render_all(filtered_modules, ".md", config, highlight_tag: "samp") | ||
| Formatter.render_all(project_nodes, filtered_modules, ".md", config, highlight_tag: "samp") | ||
|
|
||
| nodes_map = %{ | ||
| modules: Formatter.filter_list(:module, project_nodes), | ||
|
|
@@ -30,14 +29,16 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
|
|
||
| config = %{config | extras: extras} | ||
|
|
||
| all_files = | ||
| [generate_nav(config, nodes_map)] ++ | ||
| generate_extras(config) ++ | ||
| generate_list(config, nodes_map.modules) ++ | ||
| generate_list(config, nodes_map.tasks) ++ | ||
| [generate_llm_index(config, nodes_map)] | ||
| [ | ||
| generate_nav(config, nodes_map), | ||
| generate_extras(config), | ||
| generate_list(config, nodes_map.modules), | ||
| generate_list(config, nodes_map.tasks), | ||
| generate_llm_index(config, nodes_map) | ||
| ] | ||
| |> List.flatten() | ||
| |> generate_build(build) | ||
|
|
||
| generate_build(List.flatten(all_files), build) | ||
| config.output |> Path.join("index.md") |> Path.relative_to_cwd() | ||
| end | ||
|
|
||
|
|
@@ -74,11 +75,10 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
| defp generate_build(files, build) do | ||
| entries = | ||
| files | ||
| |> Enum.uniq() | ||
| |> Enum.sort() | ||
| |> Enum.map(&[&1, "\n"]) | ||
| |> Enum.dedup() | ||
| |> Enum.intersperse("\n") | ||
|
|
||
| File.mkdir_p!(Path.dirname(build)) | ||
| File.write!(build, entries) | ||
| end | ||
|
|
||
|
|
@@ -95,11 +95,16 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
| end) | ||
|
|
||
| content = | ||
| Templates.nav_template(config, nodes) | ||
| config | ||
| |> Templates.nav_template(nodes) | ||
| |> normalize_output() | ||
|
|
||
| filename = "index.md" | ||
| File.write("#{config.output}/#{filename}", content) | ||
|
|
||
| config.output | ||
| |> Path.join(filename) | ||
| |> File.write(content) | ||
|
|
||
| filename | ||
| end | ||
|
|
||
|
|
@@ -108,13 +113,13 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
| %{id: id, source: content} <- extras, | ||
| not is_map_key(%{id: id, source: content}, :url) do | ||
| filename = "#{id}.md" | ||
| output = "#{config.output}/#{filename}" | ||
| output = Path.join(config.output, filename) | ||
|
|
||
| if File.regular?(output) do | ||
| Utils.warn("file #{Path.relative_to_cwd(output)} already exists", []) | ||
| end | ||
| if File.regular?(output), | ||
| do: Utils.warn("file #{Path.relative_to_cwd(output)} already exists", []) | ||
|
|
||
| File.write!(output, normalize_output(content)) | ||
|
|
||
| filename | ||
| end | ||
| end | ||
|
|
@@ -129,18 +134,27 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
|
|
||
| defp generate_module_page(module_node, config) do | ||
| content = | ||
| Templates.module_page(config, module_node) | ||
| config | ||
| |> Templates.module_page(module_node) | ||
| |> normalize_output() | ||
|
|
||
| filename = "#{module_node.id}.md" | ||
| File.write("#{config.output}/#{filename}", content) | ||
|
|
||
| config.output | ||
| |> Path.join(filename) | ||
| |> File.write(content) | ||
|
|
||
| filename | ||
| end | ||
|
|
||
| defp generate_llm_index(config, nodes_map) do | ||
| content = generate_llm_index_content(config, nodes_map) | ||
| filename = "llms.txt" | ||
| File.write("#{config.output}/#{filename}", content) | ||
|
|
||
| config.output | ||
| |> Path.join(filename) | ||
| |> File.write(content) | ||
|
Comment on lines
+154
to
+156
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please let me know if we should include a small private helper like: defp write_content(config, filename, content) do
config.output
|> Path.join(filename)
|> File.write(content)
endBecause this is repeated a few times in this module. |
||
|
|
||
| filename | ||
| end | ||
|
|
||
|
|
@@ -155,57 +169,64 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
| """ | ||
|
|
||
| modules_info = | ||
| nodes_map.modules | ||
| |> Enum.map(fn module_node -> | ||
| "- [#{module_node.title}](#{module_node.id}.md): #{module_node.doc |> ExDoc.DocAST.synopsis() |> extract_plain_text()}" | ||
| Enum.map(nodes_map.modules, fn module_node -> | ||
| synopsis = synopsis(module_node.doc) | ||
|
|
||
| ["- [#{module_node.title}](#{module_node.id}.md): ", synopsis, "\n"] | ||
|
Comment on lines
+172
to
+175
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think is more readable to work with lists in this case, and it should be a bit more performant for bigger projects. |
||
| end) | ||
| |> Enum.join("\n") | ||
|
|
||
| tasks_info = | ||
| if length(nodes_map.tasks) > 0 do | ||
| if Enum.any?(nodes_map.tasks) do | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We just care if there is at least one element in the list, we don't mind about the total number of entries. |
||
| tasks_list = | ||
| nodes_map.tasks | ||
| |> Enum.map(fn task_node -> | ||
| "- [#{task_node.title}](#{task_node.id}.md): #{task_node.doc |> ExDoc.DocAST.synopsis() |> extract_plain_text()}" | ||
| Enum.map(nodes_map.tasks, fn task_node -> | ||
| synopsis = synopsis(task_node.doc) | ||
|
|
||
| ["- [#{task_node.title}](#{task_node.id}.md): ", synopsis, "\n"] | ||
| end) | ||
| |> Enum.join("\n") | ||
|
|
||
| "\n\n## Mix Tasks\n\n" <> tasks_list | ||
| ["\n## Mix Tasks\n\n" | tasks_list] | ||
| else | ||
| "" | ||
| [] | ||
| end | ||
|
|
||
| extras_info = | ||
| if is_list(config.extras) and length(config.extras) > 0 do | ||
| if is_list(config.extras) and Enum.any?(config.extras) do | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, we just care if there is at least one element in the list, we don't mind about the total number of entries. |
||
| extras_list = | ||
| config.extras | ||
| |> Enum.flat_map(fn | ||
| {_group, extras} when is_list(extras) -> extras | ||
| _ -> [] | ||
| end) | ||
| |> Enum.map(fn extra -> | ||
| "- [#{extra.title}](#{extra.id}.md)" | ||
| end) | ||
| |> Enum.join("\n") | ||
| |> Enum.map(&["- [#{&1.title}](#{&1.id}.md)", "\n"]) | ||
|
|
||
| if extras_list == "" do | ||
| "" | ||
| if Enum.any?(extras_list) do | ||
| ["\n## Guides\n\n" | extras_list] | ||
| else | ||
| "\n\n## Guides\n\n" <> extras_list | ||
| [] | ||
| end | ||
| else | ||
| "" | ||
| [] | ||
| end | ||
|
|
||
| project_info <> modules_info <> tasks_info <> extras_info | ||
| [project_info, modules_info, tasks_info, extras_info] | ||
| end | ||
|
|
||
| defp synopsis(doc) do | ||
| doc | ||
| |> ExDoc.DocAST.synopsis() | ||
| |> extract_plain_text() | ||
| end | ||
|
|
||
| defp extract_plain_text(""), do: "No documentation available" | ||
|
|
||
| defp extract_plain_text(html) when is_binary(html) do | ||
| html | ||
| |> String.replace(~r/<[^>]*>/, "") | ||
| |> String.replace(~r/\s+/, " ") | ||
| |> String.trim() | ||
| |> case do | ||
| html = | ||
| html | ||
| |> String.replace(~r/<[^>]*>/, "") | ||
| |> String.replace(~r/\s+/, " ") | ||
| |> String.trim() | ||
|
|
||
| case html do | ||
| "" -> | ||
| "No documentation available" | ||
|
|
||
|
|
@@ -217,6 +238,4 @@ defmodule ExDoc.Formatter.MARKDOWN do | |
| end | ||
| end | ||
| end | ||
|
|
||
| defp extract_plain_text(_), do: "No documentation available" | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed because the
config.outputdirectory is always created in theoutput_setupprivate helper that is applied before this one.