diff --git a/README.md b/README.md index 77910525..f557e9f3 100644 --- a/README.md +++ b/README.md @@ -140,8 +140,25 @@ default to left. | Texas | TX | Austin | | Maine | ME | Augusta | -Currently we assume there are always spaces around interior vertical -bars. It isn't clear what the expectation is. +Currently we assume there are always spaces around interior vertical unless +there are exterior bars. + +However in order to be more GFM compatible the `gfm_tables: true` option +can be used to interpret only interior vertical bars as a table if a seperation +line is given, therefor + + Language|Rating + --------|------ + Elixir | awesome + +is a table (iff `gfm_tables: true`) while + + Language|Rating + Elixir | awesome + +never is. + + ### Adding HTML attributes with the IAL extension diff --git a/lib/earmark.ex b/lib/earmark.ex index 608aada7..f04e656e 100644 --- a/lib/earmark.ex +++ b/lib/earmark.ex @@ -113,8 +113,25 @@ defmodule Earmark do | Texas | TX | Austin | | Maine | ME | Augusta | - Currently we assume there are always spaces around interior vertical - bars. It isn't clear what the expectation is. + Currently we assume there are always spaces around interior vertical unless + there are exterior bars. + + However in order to be more GFM compatible the `gfm_tables: true` option + can be used to interpret only interior vertical bars as a table if a seperation + line is given, therefor + + Language|Rating + --------|------ + Elixir | awesome + + is a table (iff `gfm_tables: true`) while + + Language|Rating + Elixir | awesome + + never is. + + ### Adding HTML attributes with the IAL extension diff --git a/lib/earmark/line.ex b/lib/earmark/line.ex index d1df6653..d7b439ba 100644 --- a/lib/earmark/line.ex +++ b/lib/earmark/line.ex @@ -81,7 +81,7 @@ defmodule Earmark.Line do defmodule TableLine do @moduledoc false - defstruct(lnb: 0, line: "", content: "", columns: 0, inside_code: false) + defstruct(lnb: 0, line: "", content: "", columns: 0, inside_code: false, is_header: false, needs_header: false) end defmodule Ial do diff --git a/lib/earmark/line_scanner.ex b/lib/earmark/line_scanner.ex index bcd77e58..5931e000 100644 --- a/lib/earmark/line_scanner.ex +++ b/lib/earmark/line_scanner.ex @@ -184,11 +184,15 @@ defmodule Earmark.LineScanner do |> String.trim("|") columns = split_table_columns(body) - %Line.TableLine{content: line, columns: columns} + %Line.TableLine{content: line, columns: columns, is_header: _determine_if_header(columns)} line =~ ~r/ \s \| \s /x -> columns = split_table_columns(line) - %Line.TableLine{content: line, columns: columns} + %Line.TableLine{content: line, columns: columns, is_header: _determine_if_header(columns)} + + line =~ ~r/ \| /x && options.gfm_tables -> + columns = split_table_columns(line) + %Line.TableLine{content: line, columns: columns, is_header: _determine_if_header(columns), needs_header: true} match = Regex.run(~r/^(=|-)+\s*$/, line) -> [_, type] = match @@ -229,6 +233,11 @@ defmodule Earmark.LineScanner do |> Enum.into(MapSet.new()) defp block_tag?(tag), do: MapSet.member?(@block_tags, tag) + @column_rgx ~r{\A[\s|:-]+\z} + defp _determine_if_header(columns) do + columns + |> Enum.all?(fn col -> Regex.run(@column_rgx, col) end) + end defp split_table_columns(line) do line |> String.split(~r{(?\n\n\nab\n\n\n\nde\n\n\n" + messages = [] + + assert as_html(markdown, gfm_tables: true) == {:ok, html, messages} + end + + test "do not need spaces around mid `\|` but w/o gfm_tables this is no good" do + markdown = "a|b\n-|-\nd|e\n" + html = "

a|b\n-|-\nd|e

\n" + messages = [] + + assert as_html(markdown, gfm_tables: false) == {:ok, html, messages} + end + test "however a header line needs to be used" do + markdown = "a|b\nd|e\n" + html = "

a|b\nd|e

\n" + messages = [] + + assert as_html(markdown, gfm_tables: true) == {:ok, html, messages} + end + end end # SPDX-License-Identifier: Apache-2.0 diff --git a/test/support/html1_helpers.ex b/test/support/html1_helpers.ex index e61913ce..937fa783 100644 --- a/test/support/html1_helpers.ex +++ b/test/support/html1_helpers.ex @@ -9,9 +9,9 @@ defmodule Support.Html1Helpers do end - def construct(constructions) do + def construct(constructions, indent \\ 2) do result = - _construct(constructions, 0, []) |> IO.iodata_to_binary + _construct(constructions, 0, [], indent) |> IO.iodata_to_binary if System.get_env("DEBUG") do IO.inspect({:constructed, result}) end @@ -34,71 +34,71 @@ defmodule Support.Html1Helpers do {:th, ~s{style="text-align: #{style};"}, content} end - defp _construct(constructions, indent, open) - defp _construct([], _indent, []), do: [] - defp _construct([], indent, [open|rest]) do - [_indent(indent - 2), "\n", _construct([], indent - 2, rest) ] + defp _construct(constructions, indent, open, iby) + defp _construct([], _indent, [], _iby), do: [] + defp _construct([], indent, [open|rest], iby) do + [_indent(indent - iby), "\n", _construct([], indent - iby, rest, iby) ] end - defp _construct([:POP|rest], indent, [tag|rest1]) do - [_indent(indent-2), "\n", _construct(rest, indent - 2, rest1)] + defp _construct([:POP|rest], indent, [tag|rest1], iby) do + [_indent(indent-iby), "\n", _construct(rest, indent - iby, rest1, iby)] end - defp _construct(head, indent, open) when is_tuple(head) do - _construct([head], indent, open) + defp _construct(head, indent, open, iby) when is_tuple(head) do + _construct([head], indent, open, iby) end - defp _construct([:br | rest], indent, open) do - _void_tag("
\n", rest, indent, open) + defp _construct([:br | rest], indent, open, iby) do + _void_tag("
\n", rest, indent, open, iby) end - defp _construct([:hr | rest], indent, open) do - _void_tag("
\n", rest, indent, open) + defp _construct([:hr | rest], indent, open, iby) do + _void_tag("
\n", rest, indent, open, iby) end - defp _construct([:wbr | rest], indent, open) do - _void_tag("\n", rest, indent, open) + defp _construct([:wbr | rest], indent, open, iby) do + _void_tag("\n", rest, indent, open, iby) end - defp _construct([{:area, atts} | rest], indent, open) do - _void_tag_with_atts("", "\n", _construct(rest, indent + 2, [tag | open])] + defp _construct([tag | rest], indent, open, iby) when is_atom(tag) do + [_indent(indent), "<", to_string(tag), ">", "\n", _construct(rest, indent + iby, [tag | open], iby)] end - defp _construct([content|rest], indent, open) when is_binary(content) do - [_indent(indent), content, "\n", _construct(rest, indent, open)] + defp _construct([content|rest], indent, open, iby) when is_binary(content) do + [_indent(indent), content, "\n", _construct(rest, indent, open, iby)] end - defp _construct([{tag, content}|rest], indent, open) when is_tuple(content), do: _construct([{tag, nil, content}|rest], indent, open) - defp _construct([{tag, content}|rest], indent, open) when is_list(content), do: _construct([{tag, nil, content}|rest], indent, open) - defp _construct([{tag, atts}|rest], indent, open) do - [_indent(indent), "<", to_string(tag), " ", atts, ">", "\n", _construct(rest, indent + 2, [tag | open])] + defp _construct([{tag, content}|rest], indent, open, iby) when is_tuple(content), do: _construct([{tag, nil, content}|rest], indent, open, iby) + defp _construct([{tag, content}|rest], indent, open, iby) when is_list(content), do: _construct([{tag, nil, content}|rest], indent, open, iby) + defp _construct([{tag, atts}|rest], indent, open, iby) do + [_indent(indent), "<", to_string(tag), " ", atts, ">", "\n", _construct(rest, indent + iby, [tag | open], iby)] end - defp _construct([{tag, atts, content}|rest], indent, open) when is_binary(content) do - _construct([{tag, atts, [content]}|rest], indent, open) + defp _construct([{tag, atts, content}|rest], indent, open, iby) when is_binary(content) do + _construct([{tag, atts, [content]}|rest], indent, open, iby) end - defp _construct([{tag, nil, content}|rest], indent, open) do + defp _construct([{tag, nil, content}|rest], indent, open, iby) do [_indent(indent), "<", to_string(tag), ">", "\n", - _construct(content, indent + 2, []), + _construct(content, indent + iby, [], iby), _indent(indent), "\n", - _construct(rest, indent, open)] + _construct(rest, indent, open, iby)] end - defp _construct([{tag, atts, content}|rest], indent, open) do + defp _construct([{tag, atts, content}|rest], indent, open, iby) do [_indent(indent), "<", to_string(tag), " ", atts, ">", "\n", - _construct(content, indent + 2, []), + _construct(content, indent + iby, [], iby), _indent(indent), "\n", - _construct(rest, indent, open)] + _construct(rest, indent, open, iby)] end defp _indent(n), do: Stream.cycle([" "]) |> Enum.take(n) - defp _void_tag( tag, rest, indent, open) do - [_indent(indent), tag, _construct(rest, indent, open)] + defp _void_tag( tag, rest, indent, open, iby) do + [_indent(indent), tag, _construct(rest, indent, open, iby)] end - defp _void_tag_with_atts(tag, atts, rest, indent, open) do - [_indent(indent), tag, atts, " />", "\n", _construct(rest, indent, open)] + defp _void_tag_with_atts(tag, atts, rest, indent, open, iby) do + [_indent(indent), tag, atts, " />", "\n", _construct(rest, indent, open, iby)] end end