Skip to content

Commit a6ed2bf

Browse files
Gazlerfishcakez
authored andcommitted
Add table column alignment in IO.ANSI.Docs (#6230)
This commit adds table alignment in markdown which allows the following for tables: right aligned | center aligned | left aligned -: | :-: | :- a | b | c This will output a table in the following format: right aligned | center aligned | left aligned a | b | c If headings are set, the header line (the line immediately below the headings is ignored when calculating the max width for the columns. All lines are trimmed of additional whitespace leading and trailing whitespace to prevent tables that look like: right aligned | center aligned | left aligned ------------: | :------------: | :- a | b | c right aligned | center aligned | left aligned a | b | c
1 parent 30aabdc commit a6ed2bf

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

lib/elixir/lib/io/ansi/docs.ex

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,11 @@ defmodule IO.ANSI.Docs do
269269

270270
widths =
271271
for line <- lines do
272-
for {_col, length} <- line, do: length
272+
if table_header?(line) do
273+
for _ <- line, do: 0
274+
else
275+
for {_col, length} <- line, do: length
276+
end
273277
end
274278

275279
col_widths = Enum.reduce(widths,
@@ -291,6 +295,7 @@ defmodule IO.ANSI.Docs do
291295
col =
292296
col
293297
|> String.replace("\\\|", "|")
298+
|> String.trim()
294299
|> handle_links
295300
|> handle_inline(options)
296301
{col, length_without_escape(col, 0)}
@@ -306,6 +311,8 @@ defmodule IO.ANSI.Docs do
306311
defp render_table([first, second | rest], widths, options) do
307312
combined = Enum.zip(first, widths)
308313
if table_header?(second) do
314+
alignments = Enum.map(second, &column_alignment/1)
315+
options = Keyword.put_new(options, :alignments, alignments)
309316
draw_table_row(combined, options, :heading)
310317
render_table(rest, widths, options)
311318
else
@@ -323,6 +330,14 @@ defmodule IO.ANSI.Docs do
323330
defp render_table([], _, _),
324331
do: nil
325332

333+
defp column_alignment({line, _}) do
334+
cond do
335+
String.starts_with?(line, ":") and String.ends_with?(line, ":") -> :center
336+
String.ends_with?(line, ":") -> :right
337+
true -> :left
338+
end
339+
end
340+
326341
defp table_header?(row) do
327342
Enum.all?(row, fn {col, _} -> table_header_column?(col) end)
328343
end
@@ -336,10 +351,13 @@ defmodule IO.ANSI.Docs do
336351
defp table_header_contents?(_), do: false
337352

338353
defp draw_table_row(cols_and_widths, options, heading \\ false) do
354+
default_alignments = List.duplicate(:left, length(cols_and_widths))
355+
alignments = Keyword.get(options, :alignments, default_alignments)
356+
339357
columns =
340-
Enum.map_join(cols_and_widths, " | ", fn {{col, length}, width} ->
341-
col <> String.duplicate(" ", width - length)
342-
end)
358+
cols_and_widths
359+
|> Enum.zip(alignments)
360+
|> Enum.map_join(" | ", &generate_table_cell/1)
343361

344362
if heading do
345363
write(:doc_table_heading, columns, options)
@@ -348,6 +366,20 @@ defmodule IO.ANSI.Docs do
348366
end
349367
end
350368

369+
defp generate_table_cell({{{col, length}, width}, :center}) do
370+
pad = if rem(length, 2) == 0, do: 1, else: rem(width, 2)
371+
spaces = div(width, 2) - div(length, 2)
372+
String.duplicate(" ", spaces) <> col <> String.duplicate(" ", spaces + pad)
373+
end
374+
375+
defp generate_table_cell({{{col, length}, width}, :right}) do
376+
String.duplicate(" ", width - length) <> col
377+
end
378+
379+
defp generate_table_cell({{{col, length}, width}, _}) do
380+
col <> String.duplicate(" ", width - length)
381+
end
382+
351383
defp table_line?(line) do
352384
line =~ " | "
353385
end

lib/elixir/test/elixir/io/ansi/docs_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,24 @@ defmodule IO.ANSI.DocsTest do
303303
"\e[7mcolumn 1 | and 2\e[0m\na | b \none | two \n\e[0m"
304304
end
305305

306+
test "table with heading alignment" do
307+
table = """
308+
column 1 | 2 | and three
309+
-------: | :------: | :-----
310+
a | even | c\none | odd | three
311+
"""
312+
313+
expected = """
314+
\e[7m\
315+
column 1 | 2 | and three\e[0m
316+
a | even | c
317+
one | odd | three
318+
\e[0m
319+
""" |> String.trim_trailing
320+
321+
assert format(table) == expected
322+
end
323+
306324
test "table with formatting in cells" do
307325
assert format("`a` | _b_\nc | d") ==
308326
"\e[36ma\e[0m | \e[4mb\e[0m\nc | d\n\e[0m"

0 commit comments

Comments
 (0)