Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/ex_doc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,16 @@ defmodule ExDoc do
end

defp find_formatter(name) do
[ExDoc.Formatter, String.upcase(name)]
[ExDoc.Formatter, format_module_name(name)]
|> Module.concat()
|> check_formatter_module(name)
end

defp format_module_name("html"), do: "HTML"
defp format_module_name("epub"), do: "EPUB"
defp format_module_name("markdown"), do: "Markdown"
defp format_module_name(name), do: String.upcase(name)

defp check_formatter_module(modname, argname) do
if Code.ensure_loaded?(modname) do
modname
Expand Down
61 changes: 61 additions & 0 deletions lib/ex_doc/doc_ast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,67 @@ defmodule ExDoc.DocAST do
Enum.map(attrs, fn {key, val} -> " #{key}=\"#{ExDoc.Utils.h(val)}\"" end)
end

@doc """
Transform AST into markdown string.
"""
def to_markdown(ast)

def to_markdown(binary) when is_binary(binary) do
ExDoc.Utils.h(binary)
end

def to_markdown(list) when is_list(list) do
Enum.map_join(list, "", &to_markdown/1)
end

def to_markdown({:comment, _attrs, inner, _meta}) do
"<!--#{inner}-->"
end

def to_markdown({:code, attrs, inner, _meta}) do
lang = attrs[:class] || ""

"""
```#{lang}
#{inner}
```
"""
end

def to_markdown({:a, attrs, inner, _meta}) do
"[#{to_markdown(inner)}](#{attrs[:href]})"
end

def to_markdown({:hr, _attrs, _inner, _meta}) do
"\n\n---\n\n"
end

def to_markdown({:p, _attrs, inner, _meta}) do
to_markdown(inner) <> "\n\n"
end

def to_markdown({:br, _attrs, _inner, _meta}) do
"\n\n"
end

def to_markdown({:img, attrs, _inner, _meta}) do
alt = attrs[:alt] || ""
title = attrs[:title] || ""
"![#{alt}](#{attrs[:src]} \"#{title}\")"
end

def to_markdown({tag, _attrs, _inner, _meta}) when tag in @void_elements do
""
end

def to_markdown({_tag, _attrs, inner, %{verbatim: true}}) do
Enum.join(inner, "")
end

def to_markdown({_tag, _attrs, inner, _meta}) do
to_markdown(inner)
end

## parse markdown

defp parse_markdown(markdown, opts) do
Expand Down
23 changes: 15 additions & 8 deletions lib/ex_doc/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ defmodule ExDoc.Formatter do

specs = Enum.map(child_node.specs, &language.autolink_spec(&1, autolink_opts))
child_node = %{child_node | specs: specs}
render_doc(child_node, language, autolink_opts, opts)
render_doc(child_node, ext, language, autolink_opts, opts)
end

%{render_doc(group, language, autolink_opts, opts) | docs: docs}
%{render_doc(group, ext, language, autolink_opts, opts) | docs: docs}
end

%{
render_doc(node, language, [{:id, node.id} | autolink_opts], opts)
render_doc(node, ext, language, [{:id, node.id} | autolink_opts], opts)
| docs_groups: docs_groups
}
end,
Expand Down Expand Up @@ -117,11 +117,11 @@ defmodule ExDoc.Formatter do

# Helper functions

defp render_doc(%{doc: nil} = node, _language, _autolink_opts, _opts),
defp render_doc(%{doc: nil} = node, _ext, _language, _autolink_opts, _opts),
do: node

defp render_doc(%{doc: doc} = node, language, autolink_opts, opts) do
doc = autolink_and_highlight(doc, language, autolink_opts, opts)
defp render_doc(%{doc: doc} = node, ext, language, autolink_opts, opts) do
doc = autolink_and_render(doc, ext, language, autolink_opts, opts)
%{node | doc: doc}
end

Expand All @@ -137,7 +137,13 @@ defmodule ExDoc.Formatter do
mod_id <> "." <> id
end

defp autolink_and_highlight(doc, language, autolink_opts, opts) do
defp autolink_and_render(doc, ".md", language, autolink_opts, opts) do
doc
|> language.autolink_doc(autolink_opts)
|> ExDoc.DocAST.highlight(language, opts)
end

defp autolink_and_render(doc, _html_ext, language, autolink_opts, opts) do
doc
|> language.autolink_doc(autolink_opts)
|> ExDoc.DocAST.highlight(language, opts)
Expand Down Expand Up @@ -187,6 +193,7 @@ defmodule ExDoc.Formatter do

source_file = validate_extra_string!(input_options, :source) || input
opts = [file: source_file, line: 1]
ext = Keyword.fetch!(autolink_opts, :ext)

{extension, source, ast} =
case extension_name(input) do
Expand All @@ -202,7 +209,7 @@ defmodule ExDoc.Formatter do
source
|> Markdown.to_ast(opts)
|> ExDoc.DocAST.add_ids_to_headers([:h2, :h3])
|> autolink_and_highlight(language, [file: input] ++ autolink_opts, opts)
|> autolink_and_render(ext, language, [file: input] ++ autolink_opts, opts)

{extension, source, ast}

Expand Down
66 changes: 54 additions & 12 deletions lib/ex_doc/formatter/epub.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ defmodule ExDoc.Formatter.EPUB do
"""
@spec run([ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()], ExDoc.Config.t()) :: String.t()
def run(project_nodes, filtered_modules, config) when is_map(config) do
# Store original output for build file before normalize_config creates temp path
original_output = config.output
config = normalize_config(config)
File.rm_rf!(config.output)

build = Path.join(original_output, ".build")
output_setup(build, config)
File.mkdir_p!(Path.join(config.output, "OEBPS"))

project_nodes =
Expand Down Expand Up @@ -40,12 +44,15 @@ defmodule ExDoc.Formatter.EPUB do
uuid = "urn:uuid:#{uuid4()}"
datetime = format_datetime()

generate_content(config, nodes_map, uuid, datetime, static_files)
generate_nav(config, nodes_map)
generate_title(config)
generate_extras(config)
generate_list(config, nodes_map.modules)
generate_list(config, nodes_map.tasks)
content_files = generate_content(config, nodes_map, uuid, datetime, static_files)
nav_files = generate_nav(config, nodes_map)
title_files = generate_title(config)
extra_files = generate_extras(config)
module_files = generate_list(config, nodes_map.modules)
task_files = generate_list(config, nodes_map.tasks)

all_files = List.flatten([content_files, nav_files, title_files, extra_files, module_files, task_files])
generate_build(all_files, build)

{:ok, epub} = generate_epub(config.output)
File.rm_rf!(config.output)
Expand All @@ -65,14 +72,16 @@ defmodule ExDoc.Formatter.EPUB do
for {_title, extras} <- config.extras,
node <- extras,
not is_map_key(node, :url) do
output = "#{config.output}/OEBPS/#{node.id}.xhtml"
filename = "OEBPS/#{node.id}.xhtml"
output = "#{config.output}/#{filename}"
html = Templates.extra_template(config, node)

if File.regular?(output) do
Utils.warn("file #{Path.relative_to_cwd(output)} already exists", [])
end

File.write!(output, html)
filename
end
end

Expand All @@ -84,7 +93,9 @@ defmodule ExDoc.Formatter.EPUB do
do: {Path.relative_to(name, "OEBPS"), media_type}

content = Templates.content_template(config, nodes, uuid, datetime, static_files)
File.write("#{config.output}/OEBPS/content.opf", content)
filename = "OEBPS/content.opf"
File.write("#{config.output}/#{filename}", content)
[filename]
end

defp generate_nav(config, nodes) do
Expand All @@ -94,12 +105,16 @@ defmodule ExDoc.Formatter.EPUB do
end)

content = Templates.nav_template(config, nodes)
File.write("#{config.output}/OEBPS/nav.xhtml", content)
filename = "OEBPS/nav.xhtml"
File.write("#{config.output}/#{filename}", content)
[filename]
end

defp generate_title(config) do
content = Templates.title_template(config)
File.write("#{config.output}/OEBPS/title.xhtml", content)
filename = "OEBPS/title.xhtml"
File.write("#{config.output}/#{filename}", content)
[filename]
end

defp generate_list(config, nodes) do
Expand All @@ -126,6 +141,31 @@ defmodule ExDoc.Formatter.EPUB do
)
end

defp output_setup(build, config) do
if File.exists?(build) do
build
|> File.read!()
|> String.split("\n", trim: true)
|> Enum.map(&Path.join(config.output, &1))
|> Enum.each(&File.rm/1)

File.rm(build)
else
File.rm_rf!(config.output)
end
end

defp generate_build(files, build) do
entries =
files
|> Enum.uniq()
|> Enum.sort()
|> Enum.map(&[&1, "\n"])

File.mkdir_p!(Path.dirname(build))
File.write!(build, entries)
end

## Helpers

defp default_assets(config) do
Expand Down Expand Up @@ -159,7 +199,9 @@ defmodule ExDoc.Formatter.EPUB do

defp generate_module_page(module_node, config) do
content = Templates.module_page(config, module_node)
File.write("#{config.output}/OEBPS/#{module_node.id}.xhtml", content)
filename = "OEBPS/#{module_node.id}.xhtml"
File.write("#{config.output}/#{filename}", content)
filename
end

@two_power_16 65536
Expand Down
4 changes: 2 additions & 2 deletions lib/ex_doc/formatter/epub/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ defmodule ExDoc.Formatter.EPUB.Templates do
defp render_doc(ast), do: ast && ExDoc.DocAST.to_string(ast)

@doc """
Generate content from the module template for a given `node`
Generate content from the module template for a given `node`.
"""
def module_page(config, module_node) do
module_template(config, module_node)
end

@doc """
Generated ID for static file
Generated ID for static file.
"""
def static_file_to_id(static_file) do
static_file |> Path.basename() |> text_to_id()
Expand Down
1 change: 1 addition & 0 deletions lib/ex_doc/formatter/html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ defmodule ExDoc.Formatter.HTML do
|> Enum.sort()
|> Enum.map(&[&1, "\n"])

File.mkdir_p!(Path.dirname(build))
File.write!(build, entries)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/html/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule ExDoc.Formatter.HTML.Templates do
]

@doc """
Generate content from the module template for a given `node`
Generate content from the module template for a given `node`.
"""
def module_page(module_node, config) do
module_template(config, module_node)
Expand Down
Loading