-
Notifications
You must be signed in to change notification settings - Fork 367
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
Adding support for rich display of Markdown cells #2346
Changes from 6 commits
273bbb3
e72d033
a1d9619
57a3b58
531e089
ea571e6
c53598e
5df96a6
a391912
5f5f121
73a9010
3a85fbf
8928695
1a0fd3b
c1e38d4
be35852
7d4e5d0
2f5c2d3
c05e3ea
b90696d
606f3eb
174d632
5414031
17b6e1b
7a8c0de
c112ef4
8a04964
9fbe264
43a047c
8ff0ff0
e67308e
ab4209c
4993be3
feb0f5d
bbd5143
088c894
6a2ed0d
b3866e5
0f6fb38
1342b4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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) | ||||||
|
@@ -303,6 +307,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 | ||||||
show(io, "text/latex", cell) | ||||||
elseif cell isa SHOW_TABULAR_TYPES | ||||||
print(io, "\\emph{") | ||||||
print(io, latex_escape(sprint(ourshow, cell, context=io))) | ||||||
|
@@ -417,7 +423,12 @@ function printtable(io::IO, | |||||
elseif isnothing(df[i, j]) | ||||||
print(io, nothingstring) | ||||||
else | ||||||
if ! (etypes[j] <: Real) | ||||||
if df[i,j] isa Markdown.MD | ||||||
print(io, quotemark) | ||||||
r=repr(df[i,j]) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
escapedprint(io, chomp(r), quotestr) | ||||||
print(io, quotemark) | ||||||
elseif ! (etypes[j] <: Real) | ||||||
print(io, quotemark) | ||||||
escapedprint(io, df[i, j], quotestr) | ||||||
print(io, quotemark) | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't abbreviate in other formats, so why do it for Markdown? Regarding newlines, I would just print them as We need a general mechanism to abbreviate too long cells, but this should be applied systematically disregarding the column type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would keep it here for the time being as In general maybe we will switch to PrettyTables.jl which actually handles this already. These changes will be possible to implement after 1.0 release if needed, as display changes are not considered to be breaking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, let's do that. Though do you think it would make sense to apply this 32-char limit to other types as well? I guess it can be wasteful when there are only a few columns and that more space is available, but it's hard to handle. How does PrettyTables handle it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But how wide terminal displays do you expect people have? PrettyTables.jl behaves like this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum, currently we can set the size of a column or its minimum size. However, I think it will be nice to have an option to set the maximum size also. In this case, we can set it to 32. Thus, columns that can be displayed with less characters will be, but columns larger than this will be cropped just like this: julia> pretty_table(df, alignment = :l, columns_width = [10,10])
┌────────────┬────────────┐
│ a │ b │
│ String │ String │
├────────────┼────────────┤
│ aaaaaaaaa… │ bbbbbbbbb… │
└────────────┴────────────┘ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! It is in julia> df = DataFrame(a="a"^5, b="b"^10, c="c"^15, d = "d"^20);
julia> pretty_table(df, alignment = :l, maximum_columns_width = 10)
┌────────┬────────────┬────────────┬────────────┐
│ a │ b │ c │ d │
│ String │ String │ String │ String │
├────────┼────────────┼────────────┼────────────┤
│ aaaaa │ bbbbbbbbbb │ ccccccccc… │ ddddddddd… │
└────────┴────────────┴────────────┴────────────┘ Off-topic: I think I just need to add 2 more features before starting to testing good configurations to print DataFrames! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, then maybe we should set a limit to 32 characters for all types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @NicholasWMRitchie - would you be willing to make this change everywhere in this PR or we leave it for a follow up PR (I can do it) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So what's the conclusion on this? |
||||||
return print(io, len < length(r) - 1 ? first(r, len)*"…" : first(r, len)) | ||||||
bkamins marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using chars should give a slightly more efficient code:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
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) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
module TestIO | ||
|
||
using Test, DataFrames, CategoricalArrays, Dates | ||
using Test, DataFrames, CategoricalArrays, Dates, Markdown | ||
|
||
# Test LaTeX export | ||
@testset "LaTeX export" begin | ||
|
@@ -9,20 +9,21 @@ using Test, DataFrames, CategoricalArrays, Dates | |
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\\\\ | ||
\t\\hline | ||
\t& $(Int) & String & String & Float64? & Cat…? & String\\\\ | ||
\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} \\\\ | ||
\\end{tabular} | ||
""" | ||
str = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you keep the triple quoted form? It's more readable. Same below. |
||
"\\begin{tabular}{r|ccccccc}\n" * | ||
"\t& A & B & C & D & E & F & G\\\\\n" * | ||
"\t\\hline\n\t& "*repr(Int)*" & String & String & Float64? & Cat…? & String & MD…\\\\\n" * | ||
"\t\\hline\n" * | ||
"\t1 & 1 & \\\$10.0 & A & 1.0 & a & \\emph{\\#undef} & \\href{http://juliadata.github.io/DataFrames.jl}{DataFrames.jl}\n\n \\\\\n" * | ||
"\t2 & 2 & M\\&F & B & 2.0 & \\emph{missing} & \\emph{\\#undef} & \\#\\#\\#A\n\n \\\\\n" * | ||
"\t3 & 3 & A\\textasciitilde{}B & C & \\emph{missing} & c & \\emph{\\#undef} & \$\\frac{A}{B}\$\n\n \\\\\n" * | ||
"\t4 & 4 & \\textbackslash{}\\textbackslash{}alpha & S & 3.0 & d & \\emph{\\#undef} & \\emph{A}b\\textbf{A}\n\n \\\\\n" * | ||
"\\end{tabular}\n" | ||
|
||
|
||
@test repr(MIME("text/latex"), df) == str | ||
@test repr(MIME("text/latex"), eachcol(df)) == str | ||
@test repr(MIME("text/latex"), eachrow(df)) == str | ||
|
@@ -130,6 +131,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) == | ||
NicholasWMRitchie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"<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>$\\frac{A}{B}$</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 | ||
|
@@ -180,6 +202,100 @@ 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"), | ||
NicholasWMRitchie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
] | ||
) | ||
@test sprint(show, "text/plain", df) == """ | ||
8×2 DataFrame | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add 8-characters indentation. Same below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one still applies. |
||
│ 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>$\\frac{x^2}{x^2+y^2}$</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]) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since accessing
df[i, j]
is relatively slow, better store it incell = df[i, j]
and use that afterwards as in the HTML method (not sure why we didn't do this here).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see it, nor the other changes you mention below. :-)