Skip to content

Commit

Permalink
Adding support for rich display of Markdown cells in HTML and LaTeX o…
Browse files Browse the repository at this point in the history
…utput (#2390)
  • Loading branch information
NicholasWMRitchie authored Aug 31, 2020
1 parent 6b0feaa commit 8f7bee2
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 15 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,5 @@

* Documentation is now available also in *Dark* mode
([#2315](https://github.com/JuliaData/DataFrames.jl/pull/2315))
* add rich display support for Markdown cell entries in HTML and LaTeX
([#2346](https://github.com/JuliaData/DataFrames.jl/pull/2346))
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Future = "9fa8497b-333b-5362-9e8d-4d0656e87820"
InvertedIndices = "41ab1584-1d38-5bbf-9106-f11c6c58b48f"
IteratorInterfaceExtensions = "82899510-4779-5014-852e-03e436cf321d"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
PooledArrays = "2dfb63ee-cc39-5dd5-95bd-886bf059d720"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Expand Down
1 change: 1 addition & 0 deletions src/DataFrames.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ using Reexport, SortingAlgorithms, Compat, Unicode, PooledArrays
using Base.Sort, Base.Order, Base.Iterators
using TableTraits, IteratorInterfaceExtensions
import LinearAlgebra: norm
using Markdown

import DataAPI,
DataAPI.All,
Expand Down
22 changes: 17 additions & 5 deletions src/abstractdataframe/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ function _show(io::IO, ::MIME"text/html", df::AbstractDataFrame;
cell_val = df[row, column_name]
if ismissing(cell_val)
write(io, "<td><em>missing</em></td>")
elseif cell_val isa Markdown.MD
write(io, "<td>")
show(io, "text/html", cell_val)
write(io, "</td>")
elseif cell_val isa SHOW_TABULAR_TYPES
write(io, "<td><em>")
cell = sprint(ourshow, cell_val)
Expand Down Expand Up @@ -302,6 +306,8 @@ function _show(io::IO, ::MIME"text/latex", df::AbstractDataFrame;
cell = df[row,col]
if ismissing(cell)
print(io, "\\emph{missing}")
elseif cell isa Markdown.MD
print(io, strip(repr(MIME("text/latex"), cell)))
elseif cell isa SHOW_TABULAR_TYPES
print(io, "\\emph{")
print(io, latex_escape(sprint(ourshow, cell, context=io)))
Expand Down Expand Up @@ -410,17 +416,23 @@ function printtable(io::IO,
quotestr = string(quotemark)
for i in 1:n
for j in 1:p
if ismissing(df[i, j])
cell = df[i, j]
if ismissing(cell)
print(io, missingstring)
elseif isnothing(df[i, j])
elseif isnothing(cell)
print(io, nothingstring)
else
if ! (etypes[j] <: Real)
if cell isa Markdown.MD
print(io, quotemark)
escapedprint(io, df[i, j], quotestr)
r = repr(cell)
escapedprint(io, chomp(r), quotestr)
print(io, quotemark)
elseif ! (etypes[j] <: Real)
print(io, quotemark)
escapedprint(io, cell, quotestr)
print(io, quotemark)
else
print(io, df[i, j])
print(io, cell)
end
end
if j < p
Expand Down
5 changes: 5 additions & 0 deletions src/abstractdataframe/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ ourshow(io::IO, x::Symbol) = ourshow(io, string(x))
ourshow(io::IO, x::Nothing; styled::Bool=false) = ourshow(io, "", styled=styled)
ourshow(io::IO, x::SHOW_TABULAR_TYPES; styled::Bool=false) =
ourshow(io, summary(x), styled=styled)
function ourshow(io::IO, x::Markdown.MD)
r = repr(x)
len = min(length(r, 1, something(findfirst(==('\n'), r), lastindex(r)+1)-1), 32)
return print(io, len < length(r) - 1 ? first(r, len)*'' : first(r, len))
end

# AbstractChar: https://github.com/JuliaLang/julia/pull/34730 (1.5.0-DEV.261)
# Irrational: https://github.com/JuliaLang/julia/pull/34741 (1.5.0-DEV.266)
Expand Down
138 changes: 128 additions & 10 deletions test/io.jl
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
module TestIO

using Test, DataFrames, CategoricalArrays, Dates
using Test, DataFrames, CategoricalArrays, Dates, Markdown

# Test LaTeX export
@testset "LaTeX export" begin
df = DataFrame(A = 1:4,
df = DataFrame(A = Int64.( 1:4 ),
B = ["\$10.0", "M&F", "A~B", "\\alpha"],
C = ["A", "B", "C", "S"],
D = [1.0, 2.0, missing, 3.0],
E = CategoricalArray(["a", missing, "c", "d"]),
F = Vector{String}(undef, 4)
F = Vector{String}(undef, 4),
G = [ md"[DataFrames.jl](http://juliadata.github.io/DataFrames.jl)", md"###A", md"``\frac{A}{B}``", md"*A*b**A**"]
)
str = """
\\begin{tabular}{r|cccccc}
\t& A & B & C & D & E & F\\\\
\\begin{tabular}{r|ccccccc}
\t& A & B & C & D & E & F & G\\\\
\t\\hline
\t& $(Int) & String & String & Float64? & Cat…? & String\\\\
\t& Int64 & String & String & Float64? & Cat…? & String & MD…\\\\
\t\\hline
\t1 & 1 & \\\$10.0 & A & 1.0 & a & \\emph{\\#undef} \\\\
\t2 & 2 & M\\&F & B & 2.0 & \\emph{missing} & \\emph{\\#undef} \\\\
\t3 & 3 & A\\textasciitilde{}B & C & \\emph{missing} & c & \\emph{\\#undef} \\\\
\t4 & 4 & \\textbackslash{}\\textbackslash{}alpha & S & 3.0 & d & \\emph{\\#undef} \\\\
\t1 & 1 & \\\$10.0 & A & 1.0 & a & \\emph{\\#undef} & \\href{http://juliadata.github.io/DataFrames.jl}{DataFrames.jl} \\\\
\t2 & 2 & M\\&F & B & 2.0 & \\emph{missing} & \\emph{\\#undef} & \\#\\#\\#A \\\\
\t3 & 3 & A\\textasciitilde{}B & C & \\emph{missing} & c & \\emph{\\#undef} & \$\\frac{A}{B}\$ \\\\
\t4 & 4 & \\textbackslash{}\\textbackslash{}alpha & S & 3.0 & d & \\emph{\\#undef} & \\emph{A}b\\textbf{A} \\\\
\\end{tabular}
"""

@test repr(MIME("text/latex"), df) == str
@test repr(MIME("text/latex"), eachcol(df)) == str
@test repr(MIME("text/latex"), eachrow(df)) == str
Expand Down Expand Up @@ -130,6 +132,27 @@ end

@test_throws ArgumentError DataFrames._show(stdout, MIME("text/html"),
DataFrame(ones(2,2)), rowid=10)

df = DataFrame(
A=Int64[1,4,9,16],
B = [
md"[DataFrames.jl](http://juliadata.github.io/DataFrames.jl)",
md"###A",
md"``\frac{A}{B}``",
md"*A*b**A**" ]
)

@test repr(MIME("text/html"), df) ==
"<table class=\"data-frame\"><thead><tr><th></th><th>A</th><th>B</th></tr><tr><th></th>" *
"<th>Int64</th><th>MD…</th></tr></thead><tbody><p>4 rows × 2 columns</p><tr><th>1</th>" *
"<td>1</td><td><div class=\"markdown\">" *
"<p><a href=\"http://juliadata.github.io/DataFrames.jl\">DataFrames.jl</a>" *
"</p>\n</div></td></tr><tr><th>2</th><td>4</td><td><div class=\"markdown\">" *
"<p>###A</p>\n</div></td></tr><tr><th>3</th><td>9</td><td><div class=\"markdown\">" *
"<p>&#36;\\frac&#123;A&#125;&#123;B&#125;&#36;</p>\n</div></td></tr><tr><th>4</th>" *
"<td>16</td><td><div class=\"markdown\"><p><em>A</em>b<strong>A</strong></p>"*
"\n</div></td></tr></tbody></table>"

end

# test limit attribute of IOContext is used
Expand Down Expand Up @@ -180,6 +203,101 @@ end
end
end

@testset "Markdown as text/plain and as text/csv" begin
df = DataFrame(
A=Int64[1,4,9,16,25,36,49,64],
B = [
md"[DataFrames.jl](http://juliadata.github.io/DataFrames.jl)",
md"``\frac{x^2}{x^2+y^2}``",
md"# Header",
md"This is *very*, **very**, very, very, very, very, very, very, very long line" ,
md"",
Markdown.parse("∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫αγ∞1∫αγ∞2∫αγ∞3"),
Markdown.parse("∫αγ∞1∫αγ∞\n"*
" * 2∫αγ∞3∫αγ∞4\n"*
" * ∫αγ∞5∫αγ\n"*
" * ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0"),
Markdown.parse("∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫α\n"*
" * γ∞1∫α\n"*
" * γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0"),
]
)
@test sprint(show, "text/plain", df) == """
8×2 DataFrame
│ Row │ A │ B │
│ │ Int64 │ Markdown.MD │
├─────┼───────┼───────────────────────────────────┤
│ 1 │ 1 │ [DataFrames.jl](http://juliadata… │
│ 2 │ 4 │ \$\\frac{x^2}{x^2+y^2}\$
│ 3 │ 9 │ # Header │
│ 4 │ 16 │ This is *very*, **very**, very, … │
│ 5 │ 25 │ │
│ 6 │ 36 │ ∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫α… │
│ 7 │ 49 │ ∫αγ∞1∫αγ∞… │
│ 8 │ 64 │ ∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫α… │"""

@test sprint(show, "text/csv", df) ==
"""
\"A\",\"B\"
1,\"[DataFrames.jl](http://juliadata.github.io/DataFrames.jl)\"
4,\"\$\\\\frac{x^2}{x^2+y^2}\$\"
9,\"# Header\"
16,\"This is *very*, **very**, very, very, very, very, very, very, very long line\"
25,\"\"
36,\"∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫αγ∞1∫αγ∞2∫αγ∞3\"
49,\"∫αγ∞1∫αγ∞\\n\\n * 2∫αγ∞3∫αγ∞4\\n * ∫αγ∞5∫αγ\\n * ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0\"
64,\"∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫α\\n\\n * γ∞1∫α\\n * γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0\"
"""
end

@testset "Markdown as HTML" begin
df = DataFrame(
A=Int64[1,4,9,16,25,36,49,64],
B = [
md"[DataFrames.jl](http://juliadata.github.io/DataFrames.jl)",
md"``\frac{x^2}{x^2+y^2}``",
md"# Header",
md"This is *very*, **very**, very, very, very, very, very, very, very long line" ,
md"",
Markdown.parse("∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0" *
"∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0"),
Markdown.parse("∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ\n"*
" * ∞7∫αγ\n"*
" * ∞8∫αγ\n"*
" * ∞9∫αγ∞0∫α\nγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0"),
Markdown.parse("∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫α\n"*
" * γ∞1∫α\n"*
" * γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0"),
]
)
@test sprint(show,"text/html",df) ==
"<table class=\"data-frame\"><thead>" *
"<tr><th></th><th>A</th><th>B</th></tr>" *
"<tr><th></th><th>Int64</th><th>MD…</th></tr>" *
"</thead>" *
"<tbody>" * "<p>8 rows × 2 columns</p>" *
"<tr><th>1</th><td>1</td><td><div class=\"markdown\">" *
"<p><a href=\"http://juliadata.github.io/DataFrames.jl\">DataFrames.jl</a></p>\n</div></td></tr>" *
"<tr><th>2</th><td>4</td><td><div class=\"markdown\"><p>&#36;\\frac&#123;x^2&#125;&#123;x^2&#43;y^2&#125;&#36;</p>\n</div></td></tr>" *
"<tr><th>3</th><td>9</td><td><div class=\"markdown\"><h1>Header</h1>\n</div></td></tr>" *
"<tr><th>4</th><td>16</td><td><div class=\"markdown\">" *
"<p>This is <em>very</em>, <strong>very</strong>, very, very, very, very, very, very, very long line</p>\n" *
"</div></td></tr>" *
"<tr><th>5</th><td>25</td><td><div class=\"markdown\"></div></td></tr>" *
"<tr><th>6</th><td>36</td><td><div class=\"markdown\">" *
"<p>∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0</p>\n" *
"</div></td></tr>" *
"<tr><th>7</th><td>49</td><td><div class=\"markdown\">" *
"<p>∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ</p>\n<ul>\n<li><p>∞7∫αγ</p>\n</li>\n<li><p>∞8∫αγ</p>\n</li>\n<li><p>∞9∫αγ∞0∫α</p>\n</li>\n</ul>\n<p>γ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0</p>\n" *
"</div></td></tr>" *
"<tr><th>8</th><td>64</td><td><div class=\"markdown\">" *
"<p>∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫α</p>" *
"\n<ul>\n" *
"<li><p>γ∞1∫α</p>\n</li>\n" *
"<li><p>γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0</p>\n</li>\n" *
"</ul>\n" * "</div></td></tr></tbody></table>"
end

@testset "empty data frame and DataFrameRow" begin
df = DataFrame(a = [1,2], b = [1.0, 2.0])

Expand Down

0 comments on commit 8f7bee2

Please sign in to comment.