From 9d60cf052f16fc3b35392e892d61676300c99bc4 Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Thu, 22 Jan 2015 00:39:47 -0800 Subject: [PATCH 1/7] ENH add markdown Table --- base/markdown/GitHub/GitHub.jl | 84 ++++++++++++++++++++- base/markdown/Julia/Julia.jl | 2 +- base/markdown/render/html.jl | 15 ++++ base/markdown/render/latex.jl | 24 ++++++ base/markdown/render/plain.jl | 39 ++++++++++ base/markdown/render/terminal/formatting.jl | 19 +++++ base/markdown/render/terminal/render.jl | 30 ++++++++ 7 files changed, 208 insertions(+), 5 deletions(-) diff --git a/base/markdown/GitHub/GitHub.jl b/base/markdown/GitHub/GitHub.jl index f8ac635431b11..0296bf3eb4119 100644 --- a/base/markdown/GitHub/GitHub.jl +++ b/base/markdown/GitHub/GitHub.jl @@ -33,9 +33,85 @@ function github_paragraph(stream::IO, md::MD, config::Config) return true end -# TODO: tables +typealias Row Vector -@flavor github [list, indentcode, blockquote, fencedcode, hashheader, github_paragraph, +type Table + rows::Vector{Row} + align +end + +function github_table(stream::IO, md::MD, config::Config) + withstream(stream) do + rows = Row[] + n = 0 + align = :r # default is to align right + while !eof(stream) + n += 1 + pos = position(stream) + skipwhitespace(stream) + line = readline(stream) |> chomp + + if n == 1 + pipe_border = line[1] == '|' + if !('|' in line) + return false + end + end + + row = map(strip, split(line, "|")) + if pipe_border + if row[1] == row[end] == "" + row = row[2:end-1] + else + return false + end + end + + if n == 2 && all(['-' in r && issubset(Set(r), Set(" -:")) + for r in row]) + # handle possible --- line + align = Symbol[] + for r in row + if r[1] == ':' + if r[end] == ':' + push!(align, :c) + else + push!(align, :l) + end + else + if r[end] == ':' + push!(align, :r) + else + # default is align right + push!(align, :r) + end + end + end + + elseif n == 1 || length(rows[1]) == length(row) + push!(rows, Row(map(x -> parseinline(IOBuffer(_full(x)), config), row))) + elseif length(row) > 1 + seek(stream, pos) + break + else + seek(stream, 0) + return false + end + end + if length(rows) < 2 + seek(stream, 0) + return false + end + push!(md, Table(rows, align)) + return true + end +end + +_full{T}(s::SubString{T}) = convert(T, s) +_full(s::AbstractString) = s + +@flavor github [list, indentcode, blockquote, fencedcode, hashheader, + github_paragraph, github_table, + linebreak, espaces, en_dash, inline_code, asterisk_bold, + asterisk_italic, image, link] - linebreak, escapes, en_dash, inline_code, asterisk_bold, asterisk_italic, - image, link] diff --git a/base/markdown/Julia/Julia.jl b/base/markdown/Julia/Julia.jl index 20f89000836d4..ed9220e6807ca 100644 --- a/base/markdown/Julia/Julia.jl +++ b/base/markdown/Julia/Julia.jl @@ -7,7 +7,7 @@ We start by borrowing GitHub's `fencedcode` extension – more to follow. include("interp.jl") -@flavor julia [blocktex, blockinterp, hashheader, list, indentcode, fencedcode, +@flavor julia [github_table, blocktex, blockinterp, hashheader, list, indentcode, fencedcode, blockquote, paragraph, linebreak, escapes, latex, interp, en_dash, inline_code, asterisk_bold, diff --git a/base/markdown/render/html.jl b/base/markdown/render/html.jl index 5301f207fa33c..5a437f8ed8185 100644 --- a/base/markdown/render/html.jl +++ b/base/markdown/render/html.jl @@ -56,6 +56,21 @@ function html(io::IO, md::List) end end +function html(io::IO, md::Table) + withtag(io, :table) do + for (i, row) in enumerate(md.rows) + withtag(io, :tr) do + for c in md.rows[i] + t = i == 1 ? :th : :td + withtag(io, t) do + htmlinline(io, c) + end + end + end + end + end +end + html(io::IO, x) = tohtml(io, x) # Inline elements diff --git a/base/markdown/render/latex.jl b/base/markdown/render/latex.jl index 4fb5010ce98b1..397a4eb440e57 100644 --- a/base/markdown/render/latex.jl +++ b/base/markdown/render/latex.jl @@ -66,6 +66,30 @@ function writemime(io::IO, ::MIME"text/latex", md::List) end end +function writemime(io::IO, ::MIME"text/latex", md::Table) + wrapblock(io, "tabular") do + if typeof(md.align) == Symbol + align = string(md.align) ^ length(md.rows[1]) + else + align = md.align + end + println(io, "{$(join(align, " | "))}") + for (i, row) in enumerate(md.rows) + for (j, cell) in enumerate(row) + if j != 1 + print(io, " & ") + end + latex_inline(io, cell) + end + println(io, " \\\\") + if i == 1 + println("\\hline") + end + end + end +end + + # Inline elements function writemime(io::IO, ::MIME"text/latex", md::Plain) diff --git a/base/markdown/render/plain.jl b/base/markdown/render/plain.jl index 630a80a09a94c..6859b5294f8e7 100644 --- a/base/markdown/render/plain.jl +++ b/base/markdown/render/plain.jl @@ -40,6 +40,45 @@ function plaininline(io::IO, br::LineBreak) println(io) end +function plain(io::IO, md::Table) + println("asdsa") + col_widths = reduce(max, map(cell -> map(ansi_length, cell), md.rows)) + + for (n, row) in enumerate(md.rows) + for (i, h) in enumerate(row) + a = typeof(md.align) == Symbol ? md.align : md.align[i] + # TODO use not terminal version of print_align + error("NotImplemented") + print_align(io, h, col_widths[i], a) + print(io, " ") + end + println(io, "") + + if n == 1 + for (j, w) in enumerate(col_widths) + if j != 1 + print(io, "|") + end + a = typeof(md.align) == Symbol ? md.align : md.align[j] + print(io, _dash(w, a) * " ") + end + println(io, "") + end + end +end + +function _dash(width, align) + if align == :l + return ":" * "-" ^ max(1, width - 1) + elseif align == :r + return "-" ^ max(1, width - 1) * ":" + elseif align == :c + return "-" ^ width + else + throw(ArgumentError("Unrecognized alignment $align")) + end +end + plain(io::IO, x) = tohtml(io, x) # Inline elements diff --git a/base/markdown/render/terminal/formatting.jl b/base/markdown/render/terminal/formatting.jl index cf3dacc2b808a..6ee26f743d3b3 100644 --- a/base/markdown/render/terminal/formatting.jl +++ b/base/markdown/render/terminal/formatting.jl @@ -97,6 +97,25 @@ function print_centred(io::IO, s...; columns = 80, width = columns) end end +function print_align(io::IO, text, width, align=:r) + n = ansi_length(text) + if align == :c # center + lpad = div(width - n, 2) + elseif align == :r # right + lpad = width - n + elseif align == :l # left + lpad = 0 + else + throw(ArgumentError("Alignment $align not recognised")) + end + print(io, " " ^ lpad) + for t in text + terminline(io, t) + end + print(io, " " ^ (width - lpad - n)) +end + + function centred(s, columns) pad = div(columns - ansi_length(s), 2) " "^pad * s diff --git a/base/markdown/render/terminal/render.jl b/base/markdown/render/terminal/render.jl index ffff1b570791c..42daf9bc5506e 100644 --- a/base/markdown/render/terminal/render.jl +++ b/base/markdown/render/terminal/render.jl @@ -65,6 +65,36 @@ function term(io::IO, br::LineBreak, columns) println(io) end +function term(io::IO, md::Table, columns) + col_widths = reduce(max, map(cell -> map(ansi_length, cell), md.rows)) + + for (n, row) in enumerate(md.rows) + for (i, h) in enumerate(row) + a = typeof(md.align) == Symbol ? md.align : md.align[i] + print_align(io, h, col_widths[i], a) + print(io, " ") + end + println(io, "") + + if n == 1 + for w in col_widths + print(io, ("-" ^ w) * " ") + end + println(io, "") + end + end +end + +function ansi_length(md::Vector{Any}) + total = 0 + for c in md + total += ansi_length(c) + end + total +end +ansi_length(c::Code) = ansi_length(c.code) +ansi_length(l::Link) = ansi_length(l.text) + term(io::IO, x, _) = writemime(io, MIME"text/plain"(), x) # Inline Content From b4b83240ff46717d40abb5aebcabf1c7fe02ba67 Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Thu, 22 Jan 2015 15:13:35 -0800 Subject: [PATCH 2/7] FIX plain markdown printing, and precedence for Github --- base/markdown/GitHub/GitHub.jl | 2 +- base/markdown/render/plain.jl | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/base/markdown/GitHub/GitHub.jl b/base/markdown/GitHub/GitHub.jl index 0296bf3eb4119..9e47ab04ee4e8 100644 --- a/base/markdown/GitHub/GitHub.jl +++ b/base/markdown/GitHub/GitHub.jl @@ -111,7 +111,7 @@ _full{T}(s::SubString{T}) = convert(T, s) _full(s::AbstractString) = s @flavor github [list, indentcode, blockquote, fencedcode, hashheader, - github_paragraph, github_table, + github_table, github_paragraph, linebreak, espaces, en_dash, inline_code, asterisk_bold, asterisk_italic, image, link] diff --git a/base/markdown/render/plain.jl b/base/markdown/render/plain.jl index 6859b5294f8e7..f5d47d6a44cf3 100644 --- a/base/markdown/render/plain.jl +++ b/base/markdown/render/plain.jl @@ -41,26 +41,26 @@ function plaininline(io::IO, br::LineBreak) end function plain(io::IO, md::Table) - println("asdsa") col_widths = reduce(max, map(cell -> map(ansi_length, cell), md.rows)) for (n, row) in enumerate(md.rows) for (i, h) in enumerate(row) - a = typeof(md.align) == Symbol ? md.align : md.align[i] # TODO use not terminal version of print_align - error("NotImplemented") - print_align(io, h, col_widths[i], a) - print(io, " ") + if i != 1 + print(io, " | ") + end + print(io, " " ^ (col_widths[i] - ansi_length(h))) + plaininline(io, h) end println(io, "") if n == 1 for (j, w) in enumerate(col_widths) if j != 1 - print(io, "|") + print(io, " | ") end a = typeof(md.align) == Symbol ? md.align : md.align[j] - print(io, _dash(w, a) * " ") + print(io, _dash(w, a)) end println(io, "") end @@ -73,7 +73,7 @@ function _dash(width, align) elseif align == :r return "-" ^ max(1, width - 1) * ":" elseif align == :c - return "-" ^ width + return ":" * "-" ^ max(1, width - 2) * ":" else throw(ArgumentError("Unrecognized alignment $align")) end From c752ffd713df6bc16bd9e029958712fabec344b7 Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Sat, 24 Jan 2015 15:14:29 -0800 Subject: [PATCH 3/7] clean up markdown table based on @one-more-minute comments --- base/markdown/GitHub/GitHub.jl | 12 ++----- base/markdown/Julia/Julia.jl | 4 +-- base/markdown/parse/parse.jl | 5 +-- base/markdown/render/html.jl | 2 +- base/markdown/render/plain.jl | 43 ++++++++++++------------- base/markdown/render/terminal/render.jl | 18 ++++++----- 6 files changed, 40 insertions(+), 44 deletions(-) diff --git a/base/markdown/GitHub/GitHub.jl b/base/markdown/GitHub/GitHub.jl index 9e47ab04ee4e8..dd27f753657e0 100644 --- a/base/markdown/GitHub/GitHub.jl +++ b/base/markdown/GitHub/GitHub.jl @@ -33,16 +33,14 @@ function github_paragraph(stream::IO, md::MD, config::Config) return true end -typealias Row Vector - type Table - rows::Vector{Row} + rows::Vector{Vector{Any}} align end function github_table(stream::IO, md::MD, config::Config) withstream(stream) do - rows = Row[] + rows = Any[] n = 0 align = :r # default is to align right while !eof(stream) @@ -89,17 +87,15 @@ function github_table(stream::IO, md::MD, config::Config) end elseif n == 1 || length(rows[1]) == length(row) - push!(rows, Row(map(x -> parseinline(IOBuffer(_full(x)), config), row))) + push!(rows, map(x -> parseinline(x, config), row)) elseif length(row) > 1 seek(stream, pos) break else - seek(stream, 0) return false end end if length(rows) < 2 - seek(stream, 0) return false end push!(md, Table(rows, align)) @@ -107,8 +103,6 @@ function github_table(stream::IO, md::MD, config::Config) end end -_full{T}(s::SubString{T}) = convert(T, s) -_full(s::AbstractString) = s @flavor github [list, indentcode, blockquote, fencedcode, hashheader, github_table, github_paragraph, diff --git a/base/markdown/Julia/Julia.jl b/base/markdown/Julia/Julia.jl index ed9220e6807ca..ef94a63cdd375 100644 --- a/base/markdown/Julia/Julia.jl +++ b/base/markdown/Julia/Julia.jl @@ -7,8 +7,8 @@ We start by borrowing GitHub's `fencedcode` extension – more to follow. include("interp.jl") -@flavor julia [github_table, blocktex, blockinterp, hashheader, list, indentcode, fencedcode, - blockquote, paragraph, +@flavor julia [blocktex, blockinterp, hashheader, list, indentcode, fencedcode, + blockquote, github_table, paragraph, linebreak, escapes, latex, interp, en_dash, inline_code, asterisk_bold, asterisk_italic, image, link] diff --git a/base/markdown/parse/parse.jl b/base/markdown/parse/parse.jl index 4e223982ae40e..478f218981841 100644 --- a/base/markdown/parse/parse.jl +++ b/base/markdown/parse/parse.jl @@ -59,8 +59,9 @@ function parseinline(stream::IO, config::Config) return content end -parseinline(s::String, c::Config) = - parseinline(IOBuffer(s), c) +parseinline(s::String, c::Config) = parseinline(IOBuffer(s), c) +# TODO remove once GH #9888 is fixed +parseinline{T}(s::SubString{T}, c::Config) = parseinline(convert(T, s), c) parseinline(s) = parseinline(s, _config_) diff --git a/base/markdown/render/html.jl b/base/markdown/render/html.jl index 5a437f8ed8185..8baece696071d 100644 --- a/base/markdown/render/html.jl +++ b/base/markdown/render/html.jl @@ -61,7 +61,7 @@ function html(io::IO, md::Table) for (i, row) in enumerate(md.rows) withtag(io, :tr) do for c in md.rows[i] - t = i == 1 ? :th : :td + t = (i == 1) ? :th : :td withtag(io, t) do htmlinline(io, c) end diff --git a/base/markdown/render/plain.jl b/base/markdown/render/plain.jl index f5d47d6a44cf3..38eb06866f864 100644 --- a/base/markdown/render/plain.jl +++ b/base/markdown/render/plain.jl @@ -41,39 +41,38 @@ function plaininline(io::IO, br::LineBreak) end function plain(io::IO, md::Table) - col_widths = reduce(max, map(cell -> map(ansi_length, cell), md.rows)) + col_widths = reduce(max, map(row -> int(map(x -> length(plaininline(x)), row)), md.rows)) + col_widths = max(col_widths, 3) for (n, row) in enumerate(md.rows) for (i, h) in enumerate(row) # TODO use not terminal version of print_align - if i != 1 - print(io, " | ") - end - print(io, " " ^ (col_widths[i] - ansi_length(h))) - plaininline(io, h) + (i != 1) && print(io, " | ") + print(io, " " ^ (col_widths[i] - length(plaininline(h)))) + print(io, plaininline(h)) end - println(io, "") + println(io) if n == 1 for (j, w) in enumerate(col_widths) if j != 1 - print(io, " | ") + print(io, " | ") end a = typeof(md.align) == Symbol ? md.align : md.align[j] print(io, _dash(w, a)) end - println(io, "") + println(io) end end end function _dash(width, align) if align == :l - return ":" * "-" ^ max(1, width - 1) + return ":" * "-" ^ max(3, width - 1) elseif align == :r - return "-" ^ max(1, width - 1) * ":" + return "-" ^ max(3, width - 1) * ":" elseif align == :c - return ":" * "-" ^ max(1, width - 2) * ":" + return ":" * "-" ^ max(3, width - 2) * ":" else throw(ArgumentError("Unrecognized alignment $align")) end @@ -90,19 +89,19 @@ function plaininline(io::IO, md...) end plaininline(io::IO, md::Vector) = !isempty(md) && plaininline(io, md...) - -plaininline(io::IO, md::Image) = print(io, "![$(md.alt)]($(md.url))") - -plaininline(io::IO, s::String) = print(io, s) - -plaininline(io::IO, md::Bold) = plaininline(io, "**", md.text, "**") - -plaininline(io::IO, md::Italic) = plaininline(io, "*", md.text, "*") - +plaininline(io::IO, md::Image) = print(io, "![", plaininline(md.alt), "](", + md.url, ")") +plaininline(io::IO, md::Link) = print(io "[", plaininline(md.text), "](", + md.url, ")") +plaininline(io::IO, md::Bold) = print(io, "**", plaininline(md.text), "**") +plaininline(io::IO, md::Italic) = print(io, "*", plaininline(md.text), "*") plaininline(io::IO, md::Code) = print(io, "`", md.code, "`") - +plaininline(io::IO, s::String) = s plaininline(io::IO, x) = writemime(io, MIME"text/plain"(), x) +plaininline(s::String) = s +plaininline(x) = sprint(plaininline, x) + # writemime Base.writemime(io::IO, ::MIME"text/plain", md::MD) = plain(io, md) diff --git a/base/markdown/render/terminal/render.jl b/base/markdown/render/terminal/render.jl index 42daf9bc5506e..a47a3c11acf30 100644 --- a/base/markdown/render/terminal/render.jl +++ b/base/markdown/render/terminal/render.jl @@ -66,21 +66,23 @@ function term(io::IO, br::LineBreak, columns) end function term(io::IO, md::Table, columns) - col_widths = reduce(max, map(cell -> map(ansi_length, cell), md.rows)) - + col_widths = reduce(max, map(cell -> int(map(ansi_length, cell)), + md.rows)) + col_widths = max(col_widths, 3) for (n, row) in enumerate(md.rows) for (i, h) in enumerate(row) + (i != 1) && print(io, " ") a = typeof(md.align) == Symbol ? md.align : md.align[i] print_align(io, h, col_widths[i], a) - print(io, " ") end - println(io, "") + println(io) if n == 1 - for w in col_widths - print(io, ("-" ^ w) * " ") + for (j, w) in enumerate(col_widths) + (j != 1) && print(io, " ") + print(io, "-" ^ w) end - println(io, "") + println(io) end end end @@ -93,7 +95,7 @@ function ansi_length(md::Vector{Any}) total end ansi_length(c::Code) = ansi_length(c.code) -ansi_length(l::Link) = ansi_length(l.text) +ansi_length(l::Union(Link, Italic, Bold)) = ansi_length(l.text) term(io::IO, x, _) = writemime(io, MIME"text/plain"(), x) From fc60fb897f88391bbd4269ed439fde70a36485c9 Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Sun, 25 Jan 2015 09:50:13 -0800 Subject: [PATCH 4/7] fix use of sprint and escapes mispelling --- base/markdown/GitHub/GitHub.jl | 2 +- base/markdown/render/plain.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/markdown/GitHub/GitHub.jl b/base/markdown/GitHub/GitHub.jl index dd27f753657e0..765f033e87dc2 100644 --- a/base/markdown/GitHub/GitHub.jl +++ b/base/markdown/GitHub/GitHub.jl @@ -106,6 +106,6 @@ end @flavor github [list, indentcode, blockquote, fencedcode, hashheader, github_table, github_paragraph, - linebreak, espaces, en_dash, inline_code, asterisk_bold, + linebreak, escapes, en_dash, inline_code, asterisk_bold, asterisk_italic, image, link] diff --git a/base/markdown/render/plain.jl b/base/markdown/render/plain.jl index 38eb06866f864..52dcd6f659ca5 100644 --- a/base/markdown/render/plain.jl +++ b/base/markdown/render/plain.jl @@ -91,12 +91,12 @@ end plaininline(io::IO, md::Vector) = !isempty(md) && plaininline(io, md...) plaininline(io::IO, md::Image) = print(io, "![", plaininline(md.alt), "](", md.url, ")") -plaininline(io::IO, md::Link) = print(io "[", plaininline(md.text), "](", +plaininline(io::IO, md::Link) = print(io, "[", plaininline(md.text), "](", md.url, ")") plaininline(io::IO, md::Bold) = print(io, "**", plaininline(md.text), "**") plaininline(io::IO, md::Italic) = print(io, "*", plaininline(md.text), "*") plaininline(io::IO, md::Code) = print(io, "`", md.code, "`") -plaininline(io::IO, s::String) = s +plaininline(io::IO, s::String) = print(io, s) plaininline(io::IO, x) = writemime(io, MIME"text/plain"(), x) plaininline(s::String) = s From d36e51ef529702f262293ac49ec8be4bafac295f Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Sun, 25 Jan 2015 11:30:00 -0800 Subject: [PATCH 5/7] move markdown table code to its own file --- base/markdown/GitHub/GitHub.jl | 73 +--------- base/markdown/GitHub/table.jl | 168 ++++++++++++++++++++++++ base/markdown/render/html.jl | 15 --- base/markdown/render/latex.jl | 23 ---- base/markdown/render/plain.jl | 38 ------ base/markdown/render/terminal/render.jl | 22 ---- 6 files changed, 170 insertions(+), 169 deletions(-) create mode 100644 base/markdown/GitHub/table.jl diff --git a/base/markdown/GitHub/GitHub.jl b/base/markdown/GitHub/GitHub.jl index 765f033e87dc2..9d0a52f69fc37 100644 --- a/base/markdown/GitHub/GitHub.jl +++ b/base/markdown/GitHub/GitHub.jl @@ -1,3 +1,5 @@ +include("table.jl") + @breaking true -> function fencedcode(stream::IO, block::MD, config::Config) startswith(stream, "```", padding = true) || return false @@ -33,77 +35,6 @@ function github_paragraph(stream::IO, md::MD, config::Config) return true end -type Table - rows::Vector{Vector{Any}} - align -end - -function github_table(stream::IO, md::MD, config::Config) - withstream(stream) do - rows = Any[] - n = 0 - align = :r # default is to align right - while !eof(stream) - n += 1 - pos = position(stream) - skipwhitespace(stream) - line = readline(stream) |> chomp - - if n == 1 - pipe_border = line[1] == '|' - if !('|' in line) - return false - end - end - - row = map(strip, split(line, "|")) - if pipe_border - if row[1] == row[end] == "" - row = row[2:end-1] - else - return false - end - end - - if n == 2 && all(['-' in r && issubset(Set(r), Set(" -:")) - for r in row]) - # handle possible --- line - align = Symbol[] - for r in row - if r[1] == ':' - if r[end] == ':' - push!(align, :c) - else - push!(align, :l) - end - else - if r[end] == ':' - push!(align, :r) - else - # default is align right - push!(align, :r) - end - end - end - - elseif n == 1 || length(rows[1]) == length(row) - push!(rows, map(x -> parseinline(x, config), row)) - elseif length(row) > 1 - seek(stream, pos) - break - else - return false - end - end - if length(rows) < 2 - return false - end - push!(md, Table(rows, align)) - return true - end -end - - @flavor github [list, indentcode, blockquote, fencedcode, hashheader, github_table, github_paragraph, linebreak, escapes, en_dash, inline_code, asterisk_bold, diff --git a/base/markdown/GitHub/table.jl b/base/markdown/GitHub/table.jl new file mode 100644 index 0000000000000..18da05d22e169 --- /dev/null +++ b/base/markdown/GitHub/table.jl @@ -0,0 +1,168 @@ +type Table + rows::Vector{Vector{Any}} + align +end + +function github_table(stream::IO, md::MD, config::Config) + withstream(stream) do + rows = Any[] + n = 0 + align = :r # default is to align right + while !eof(stream) + n += 1 + pos = position(stream) + skipwhitespace(stream) + line = readline(stream) |> chomp + + if n == 1 + pipe_border = line[1] == '|' + if !('|' in line) + return false + end + end + + row = map(strip, split(line, "|")) + if pipe_border + if row[1] == row[end] == "" + row = row[2:end-1] + else + return false + end + end + + if n == 2 && all(['-' in r && issubset(Set(r), Set(" -:")) + for r in row]) + # handle possible --- line + align = Symbol[] + for r in row + if r[1] == ':' + if r[end] == ':' + push!(align, :c) + else + push!(align, :l) + end + else + if r[end] == ':' + push!(align, :r) + else + # default is align right + push!(align, :r) + end + end + end + + elseif n == 1 || length(rows[1]) == length(row) + push!(rows, map(x -> parseinline(x, config), row)) + elseif length(row) > 1 + seek(stream, pos) + break + else + return false + end + end + if length(rows) < 2 + return false + end + push!(md, Table(rows, align)) + return true + end +end + +function html(io::IO, md::Table) + withtag(io, :table) do + for (i, row) in enumerate(md.rows) + withtag(io, :tr) do + for c in md.rows[i] + t = (i == 1) ? :th : :td + withtag(io, t) do + htmlinline(io, c) + end + end + end + end + end +end + +function plain(io::IO, md::Table) + col_widths = reduce(max, map(row -> int(map(x -> length(plaininline(x)), row)), md.rows)) + col_widths = max(col_widths, 3) + + for (n, row) in enumerate(md.rows) + for (i, h) in enumerate(row) + # TODO use not terminal version of print_align + (i != 1) && print(io, " | ") + print(io, " " ^ (col_widths[i] - length(plaininline(h)))) + print(io, plaininline(h)) + end + println(io) + + if n == 1 + for (j, w) in enumerate(col_widths) + if j != 1 + print(io, " | ") + end + a = typeof(md.align) == Symbol ? md.align : md.align[j] + print(io, _dash(w, a)) + end + println(io) + end + end +end + +function _dash(width, align) + if align == :l + return ":" * "-" ^ max(3, width - 1) + elseif align == :r + return "-" ^ max(3, width - 1) * ":" + elseif align == :c + return ":" * "-" ^ max(3, width - 2) * ":" + else + throw(ArgumentError("Unrecognized alignment $align")) + end +end + + +function writemime(io::IO, ::MIME"text/latex", md::Table) + wrapblock(io, "tabular") do + if typeof(md.align) == Symbol + align = string(md.align) ^ length(md.rows[1]) + else + align = md.align + end + println(io, "{$(join(align, " | "))}") + for (i, row) in enumerate(md.rows) + for (j, cell) in enumerate(row) + if j != 1 + print(io, " & ") + end + latex_inline(io, cell) + end + println(io, " \\\\") + if i == 1 + println("\\hline") + end + end + end +end + +function term(io::IO, md::Table, columns) + col_widths = reduce(max, map(cell -> int(map(ansi_length, cell)), + md.rows)) + col_widths = max(col_widths, 3) + for (n, row) in enumerate(md.rows) + for (i, h) in enumerate(row) + (i != 1) && print(io, " ") + a = typeof(md.align) == Symbol ? md.align : md.align[i] + print_align(io, h, col_widths[i], a) + end + println(io) + + if n == 1 + for (j, w) in enumerate(col_widths) + (j != 1) && print(io, " ") + print(io, "-" ^ w) + end + println(io) + end + end +end diff --git a/base/markdown/render/html.jl b/base/markdown/render/html.jl index 8baece696071d..5301f207fa33c 100644 --- a/base/markdown/render/html.jl +++ b/base/markdown/render/html.jl @@ -56,21 +56,6 @@ function html(io::IO, md::List) end end -function html(io::IO, md::Table) - withtag(io, :table) do - for (i, row) in enumerate(md.rows) - withtag(io, :tr) do - for c in md.rows[i] - t = (i == 1) ? :th : :td - withtag(io, t) do - htmlinline(io, c) - end - end - end - end - end -end - html(io::IO, x) = tohtml(io, x) # Inline elements diff --git a/base/markdown/render/latex.jl b/base/markdown/render/latex.jl index 397a4eb440e57..c23ee6876f015 100644 --- a/base/markdown/render/latex.jl +++ b/base/markdown/render/latex.jl @@ -66,29 +66,6 @@ function writemime(io::IO, ::MIME"text/latex", md::List) end end -function writemime(io::IO, ::MIME"text/latex", md::Table) - wrapblock(io, "tabular") do - if typeof(md.align) == Symbol - align = string(md.align) ^ length(md.rows[1]) - else - align = md.align - end - println(io, "{$(join(align, " | "))}") - for (i, row) in enumerate(md.rows) - for (j, cell) in enumerate(row) - if j != 1 - print(io, " & ") - end - latex_inline(io, cell) - end - println(io, " \\\\") - if i == 1 - println("\\hline") - end - end - end -end - # Inline elements diff --git a/base/markdown/render/plain.jl b/base/markdown/render/plain.jl index 52dcd6f659ca5..462e902d53adf 100644 --- a/base/markdown/render/plain.jl +++ b/base/markdown/render/plain.jl @@ -40,44 +40,6 @@ function plaininline(io::IO, br::LineBreak) println(io) end -function plain(io::IO, md::Table) - col_widths = reduce(max, map(row -> int(map(x -> length(plaininline(x)), row)), md.rows)) - col_widths = max(col_widths, 3) - - for (n, row) in enumerate(md.rows) - for (i, h) in enumerate(row) - # TODO use not terminal version of print_align - (i != 1) && print(io, " | ") - print(io, " " ^ (col_widths[i] - length(plaininline(h)))) - print(io, plaininline(h)) - end - println(io) - - if n == 1 - for (j, w) in enumerate(col_widths) - if j != 1 - print(io, " | ") - end - a = typeof(md.align) == Symbol ? md.align : md.align[j] - print(io, _dash(w, a)) - end - println(io) - end - end -end - -function _dash(width, align) - if align == :l - return ":" * "-" ^ max(3, width - 1) - elseif align == :r - return "-" ^ max(3, width - 1) * ":" - elseif align == :c - return ":" * "-" ^ max(3, width - 2) * ":" - else - throw(ArgumentError("Unrecognized alignment $align")) - end -end - plain(io::IO, x) = tohtml(io, x) # Inline elements diff --git a/base/markdown/render/terminal/render.jl b/base/markdown/render/terminal/render.jl index a47a3c11acf30..f9aa27b5e3c8e 100644 --- a/base/markdown/render/terminal/render.jl +++ b/base/markdown/render/terminal/render.jl @@ -65,28 +65,6 @@ function term(io::IO, br::LineBreak, columns) println(io) end -function term(io::IO, md::Table, columns) - col_widths = reduce(max, map(cell -> int(map(ansi_length, cell)), - md.rows)) - col_widths = max(col_widths, 3) - for (n, row) in enumerate(md.rows) - for (i, h) in enumerate(row) - (i != 1) && print(io, " ") - a = typeof(md.align) == Symbol ? md.align : md.align[i] - print_align(io, h, col_widths[i], a) - end - println(io) - - if n == 1 - for (j, w) in enumerate(col_widths) - (j != 1) && print(io, " ") - print(io, "-" ^ w) - end - println(io) - end - end -end - function ansi_length(md::Vector{Any}) total = 0 for c in md From 575cf67a74bad002322539c2391123bcb5be35ee Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Sun, 25 Jan 2015 20:46:55 -0800 Subject: [PATCH 6/7] test markdown table --- base/markdown/GitHub/table.jl | 18 +++++++++--------- test/markdown.jl | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/base/markdown/GitHub/table.jl b/base/markdown/GitHub/table.jl index 18da05d22e169..560401a3fdf25 100644 --- a/base/markdown/GitHub/table.jl +++ b/base/markdown/GitHub/table.jl @@ -84,19 +84,19 @@ function html(io::IO, md::Table) end function plain(io::IO, md::Table) - col_widths = reduce(max, map(row -> int(map(x -> length(plaininline(x)), row)), md.rows)) - col_widths = max(col_widths, 3) + plain_cells = map(row -> map(Markdown.plaininline, row), md.rows) + plain_lengths = map(row -> int(map(length, row)), plain_cells) + col_widths = reduce(max, plain_lengths) - for (n, row) in enumerate(md.rows) - for (i, h) in enumerate(row) - # TODO use not terminal version of print_align - (i != 1) && print(io, " | ") - print(io, " " ^ (col_widths[i] - length(plaininline(h)))) - print(io, plaininline(h)) + for i in 1:length(md.rows) + for (j, text) in enumerate(plain_cells[i]) + (j != 1) && print(io, " | ") + print(io, " " ^ (col_widths[j] - plain_lengths[i][j])) + print(io, text) end println(io) - if n == 1 + if i == 1 for (j, w) in enumerate(col_widths) if j != 1 print(io, " | ") diff --git a/test/markdown.jl b/test/markdown.jl index 719f324ac96cd..7ce36b03b7303 100644 --- a/test/markdown.jl +++ b/test/markdown.jl @@ -1,5 +1,5 @@ using Base.Markdown -import Base.Markdown: MD, Paragraph, Italic, Bold, plain, term, html +import Base.Markdown: MD, Paragraph, Italic, Bold, plain, term, html, Table, Code import Base: writemime # Basics @@ -39,3 +39,17 @@ writemime(io::IO, m::MIME"text/plain", r::Reference) = print(io, "$(r.ref) (see Julia docs)") @test md"Behaves like $(ref(fft))" == md"Behaves like fft (see Julia docs)" + +# GH tables +@test md"""a|b +1|2""" == MD(Table(Any[Any[Any["a"],Any["b"]];Any[Any["1"],Any["2"]]], :r)) + +table = md"""| a | b | c | + | :- | -: | - | + | d`gh`hg | hgh**jhj**ge | f |""" +@test table == MD(Table(Any[Any[Any["a"],Any["b"],Any["c"]], + Any[Any["d",Markdown.Code("","gh"),"hg"], + Any["hgh",Markdown.Bold(Any["jhj"]),"ge"], + Any["f"]]], + [:l, :r, :r])) + From cca79f6cbbbe10c954d895e0edf2b4b5d2b57214 Mon Sep 17 00:00:00 2001 From: Andy Hayden Date: Mon, 26 Jan 2015 09:41:48 -0800 Subject: [PATCH 7/7] remove ansi_length calls --- base/markdown/GitHub/table.jl | 20 +++++++++++--------- base/markdown/render/terminal/formatting.jl | 13 +++++-------- base/markdown/render/terminal/render.jl | 10 ---------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/base/markdown/GitHub/table.jl b/base/markdown/GitHub/table.jl index 560401a3fdf25..1e0f69cba2209 100644 --- a/base/markdown/GitHub/table.jl +++ b/base/markdown/GitHub/table.jl @@ -84,7 +84,7 @@ function html(io::IO, md::Table) end function plain(io::IO, md::Table) - plain_cells = map(row -> map(Markdown.plaininline, row), md.rows) + plain_cells = map(row -> map(plaininline, row), md.rows) plain_lengths = map(row -> int(map(length, row)), plain_cells) col_widths = reduce(max, plain_lengths) @@ -146,18 +146,20 @@ function writemime(io::IO, ::MIME"text/latex", md::Table) end function term(io::IO, md::Table, columns) - col_widths = reduce(max, map(cell -> int(map(ansi_length, cell)), - md.rows)) + plain_lengths = map(row -> int(map(x -> ansi_length(terminline(x)), row)), + md.rows) + col_widths = reduce(max, plain_lengths) + col_widths = max(col_widths, 3) - for (n, row) in enumerate(md.rows) - for (i, h) in enumerate(row) - (i != 1) && print(io, " ") - a = typeof(md.align) == Symbol ? md.align : md.align[i] - print_align(io, h, col_widths[i], a) + for (i, row) in enumerate(md.rows) + for (j, h) in enumerate(row) + (j != 1) && print(io, " ") + a = typeof(md.align) == Symbol ? md.align : md.align[j] + print_align(io, h, plain_lengths[i][j], col_widths[j], a) end println(io) - if n == 1 + if i == 1 for (j, w) in enumerate(col_widths) (j != 1) && print(io, " ") print(io, "-" ^ w) diff --git a/base/markdown/render/terminal/formatting.jl b/base/markdown/render/terminal/formatting.jl index 6ee26f743d3b3..9adecb5eb20f4 100644 --- a/base/markdown/render/terminal/formatting.jl +++ b/base/markdown/render/terminal/formatting.jl @@ -97,22 +97,19 @@ function print_centred(io::IO, s...; columns = 80, width = columns) end end -function print_align(io::IO, text, width, align=:r) - n = ansi_length(text) +function print_align(io::IO, text, text_width, width, align=:r) if align == :c # center - lpad = div(width - n, 2) + lpad = div(width - text_width, 2) elseif align == :r # right - lpad = width - n + lpad = width - text_width elseif align == :l # left lpad = 0 else throw(ArgumentError("Alignment $align not recognised")) end print(io, " " ^ lpad) - for t in text - terminline(io, t) - end - print(io, " " ^ (width - lpad - n)) + terminline(io, text) + print(io, " " ^ (width - lpad - text_width)) end diff --git a/base/markdown/render/terminal/render.jl b/base/markdown/render/terminal/render.jl index f9aa27b5e3c8e..ffff1b570791c 100644 --- a/base/markdown/render/terminal/render.jl +++ b/base/markdown/render/terminal/render.jl @@ -65,16 +65,6 @@ function term(io::IO, br::LineBreak, columns) println(io) end -function ansi_length(md::Vector{Any}) - total = 0 - for c in md - total += ansi_length(c) - end - total -end -ansi_length(c::Code) = ansi_length(c.code) -ansi_length(l::Union(Link, Italic, Bold)) = ansi_length(l.text) - term(io::IO, x, _) = writemime(io, MIME"text/plain"(), x) # Inline Content