From b2b143c22e80a701eddfa5f6484ee55859f99e10 Mon Sep 17 00:00:00 2001 From: Matti Pastell Date: Sun, 7 Jan 2018 20:19:59 +0200 Subject: [PATCH] Add markdown2html writer, remove Documenter depency --- REQUIRE | 1 - src/Markdown2HTML.jl | 237 +++++++++++++++++++++++++++++++++++++++++ src/format.jl | 6 +- test/formatter_test.jl | 2 +- 4 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 src/Markdown2HTML.jl diff --git a/REQUIRE b/REQUIRE index 25f0abc..c2b63aa 100644 --- a/REQUIRE +++ b/REQUIRE @@ -4,6 +4,5 @@ FileIO JSON Highlights Mustache -Documenter YAML Compat 0.25.0 diff --git a/src/Markdown2HTML.jl b/src/Markdown2HTML.jl new file mode 100644 index 0000000..df5bd7f --- /dev/null +++ b/src/Markdown2HTML.jl @@ -0,0 +1,237 @@ +module Markdown2HTML +# Markdown to HTML writer, Modified from Julia Base.Markdown html writer +using Base.Markdown: MD, Header, Code, Paragraph, BlockQuote, Footnote, + Admonition, List, HorizontalRule, Bold, Italic, Image, Link, LineBreak, + LaTeX + + +function tohtml(io::IO, m::MIME"text/html", x) + show(io, m, x) +end + +function tohtml(io::IO, m::MIME"text/plain", x) + htmlesc(io, sprint(show, m, x)) +end + +function tohtml(io::IO, m::MIME"image/png", img) + print(io, """") +end + +function tohtml(m::MIME"image/svg+xml", img) + show(io, m, img) +end + +# AbstractDisplay infrastructure + +function bestmime(val) + for mime in ("text/html", "image/svg+xml", "image/png", "text/plain") + mimewritable(mime, val) && return MIME(Symbol(mime)) + end + error("Cannot render $val to Markdown.") +end + +tohtml(io::IO, x) = tohtml(io, bestmime(x), x) + + +# Utils + +function withtag(f, io::IO, tag, attrs...) + print(io, "<$tag") + for (attr, value) in attrs + print(io, " ") + htmlesc(io, attr) + print(io, "=\"") + htmlesc(io, value) + print(io, "\"") + end + f === nothing && return print(io, " />") + + print(io, ">") + f() + print(io, "") +end + +tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...) + +const _htmlescape_chars = Dict('<'=>"<", '>'=>">", + '"'=>""", '&'=>"&", + # ' '=>" ", + ) +for ch in "'`!\$%()=+{}[]" + _htmlescape_chars[ch] = "&#$(Int(ch));" +end + +function htmlesc(io::IO, s::AbstractString) + # s1 = replace(s, r"&(?!(\w+|\#\d+);)" => "&") + for ch in s + print(io, get(_htmlescape_chars, ch, ch)) + end +end +function htmlesc(io::IO, s::Symbol) + htmlesc(io, string(s)) +end +function htmlesc(io::IO, xs::Union{AbstractString,Symbol}...) + for s in xs + htmlesc(io, s) + end +end +function htmlesc(s::Union{AbstractString,Symbol}) + sprint(htmlesc, s) +end + +# Block elements + +function html(io::IO, content::Vector) + for md in content + html(io, md) + println(io) + end +end + +html(io::IO, md::MD) = html(io, md.content) + +function html(io::IO, header::Header{l}) where l + withtag(io, "h$l") do + htmlinline(io, header.text) + end +end + +function html(io::IO, code::Code) + withtag(io, :pre) do + maybe_lang = !isempty(code.language) ? Any[:class=>"language-$(code.language)"] : [] + withtag(io, :code, maybe_lang...) do + htmlesc(io, code.code) + # TODO should print newline if this is longer than one line ? + end + end +end + +function html(io::IO, md::Paragraph) + withtag(io, :p) do + htmlinline(io, md.content) + end +end + +function html(io::IO, md::BlockQuote) + withtag(io, :blockquote) do + println(io) + html(io, md.content) + end +end + +function html(io::IO, f::Footnote) + withtag(io, :div, :class => "footnote", :id => "footnote-$(f.id)") do + withtag(io, :p, :class => "footnote-title") do + print(io, f.id) + end + html(io, f.text) + end +end + +function html(io::IO, md::Admonition) + withtag(io, :div, :class => "admonition $(md.category)") do + withtag(io, :p, :class => "admonition-title") do + print(io, md.title) + end + html(io, md.content) + end +end + +function html(io::IO, md::List) + maybe_attr = md.ordered > 1 ? Any[:start => string(md.ordered)] : [] + withtag(io, isordered(md) ? :ol : :ul, maybe_attr...) do + for item in md.items + println(io) + withtag(io, :li) do + html(io, item) + end + end + println(io) + end +end + +function html(io::IO, md::HorizontalRule) + tag(io, :hr) +end + +function html(io::IO, tex::LaTeX) + withtag(io, :p, :class => "math") do + write(io, string("\\[", tex.formula, "\\]")) + end +end + +html(io::IO, x) = tohtml(io, x) + +# Inline elements + +function htmlinline(io::IO, content::Vector) + for x in content + htmlinline(io, x) + end +end + +function htmlinline(io::IO, code::Code) + withtag(io, :code) do + htmlesc(io, code.code) + end +end + +function htmlinline(io::IO, tex::LaTeX) + withtag(io, :span, :class => "math") do + write(io, string("\$", tex.formula, "\$")) + end +end + +function htmlinline(io::IO, md::Union{Symbol,AbstractString}) + htmlesc(io, md) +end + +function htmlinline(io::IO, md::Bold) + withtag(io, :strong) do + htmlinline(io, md.text) + end +end + +function htmlinline(io::IO, md::Italic) + withtag(io, :em) do + htmlinline(io, md.text) + end +end + +function htmlinline(io::IO, md::Image) + tag(io, :img, :src=>md.url, :alt=>md.alt) +end + + +function htmlinline(io::IO, f::Footnote) + withtag(io, :a, :href => "#footnote-$(f.id)", :class => "footnote") do + print(io, "[", f.id, "]") + end +end + +function htmlinline(io::IO, link::Link) + withtag(io, :a, :href=>link.url) do + htmlinline(io, link.text) + end +end + +function htmlinline(io::IO, br::LineBreak) + tag(io, :br) +end + +htmlinline(io::IO, x) = tohtml(io, x) + +# API + +html(md) = sprint(html, md) + +function show(io::IO, ::MIME"text/html", md::MD) + withtag(io, :div, :class=>"markdown") do + html(io, md) + end +end + + +end \ No newline at end of file diff --git a/src/format.jl b/src/format.jl index a66c2ee..f2edc1f 100644 --- a/src/format.jl +++ b/src/format.jl @@ -1,4 +1,5 @@ -import Mustache, Highlights, Documenter +import Mustache, Highlights +import .Markdown2HTML using Compat function format(doc::WeaveDoc) @@ -132,7 +133,8 @@ function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2HTML) #invokelatest seems to be needed here #to fix "invalid age range" on 0.6 #21653 m = Compat.invokelatest(Base.Markdown.parse, text) - return string(Documenter.Writers.HTMLWriter.mdconvert(m)) + + return string(Markdown2HTML.html(m)) end diff --git a/test/formatter_test.jl b/test/formatter_test.jl index c6f08ea..e2142f5 100644 --- a/test/formatter_test.jl +++ b/test/formatter_test.jl @@ -15,7 +15,7 @@ f = Weave.format_chunk(dchunk, pformat.formatdict, pformat) @test f == content docformat = Weave.formats["md2html"] -f_check = "

Test chunk

Test rendering \$\alpha\$

" +f_check = "

Test chunk

\n

Test rendering \$\alpha\$

\n" f = Weave.format_chunk(dchunk, docformat.formatdict, docformat) @test f_check == f