Packages
ex_doc
0.8.1
0.40.3
0.40.2
0.40.1
0.40.0
0.39.3
0.39.2
0.39.1
0.39.0
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.3
0.37.2
0.37.1
0.37.0
0.37.0-rc.2
0.37.0-rc.1
0.37.0-rc.0
0.36.1
0.36.0
0.35.1
0.35.0
0.34.2
0.34.1
0.34.0
0.33.0
0.32.2
0.32.1
0.32.0
0.31.2
0.31.1
0.31.0
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.4
0.29.3
0.29.2
0.29.1
0.29.0
0.28.6
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.3
0.27.2
0.27.1
0.27.0
0.26.0
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.2
0.24.1
0.24.0
0.23.0
0.22.6
0.22.5
0.22.4
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.2
0.20.1
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.19.0-rc
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
retired
0.17.1
0.17.0
retired
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
ExDoc is a documentation generation tool for Elixir
Current section
Files
Jump to
Current section
Files
lib/ex_doc/markdown/pandoc.ex
defmodule ExDoc.Markdown.Pandoc do
def available? do
!!System.find_executable("pandoc")
end
@doc """
Pandoc specific options:
* `:format` - output format, defaults to :html
* `:header_level` - base header level, outputs to 1
"""
def to_html(text, opts \\ []) when is_binary(text) do
text
|> text_to_file()
|> open_port(opts)
|> process_port()
|> pretty_codeblocks()
end
defp text_to_file(text) do
id = :crypto.rand_bytes(4) |> Base.encode16
unique_name = "tmpdoc_#{id}.md"
tmp_path = Path.join(System.tmp_dir, unique_name)
File.write!(tmp_path, text)
tmp_path
end
defp open_port(path, opts) do
exe = :os.find_executable('pandoc')
args = ["--from", "markdown+fenced_code_blocks",
"--to", get_string(opts, :format, :html),
"--base-header-level", get_string(opts, :header_level, 1),
path]
Port.open({:spawn_executable, exe}, [:stream, :binary, {:args, args},
:hide, :use_stdio, :stderr_to_stdout, :exit_status])
end
defp get_string(opts, key, default) do
Keyword.get(opts, key, default) |> to_string
end
defp process_port(port) do
{0, data} = collect_output(port, [])
data
end
defp collect_output(port, data) do
receive do
{^port, {:data, new_data}} ->
collect_output(port, [data, new_data])
{^port, {:exit_status, status}} ->
{status, List.to_string(data)}
end
end
@doc false
# Helper to handle fenced code blocks (```...```) with
# language specification
defp pretty_codeblocks(bin) do
# Pandoc parser puts the class attribute inside the `pre` tag
# Move the class attribute to the code element to keep consistency.
bin = Regex.replace(~r/<pre\s+class=\"([^\"]+)\"><code>/,
bin, "<pre><code class=\"\\1\">")
bin
end
end