• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / 1410

16 Jan 2026 12:27AM UTC coverage: 76.656% (-0.06%) from 76.712%
1410

push

buildkite

web-flow
Test: improve `@test_warn` failure display (#60696)

50 of 55 new or added lines in 1 file covered. (90.91%)

87 existing lines in 14 files now uncovered.

62673 of 81759 relevant lines covered (76.66%)

23410600.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.62
/stdlib/Markdown/src/render/html.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
include("rich.jl")
4

5
# Utils
6

7

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

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

29
function withtag(f, io::IO, tag, attrs...)
41,628✔
30
    print(io, "<$tag")
42,834✔
31
    for (attr, value) in attrs
42,834✔
32
        print(io, " ", attr, "=\"")
3,003✔
33
        htmlesc(io, value)
3,165✔
34
        print(io, "\"")
3,003✔
35
    end
3,297✔
36
    f === nothing && return print(io, " />")
42,834✔
37

38
    print(io, ">")
41,358✔
39
    f()
46,395✔
40
    print(io, "</$tag>")
41,358✔
41
end
42

43
tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...)
1,476✔
44

45
function htmlesc(io::IO, s::AbstractString)
46
    replace(io, s, '<'=>"&lt;", '>'=>"&gt;", '"'=>"&quot;", '&'=>"&amp;")
36,945✔
47
end
48
function htmlesc(io::IO, s::Symbol)
9✔
49
    htmlesc(io, string(s))
9✔
50
end
51
function htmlesc(io::IO, xs::Union{AbstractString,Symbol}...)
×
52
    for s in xs
×
53
        htmlesc(io, s)
×
54
    end
×
55
end
56
function htmlesc(s::Union{AbstractString,Symbol})
×
57
    sprint(htmlesc, s)
×
58
end
59

60
# Block elements
61

62
function html(io::IO, content::Vector)
21,453✔
63
    for md in content
21,453✔
64
        html(io, md)
29,610✔
65
        println(io)
29,610✔
66
    end
29,610✔
67
end
68

69
html(io::IO, md::MD) = html(io, md.content)
18,075✔
70

71
function html(io::IO, header::Header{l}) where l
1,518✔
72
    withtag(io, "h$l") do
1,554✔
73
        htmlinline(io, header.text)
1,554✔
74
    end
75
end
76

77
function html(io::IO, code′::Code)
2,061✔
78
    if code′.language == "styled"
2,061✔
79
        code′ = Code("", String(styled(code′.code)))
×
80
    end
81
    code = code′
2,061✔
82
    withtag(io, :pre) do
2,061✔
83
        maybe_lang = !isempty(code.language) ? Any[:class=>"language-$(code.language)"] : []
3,951✔
84
        withtag(io, :code, maybe_lang...) do
2,061✔
85
            htmlesc(io, code.code)
2,382✔
86
            !isempty(code.code) && println(io)
2,061✔
87
        end
88
    end
89
end
90

91
function html(io::IO, md::Paragraph)
19,284✔
92
    withtag(io, :p) do
19,284✔
93
        htmlinline(io, md.content)
19,284✔
94
    end
95
end
96

97
function html(io::IO, md::HTML)
1,512✔
98
    for line in md.content[1:end-1]
1,512✔
99
        println(io, line)
2,322✔
100
    end
2,322✔
101
    print(io, md.content[end])
1,512✔
102
end
103

104
function html(io::IO, md::BlockQuote)
1,461✔
105
    withtag(io, :blockquote) do
1,551✔
106
        println(io)
1,551✔
107
        html(io, md.content)
1,551✔
108
    end
109
end
110

111
function html(io::IO, f::Footnote)
12✔
112
    withtag(io, :div, :class => "footnote", :id => "footnote-$(f.id)") do
12✔
113
        withtag(io, :p, :class => "footnote-title") do
12✔
114
            print(io, f.id)
12✔
115
        end
116
        html(io, f.text)
12✔
117
    end
118
end
119

120
function html(io::IO, md::Admonition)
15✔
121
    withtag(io, :div, :class => "admonition $(md.category)") do
15✔
122
        withtag(io, :p, :class => "admonition-title") do
15✔
123
            print(io, md.title)
15✔
124
        end
125
        html(io, md.content)
15✔
126
    end
127
end
128

129
function html(io::IO, md::List)
2,688✔
130
    maybe_attr = md.ordered > 1 ? Any[:start => string(md.ordered)] : []
5,877✔
131
    withtag(io, isordered(md) ? :ol : :ul, maybe_attr...) do
3,084✔
132
        for item in md.items
3,084✔
133
            println(io)
4,476✔
134
            withtag(io, :li) do
8,952✔
135
                if md.loose
4,476✔
136
                    println(io)
1,800✔
137
                    html(io, item)
1,800✔
138
                else
139
                    htmltight(io, item)
2,676✔
140
                end
141
            end
142
        end
4,476✔
143
        println(io)
3,084✔
144
    end
145
end
146

147
htmltight(io::IO, md) = html(io, md)
621✔
148
htmltight(io::IO, md::Paragraph) = htmlinline(io, md.content)
2,199✔
149
function htmltight(io::IO, content::Vector)
2,676✔
150
    for md in content
2,676✔
151
        htmltight(io, md)
3,441✔
152
    end
2,820✔
153
end
154

155
function html(io::IO, md::HorizontalRule)
1,014✔
156
    tag(io, :hr)
1,041✔
157
end
158

159
html(io::IO, x) = tohtml(io, x)
39✔
160

161
# Inline elements
162

163
function htmlinline(io::IO, content::Vector)
28,758✔
164
    for x in content
28,758✔
165
        htmlinline(io, x)
37,263✔
166
    end
37,263✔
167
end
168

169
function htmlinline(io::IO, code′::Code)
1,074✔
170
    if code′.language == "styled"
1,074✔
UNCOV
171
        code′ = Code("", String(styled(code′.code)))
×
172
    end
173
    code = code′
1,074✔
174
    withtag(io, :code) do
1,074✔
175
        htmlesc(io, code.code)
1,191✔
176
    end
177
end
178

179
function htmlinline(io::IO, md::Union{Symbol,AbstractString})
29,874✔
180
    htmlesc(io, md)
29,925✔
181
end
182

183
function htmlinline(io::IO, md::Bold)
1,296✔
184
    withtag(io, :strong) do
1,296✔
185
        htmlinline(io, md.text)
1,296✔
186
    end
187
end
188

189
function htmlinline(io::IO, md::Italic)
2,829✔
190
    withtag(io, :em) do
2,829✔
191
        htmlinline(io, md.text)
2,829✔
192
    end
193
end
194

195
function htmlinline(io::IO, md::Strikethrough)
18✔
196
    withtag(io, :s) do
18✔
197
        htmlinline(io, md.text)
18✔
198
    end
199
end
200

201
function htmlinline(io::IO, md::Image)
270✔
202
    tag(io, :img, :src=>encode_uri_component(md.url), :alt=>md.alt)
270✔
203
end
204

205

206
function htmlinline(io::IO, f::Footnote)
12✔
207
    withtag(io, :a, :href => "#footnote-$(f.id)", :class => "footnote") do
12✔
208
        print(io, "[", f.id, "]")
12✔
209
    end
210
end
211

212
function htmlinline(io::IO, link::Link)
1,758✔
213
    withtag(io, :a, :href=>encode_uri_component(link.url)) do
1,758✔
214
        htmlinline(io, link.text)
1,758✔
215
    end
216
end
217

218
function htmlinline(io::IO, br::LineBreak)
165✔
219
    tag(io, :br)
165✔
220
    println(io)
165✔
221
end
222

223
htmlinline(io::IO, x) = tohtml(io, x)
243✔
224

225
# API
226

227
export html
228

229
"""
230
    html([io::IO], md)
231

232
Output the contents of the Markdown object `md` in HTML format, either
233
writing to an (optional) `io` stream or returning a string.
234

235
One can alternatively use `show(io, "text/html", md)` or `repr("text/html", md)`, which
236
differ in that they wrap the output in a `<div class="markdown"> ... </div>` element.
237

238
# Examples
239
```jldoctest
240
julia> html(md"hello _world_")
241
"<p>hello <em>world</em></p>\\n"
242
```
243
"""
244
html(md) = sprint(html, md)
11,994✔
245

246
function show(io::IO, ::MIME"text/html", md::MD)
3✔
247
    withtag(io, :div, :class=>"markdown") do
3✔
248
        html(io, md)
3✔
249
    end
250
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc