Packages

**The extended, configurable markdown-like syntax parser, that produces an AST.** Supports the full set of `markdown`, plus extensions (custom markup with a bit of elixir code to handle parsing.) The AST produced is understandable by [`XmlBuilder`](https://github.com/joshnuss/xml_builder).

Current section

Files

Jump to
markright lib markright.ex
Raw

lib/markright.ex

defmodule Markright do
@moduledoc """
Custom syntax `Markdown`-like text processor.
"""
@behaviour Markright.Parser
alias Markright.Continuation, as: Plume
@doc """
Main application helper: call
`Markright.to_ast(input, fn {ast, tail} -> IO.inspect(ast) end)`
to transform the markright into the AST, optionally being called back
on every subsequent transform.
## Examples
iex> Markright.to_ast("Plain string.")
{:article, %{}, [{:p, %{}, "Plain string."}]}
iex> input = "plain *bold* rest!"
iex> Markright.to_ast(input)
{:article, %{}, [{:p, %{}, ["plain ", {:strong, %{}, "bold"}, " rest!"]}]}
iex> input = "plain *bold1* _italic_ *bold2* rest!"
iex> Markright.to_ast(input)
{:article, %{},
[{:p, %{}, ["plain ", {:strong, %{}, "bold1"}, " ", {:em, %{}, "italic"}, " ",
{:strong, %{}, "bold2"}, " rest!"]}]}
iex> input = "plainplainplain *bold1bold1bold1* and *bold21bold21bold21 _italicitalicitalic_ bold22bold22bold22* rest!"
iex> Markright.to_ast(input)
{:article, %{},
[{:p, %{}, ["plainplainplain ", {:strong, %{}, "bold1bold1bold1"}, " and ",
{:strong, %{},
["bold21bold21bold21 ", {:em, %{}, "italicitalicitalic"},
" bold22bold22bold22"]}, " rest!"]}]}
iex> input = "_Please ~use~ love **`Markright`** since it is *great*_!"
iex> Markright.to_ast(input)
{:article, %{},
[{:p, %{}, [
{:em, %{},
["Please ", {:strike, %{}, "use"}, " love ",
{:b, %{}, [{:code, %{}, "Markright"}]}, " since it is ",
{:strong, %{}, "great"}]}, "!"]}]}
iex> input = "Escaped /*asterisk"
iex> Markright.to_ast(input)
{:article, %{}, [{:p, %{}, "Escaped *asterisk"}]}
iex> input = "Escaped \\\\*asterisk 2"
iex> Markright.to_ast(input)
{:article, %{}, [{:p, %{}, "Escaped *asterisk 2"}]}
"""
def to_ast(input, fun_or_collector \\ nil, opts \\ %{})
def to_ast(input, fun, opts)
when is_binary(input) and (is_nil(fun) or is_function(fun)) and is_map(opts) do
with plume <- %Plume{fun: fun},
%Plume{ast: ast} <- Markright.Parsers.Article.to_ast(input, plume),
do: ast
end
def to_ast(input, collector, opts)
when is_binary(input) and is_atom(collector) and is_map(opts) do
apply(collector, :start_link, [])
fun = apply(collector, :on_ast_callback, [])
ast = to_ast(input, fun, opts)
collected = apply(collector, :ast_collected, [])
afterwards = apply(collector, :afterwards, [collected])
{ast, afterwards}
after
apply(collector, :stop, [])
end
@doc """
Most shame part of this package: here we use `Regex` because, you know, fuck Windows.
@fixme
"""
def to_ast_safe_input(input, fun \\ nil, opts \\ %{}) do
to_ast(Regex.replace(~r/\r\n|\r/, input, "\n"), fun, opts)
end
##############################################################################
def main(args) do
args |> parse_args |> process
end
def process([]) do
IO.puts "Usage: markright --file=filename.md"
end
def process(options) do
if File.exists?(options[:file]) do
result = options[:file]
|> File.read!
|> to_ast
|> XmlBuilder.generate
# result = if options[:squeeze], do: result |> String.split |> Enum.join(" "), else: result
if options[:silent], do: result, else: IO.puts(result)
else
IO.puts "Unable to read file #{options[:file]}"
end
end
defp parse_args(args) do
{options, _, _} = OptionParser.parse(args,
switches: [file: :string]
)
options
end
end