Repository: JuliaLang/julia
File: stdlib/Markdown/src/render/html.jl (Julia)

Code before:

# Handle URI encoding, poorly. The code here was initially copied from
# Base.Filesystem, then modified. The original code implements RFC3986 Section
# 2.1 and 2.2.
#
# We changed encode_uri_component to not encode characters declared as
# "reserved" by RFC3986 Section 2.2 (i.e., those from the gen-delims and
# sub-delims sets). Instead the user is expected to percent encode these (or
# not) as needed -- the alternative would be implement full URI parsing, which
# is non-trivial. That said, it would be better to do that, by e.g. using the
# URIs.jl package, but that is not an option for us, as it is not a stdlib...
#
# As a special affordance, we deviate from the "reserved" list in one way: we
# do *not* excllude '[' and ']' from percent encoding, even thoug they are in
# the gen-delims set. They are only used to encode IPv6 literal addresses in
# the URI, which is (still) rare. But they do occur in query strings and
# indeed in the CommonMark spec tests.

percent_escape(s) = '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%')
encode_uri_component(s::AbstractString) = replace(s, r"[^A-Za-z0-9\-_.~/:?#@!$&'()*+,;=]+" => percent_escape)
encode_uri_component(s::Symbol) = encode_uri_component(string(s))

function withtag(f, io::IO, tag, attrs...)
    print(io, "<$tag")
    for (attr, value) in attrs
        print(io, " ", attr, "=\"")
        htmlesc(io, value)
        print(io, "\"")
    end
    f === nothing && return print(io, " />")

    print(io, ">")
    f()
    print(io, "</$tag>")
end

tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...)

function htmlesc(io::IO, s::AbstractString)
    replace(io, s, '<'=>"&lt;", '>'=>"&gt;", '"'=>"&quot;", '&'=>"&amp;")
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)

Review comment (suggestion):
There shouldn't be any need to encode attribute names; indeed I am not sure this would even work correctly if it happens. But here we fully know all attribute names that are actually used (they all appear in this file), and none of them needs any encoding.


Code after:

# Handle URI encoding, poorly. The code here was initially copied from
# Base.Filesystem, then modified. The original code implements RFC3986 Section
# 2.1 and 2.2.
#
# We changed encode_uri_component to not encode characters declared as
# "reserved" by RFC3986 Section 2.2 (i.e., those from the gen-delims and
# sub-delims sets). Instead the user is expected to percent encode these (or
# not) as needed -- the alternative would be implement full URI parsing, which
# is non-trivial. That said, it would be better to do that, by e.g. using the
# URIs.jl package, but that is not an option for us, as it is not a stdlib...
#
# As a special affordance, we deviate from the "reserved" list in one way: we
# do *not* exclude '[' and ']' from percent encoding, even though they are in
# the gen-delims set. They are only used to encode IPv6 literal addresses in
# the URI, which is (still) rare. But they do occur in query strings and
# indeed in the CommonMark spec tests.

percent_escape(s) = '%' * join(map(b -> uppercase(string(b, base=16)), codeunits(s)), '%')
encode_uri_component(s::AbstractString) = replace(s, r"[^A-Za-z0-9\-_.~/:?#@!$&'()*+,;=]+" => percent_escape)
encode_uri_component(s::Symbol) = encode_uri_component(string(s))

function withtag(f, io::IO, tag, attrs...)
    print(io, "<$tag")
    for (attr, value) in attrs
        print(io, " ", attr, "=\"")
        htmlesc(io, value)
        print(io, "\"")
    end
    f === nothing && return print(io, " />")

    print(io, ">")
    f()
    print(io, "</$tag>")
end

tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...)

function htmlesc(io::IO, s::AbstractString)
    replace(io, s, '<'=>"&lt;", '>'=>"&gt;", '"'=>"&quot;", '&'=>"&amp;")
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)