Skip to content
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

I249 gfm lists #330

Merged
merged 28 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4e104e4
Adding performance test for lists
RobertDober Mar 16, 2020
fba8ba4
interesting experiment with specific parsing of paragraphs for list i…
RobertDober Feb 21, 2020
b686557
First parse all consecutive list items, then append the resulting lis…
RobertDober Feb 5, 2020
ab229e3
First parse all consecutive list items, then append the resulting lis…
RobertDober Feb 23, 2020
a1ccaf4
GFM Compatibility for Lists ok with the exception of looseness which …
RobertDober Feb 24, 2020
f20fe93
GFM Compatibility for Lists ok with the exception of looseness which …
RobertDober Feb 25, 2020
8411f18
GFM Compatibility for Lists ok with the exception of looseness which …
RobertDober Feb 26, 2020
c2d47c3
Ready for looseness implementation
RobertDober Feb 26, 2020
69e5280
Working on looseness WIP [amend-me]
RobertDober Feb 28, 2020
32b6ded
Compromise solution for looseness
RobertDober Mar 2, 2020
9127d91
as_html* uses as_ast now...
RobertDober Mar 2, 2020
42012de
as_html* uses as_ast now... [amend-me]
RobertDober Mar 11, 2020
e2dde8a
as_html* uses as_ast now...
RobertDober Mar 13, 2020
ce197c0
as_html* uses as_ast now...
RobertDober Mar 14, 2020
003dfdd
as_html* uses as_ast now...
RobertDober Mar 14, 2020
10fae2b
as_html* uses as_ast now...
RobertDober Mar 14, 2020
7ead3d0
as_html* uses as_ast now...
RobertDober Mar 15, 2020
b891360
as_html* uses as_ast now...
RobertDober Mar 15, 2020
adf0036
as_html* uses as_ast now...
RobertDober Mar 15, 2020
15a29e6
as_html* uses as_ast now...
RobertDober Mar 15, 2020
7a25517
as_html* uses as_ast now...
RobertDober Mar 15, 2020
6db222d
as_html* uses as_ast now...
RobertDober Mar 15, 2020
704812e
as_html* uses as_ast now... [amend-me]
RobertDober Mar 16, 2020
13e9735
as_html* uses as_ast now...
RobertDober Mar 16, 2020
d68d472
as_html* uses as_ast now...
RobertDober Mar 16, 2020
301554d
as_html* uses as_ast now...
RobertDober Mar 16, 2020
57f7336
as_html* uses as_ast now...
RobertDober Mar 17, 2020
0215195
Fixes: #249;
RobertDober Mar 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
105 changes: 44 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
* [Dependency](#dependency)
* [Usage](#usage)
* [Details](#details)
* [`Earmark.as_html/2`](#earmarkas_html2)
* [`Earmark.as_ast/2`](#earmarkas_ast2)
* [`Earmark.as_html/2`](#earmarkas_html2)
* [`Earmark.Transform.transform/2`](#earmarktransformtransform2)
* [Contributing](#contributing)
* [Author](#author)
Expand All @@ -30,6 +30,17 @@

### API

Earmark now exposes a welldefined and stable Abstratc Syntax Tree

#### Earmark.as_ast

The function is described below and the other two API functions `as_html` and `as_html!` are now based upon
the structure of the result of `as_ast`.

{:ok, ast, []} = Earmark.as_ast(markdown)
{:ok, ast, deprecation_messages} = Earmark.as_ast(markdown)
{:error, ast, error_messages} = Earmark.as_ast(markdown)

#### Earmark.as_html

{:ok, html_doc, []} = Earmark.as_html(markdown)
Expand All @@ -40,24 +51,16 @@

html_doc = Earmark.as_html!(markdown, options)

All messages are printed to _stderr_.

#### Options

Options can be passed into `as_html/2` or `as_html!/2` according to the documentation.

html_doc = Earmark.as_html!(markdown)
html_doc = Earmark.as_html!(markdown, options)

Formats the error_messages returned by `as_html` and adds the filename to each.
Then prints them to stderr and just returns the html_doc

#### NEW and EXPERIMENTAL: `Earmark.as_ast`
#### Options

Although well tested the way the exposed AST will look in future versions may change, a stable
API is expected for Earmark v1.6, when the rendered HTML shall be derived from the ast too.
Options can be passed into `as_ast/2`as well as `as_html/2` or `as_html!/2` according to the documentation.

More details can be found in the function's description below.
{status, html_doc, errors} = Earmark.as_html(markdown, options)
html_doc = Earmark.as_html!(markdown, options)
{status, ast, errors} = Earmark.as_ast(markdown, options)

### Command line

Expand Down Expand Up @@ -93,7 +96,7 @@ GFM is supported by default, however as GFM is a moving target and all GFM exten
#### Strike Through

iex(1)> Earmark.as_html! ["~~hello~~"]
"<p><del>hello</del></p>\n"
"<p>\n <del>\n hello\n </del>\n</p>\n"

#### Syntax Highlighting

Expand Down Expand Up @@ -198,25 +201,25 @@ format.

iex(4)> markdown = "[link](url) {: .classy}"
...(4)> Earmark.as_html(markdown)
{ :ok, "<p><a href=\"url\" class=\"classy\">link</a></p>\n", []}
{ :ok, "<p>\n <a class=\"classy\" href=\"url\">\n link\n </a>\n</p>\n", []}

For both cases, malformed attributes are ignored and warnings are issued.

iex(5)> [ "Some text", "{:hello}" ] |> Enum.join("\n") |> Earmark.as_html()
{:error, "<p>Some text</p>\n", [{:warning, 2,"Illegal attributes [\"hello\"] ignored in IAL"}]}
{:error, "<p>\n Some text\n</p>\n", [{:warning, 2,"Illegal attributes [\"hello\"] ignored in IAL"}]}

It is possible to escape the IAL in both forms if necessary

iex(6)> markdown = "[link](url)\\{: .classy}"
...(6)> Earmark.as_html(markdown)
{:ok, "<p><a href=\"url\">link</a>{: .classy}</p>\n", []}
{:ok, "<p>\n <a href=\"url\">\n link\n </a>\n {: .classy}\n</p>\n", []}

This of course is not necessary in code blocks or text lines
containing an IAL-like string, as in the following example

iex(7)> markdown = "hello {:world}"
...(7)> Earmark.as_html!(markdown)
"<p>hello {:world}</p>\n"
"<p>\n hello {:world}\n</p>\n"

## Limitations

Expand Down Expand Up @@ -306,6 +309,28 @@ and are to serve the produced HTML on the Web.

## Details

## `Earmark.as_ast/2`

<!-- BEGIN inserted functiondoc Earmark.as_ast/2 -->
iex(9)> markdown = "My `code` is **best**"
...(9)> {:ok, ast, []} = Earmark.as_ast(markdown)
...(9)> ast
[{"p", [], ["My ", {"code", [{"class", "inline"}], ["code"]}, " is ", {"strong", [], ["best"]}]}]

Options are passes like to `as_html`, some do not have an effect though (e.g. `smartypants`) as formatting and escaping is not done
for the AST.

iex(10)> markdown = "```elixir\nIO.puts 42\n```"
...(10)> {:ok, ast, []} = Earmark.as_ast(markdown, code_class_prefix: "lang-")
...(10)> ast
[{"pre", [], [{"code", [{"class", "elixir lang-elixir"}], ["IO.puts 42"]}]}]

**Rationale**:

The AST is exposed in the spirit of [Floki's](https://hex.pm/packages/floki).

<!-- END inserted functiondoc Earmark.as_ast/2 -->

## `Earmark.as_html/2`

<!-- BEGIN inserted functiondoc Earmark.as_html/2 -->
Expand Down Expand Up @@ -370,48 +395,6 @@ Where `html_doc` is an HTML representation of the markdown document and

<!-- END inserted functiondoc Earmark.as_html/2 -->

## `Earmark.as_ast/2`

<!-- BEGIN inserted functiondoc Earmark.as_ast/2 -->
**EXPERIMENTAL**, but well tested, just expect API changes in the 1.4 branch

iex(9)> markdown = "My `code` is **best**"
...(9)> {:ok, ast, []} = Earmark.as_ast(markdown)
...(9)> ast
[{"p", [], ["My ", {"code", [{"class", "inline"}], ["code"]}, " is ", {"strong", [], ["best"]}]}]

Options are passes like to `as_html`, some do not have an effect though (e.g. `smartypants`) as formatting and escaping is not done
for the AST.

iex(10)> markdown = "```elixir\nIO.puts 42\n```"
...(10)> {:ok, ast, []} = Earmark.as_ast(markdown, code_class_prefix: "lang-")
...(10)> ast
[{"pre", [], [{"code", [{"class", "elixir lang-elixir"}], ["IO.puts 42"]}]}]

**Rationale**:

The AST is exposed in the spirit of [Floki's](https://hex.pm/packages/floki) there might be some subtle WS
differences and we chose to **always** have tripples, even for comments.
We also do return a list for a single node


Floki.parse("<!-- comment -->")
{:comment, " comment "}

Earmark.as_ast("<!-- comment -->")
{:ok, [{:comment, [], [" comment "]}], []}

Therefore `as_ast` is of the following type

@typep tag :: String.t | :comment
@typep att :: {String.t, String.t}
@typep atts :: list(att)
@typep node :: String.t | {tag, atts, ast}

@type ast :: list(node)

<!-- END inserted functiondoc Earmark.as_ast/2 -->

## `Earmark.Transform.transform/2`

<!-- BEGIN inserted functiondoc Earmark.Transform.transform/2 -->
Expand Down
8 changes: 4 additions & 4 deletions README.template
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@

## Details

## `Earmark.as_html/2`

%functiondoc Earmark.as_html/2

## `Earmark.as_ast/2`

%functiondoc Earmark.as_ast/2

## `Earmark.as_html/2`

%functiondoc Earmark.as_html/2

## `Earmark.Transform.transform/2`

%functiondoc Earmark.Transform.transform/2
Expand Down
2 changes: 0 additions & 2 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
- [320 Nested Blockquotes](https://github.com/pragdave/earmark/issues/320)




# 1.4.3 2019/11/23

- [309 fenced code allows for more than 3 backticks/tildes now](https://github.com/pragdave/earmark/issues/309)
Expand Down
90 changes: 27 additions & 63 deletions lib/earmark.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ defmodule Earmark do

### API

Earmark now exposes a welldefined and stable Abstratc Syntax Tree

#### Earmark.as_ast

The function is described below and the other two API functions `as_html` and `as_html!` are now based upon
the structure of the result of `as_ast`.

{:ok, ast, []} = Earmark.as_ast(markdown)
{:ok, ast, deprecation_messages} = Earmark.as_ast(markdown)
{:error, ast, error_messages} = Earmark.as_ast(markdown)

#### Earmark.as_html

{:ok, html_doc, []} = Earmark.as_html(markdown)
Expand All @@ -13,24 +24,16 @@ defmodule Earmark do

html_doc = Earmark.as_html!(markdown, options)

All messages are printed to _stderr_.

#### Options

Options can be passed into `as_html/2` or `as_html!/2` according to the documentation.

html_doc = Earmark.as_html!(markdown)
html_doc = Earmark.as_html!(markdown, options)

Formats the error_messages returned by `as_html` and adds the filename to each.
Then prints them to stderr and just returns the html_doc

#### NEW and EXPERIMENTAL: `Earmark.as_ast`
#### Options

Although well tested the way the exposed AST will look in future versions may change, a stable
API is expected for Earmark v1.6, when the rendered HTML shall be derived from the ast too.
Options can be passed into `as_ast/2`as well as `as_html/2` or `as_html!/2` according to the documentation.

More details can be found in the function's description below.
{status, html_doc, errors} = Earmark.as_html(markdown, options)
html_doc = Earmark.as_html!(markdown, options)
{status, ast, errors} = Earmark.as_ast(markdown, options)

### Command line

Expand Down Expand Up @@ -66,7 +69,7 @@ defmodule Earmark do
#### Strike Through

iex(1)> Earmark.as_html! ["~~hello~~"]
"<p><del>hello</del></p>\\n"
"<p>\\n <del>\\n hello\\n </del>\\n</p>\\n"

#### Syntax Highlighting

Expand Down Expand Up @@ -171,25 +174,25 @@ defmodule Earmark do

iex(4)> markdown = "[link](url) {: .classy}"
...(4)> Earmark.as_html(markdown)
{ :ok, "<p><a href=\\"url\\" class=\\"classy\\">link</a></p>\\n", []}
{ :ok, "<p>\\n <a class=\\"classy\\" href=\\"url\\">\\n link\\n </a>\\n</p>\\n", []}

For both cases, malformed attributes are ignored and warnings are issued.

iex(5)> [ "Some text", "{:hello}" ] |> Enum.join("\\n") |> Earmark.as_html()
{:error, "<p>Some text</p>\\n", [{:warning, 2,"Illegal attributes [\\"hello\\"] ignored in IAL"}]}
{:error, "<p>\\n Some text\\n</p>\\n", [{:warning, 2,"Illegal attributes [\\"hello\\"] ignored in IAL"}]}

It is possible to escape the IAL in both forms if necessary

iex(6)> markdown = "[link](url)\\\\{: .classy}"
...(6)> Earmark.as_html(markdown)
{:ok, "<p><a href=\\"url\\">link</a>{: .classy}</p>\\n", []}
{:ok, "<p>\\n <a href=\\"url\\">\\n link\\n </a>\\n {: .classy}\\n</p>\\n", []}

This of course is not necessary in code blocks or text lines
containing an IAL-like string, as in the following example

iex(7)> markdown = "hello {:world}"
...(7)> Earmark.as_html!(markdown)
"<p>hello {:world}</p>\\n"
"<p>\\n hello {:world}\\n</p>\\n"

## Limitations

Expand Down Expand Up @@ -278,7 +281,7 @@ defmodule Earmark do

alias Earmark.Error
alias Earmark.Options
import Earmark.Message, only: [emit_messages: 1, sort_messages: 1]
import Earmark.Message, only: [emit_messages: 2, sort_messages: 1]

@doc """
Given a markdown document (as either a list of lines or
Expand Down Expand Up @@ -347,23 +350,11 @@ defmodule Earmark do
end

def as_html(lines, options) do
{context, html} = _as_html(lines, options)
messages = sort_messages(context)

status =
case Enum.any?(messages, fn {severity, _, _} ->
severity == :error || severity == :warning
end) do
true -> :error
_ -> :ok
end

{status, html, messages}
{status, ast, messages} = as_ast(lines, options)
{status, Earmark.Transform.transform(ast, options), messages}
end

@doc """
**EXPERIMENTAL**, but well tested, just expect API changes in the 1.4 branch

iex(9)> markdown = "My `code` is **best**"
...(9)> {:ok, ast, []} = Earmark.as_ast(markdown)
...(9)> ast
Expand All @@ -379,25 +370,7 @@ defmodule Earmark do

**Rationale**:

The AST is exposed in the spirit of [Floki's](https://hex.pm/packages/floki) there might be some subtle WS
differences and we chose to **always** have tripples, even for comments.
We also do return a list for a single node


Floki.parse("<!-- comment -->")
{:comment, " comment "}

Earmark.as_ast("<!-- comment -->")
{:ok, [{:comment, [], [" comment "]}], []}

Therefore `as_ast` is of the following type

@typep tag :: String.t | :comment
@typep att :: {String.t, String.t}
@typep atts :: list(att)
@typep node :: String.t | {tag, atts, ast}

@type ast :: list(node)
The AST is exposed in the spirit of [Floki's](https://hex.pm/packages/floki).
"""
def as_ast(lines, options \\ %Options{})
def as_ast(lines, options) when is_list(options) do
Expand Down Expand Up @@ -429,20 +402,11 @@ defmodule Earmark do
as_html!(lines, struct(Options, options))
end
def as_html!(lines, options = %Options{}) do
{context, html} = _as_html(lines, options)
emit_messages(context)
{_status, html, messages} = as_html(lines, options)
emit_messages(messages, options)
html
end

defp _as_html(lines, options) do
{blocks, context} = Earmark.Parser.parse_markdown(lines, options)

case blocks do
[] -> {context, ""}
_ -> options.renderer.render(blocks, context)
end
end

defp _as_ast(lines, options) do
{blocks, context} = Earmark.Parser.parse_markdown(lines, options)
Earmark.AstRenderer.render(blocks, context)
Expand Down
2 changes: 1 addition & 1 deletion lib/earmark/ast/renderer/footnote_list_renderer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule Earmark.Ast.Renderer.FootnoteListRenderer do
end

defp _render_footnote_backlink(%{class: _, href: _, title: _}=atts) do
[{"a", merge_attrs(atts), ["\u21a9"]}]
[{"a", merge_attrs(atts), ["&#x21A9;"]}]
end


Expand Down