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

JuliaLang / julia / 1404

11 Jan 2026 12:09AM UTC coverage: 76.724% (+0.04%) from 76.683%
1404

push

buildkite

web-flow
Add `@stm` "SyntaxTree match" macro (#60475)

62704 of 81727 relevant lines covered (76.72%)

23123244.33 hits per line

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

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

3
include("formatting.jl")
4

5
cols(io) = displaysize(io)[2]
114✔
6

7
function term(io::IO, content::Vector, cols)
150✔
8
    isempty(content) && return
150✔
9
    for md in content[1:end-1]
147✔
10
        term(io, md, cols)
42✔
11
        print(io, '\n', '\n')
42✔
12
    end
42✔
13
    term(io, content[end], cols)
147✔
14
end
15

16
function term(io::IO, md::MD, columns = cols(io))
27✔
17
    md = insert_hlines(md)
255✔
18
    return term(io, md.content, columns)
141✔
19
end
20

21
function term(io::IO, md::Paragraph, columns)
216✔
22
    lines = wraplines(annotprint(terminline, md.content), columns-2margin)
216✔
23
    for (i, line) in enumerate(lines)
432✔
24
        print(io, ' '^margin, line)
435✔
25
        i < length(lines) && println(io)
435✔
26
    end
435✔
27
end
28

29
function term(io::IO, md::HTML, columns)
×
30
    for line in md.content[1:end-1]
×
31
        println(io, line)
×
32
    end
×
33
    print(io, md.content[end])
×
34
end
35

36
function term(io::IO, md::BlockQuote, columns)
3✔
37
    content = annotprint(term, md.content, columns - 10)
3✔
38
    lines = wraplines(rstrip(content), columns - 10)
3✔
39
    for (i, line) in enumerate(lines)
6✔
40
        print(io, ' '^margin, '│', line)
3✔
41
        i < length(lines) && println(io)
3✔
42
    end
3✔
43
end
44

45
function term(io::IO, md::Admonition, columns)
3✔
46
    accent = if md.category == "danger"
3✔
47
        :error
×
48
    elseif md.category in ("warning", "info", "note", "tip")
6✔
49
        Symbol(md.category)
3✔
50
    elseif md.category == "compat"
×
51
        :bright_cyan
×
52
    elseif md.category == "todo"
×
53
        :magenta
×
54
    else
55
        :default
6✔
56
    end
57
    title = if isempty(md.title) md.category else md.title end
6✔
58
    print(io, ' '^margin, styled"{$accent,markdown_admonition:│ $title}",
6✔
59
          '\n', ' '^margin, styled"{$accent,markdown_admonition:│}", '\n')
60
    content = annotprint(term, md.content, columns - 10)
3✔
61
    lines = split(rstrip(content), '\n')
3✔
62
    for (i, line) in enumerate(lines)
6✔
63
        print(io, ' '^margin, styled"{$accent,markdown_admonition:│}", line)
18✔
64
        i < length(lines) && println(io)
9✔
65
    end
9✔
66
end
67

68
function term(io::IO, f::Footnote, columns)
×
69
    print(io, ' '^margin, "│ ")
×
70
    print(io, styled"{markdown_footnote:[^$(f.id)]}")
×
71
    println(io, '\n', ' '^margin, '│')
×
72
    content = annotprint(term, f.text, columns - 10)
×
73
    lines = split(rstrip(content), '\n')
×
74
    for (i, line) in enumerate(lines)
×
75
        print(io, ' '^margin, '│', line)
×
76
        i < length(lines) && println(io)
×
77
    end
×
78
end
79

80
const _bullets = ("• ", "– ", "▪ ")
81

82
function term(io::IO, md::List, columns, depth::Int = 1)
123✔
83
    dterm(io, md, columns, _depth)      = term(io, md, columns)
261✔
84
    dterm(io, md::List, columns, depth) = term(io, md, columns, depth)
51✔
85

86
    function make_list_marker(i::Int)
312✔
87
        list_marker = if isordered(md)
225✔
88
            string(lpad(i + md.ordered - 1, ndigits(length(md.items) + md.ordered - 1)), ". ")
126✔
89
        elseif depth == 1
99✔
90
            first(_bullets)
30✔
91
        else
92
            _bullets[2 + mod(depth, length(_bullets) - 1)]
324✔
93
        end
94
    end
95

96
    # adjust column count to ensure word wrap works correctly; the last
97
    # label will be the widest (for ordered lists; for unordered lists they
98
    # are all the same anyway)
99
    columns -= length(make_list_marker(length(md.items)))
87✔
100

101
    for (i, point) in enumerate(md.items)
87✔
102
        list_marker = make_list_marker(i)
138✔
103
        print(io, ' '^margin, styled"{markdown_list:$list_marker}")
276✔
104
        buf = AnnotatedIOBuffer()
138✔
105
        if point isa Vector && !isempty(point)
138✔
106
            for (i, elt) in enumerate(point[1:end-1])
186✔
107
                dterm(buf, elt, columns, depth + 1)
102✔
108
                println(buf)
51✔
109
                (!(point[i+1] isa List) || point[i+1].loose) && println(buf)
102✔
110
            end
51✔
111
            dterm(buf, point[end], columns, depth + 1)
219✔
112
        else
113
            dterm(buf, point, columns, depth + 1)
3✔
114
        end
115
        content = read(seekstart(buf), AnnotatedString)
276✔
116
        lines = split(rstrip(content), '\n')
138✔
117
        common_indent = minimum(
138✔
118
            (sum((1 for _ in Iterators.takewhile(isspace, line)), init=0)
119
             for line in Iterators.filter(!isempty, lines)),
120
            init=if isempty(lines) 0 else length(first(lines)) end)
138✔
121
        for (l, line) in enumerate(lines)
276✔
122
            l > 1 && print(io, ' '^(margin + length(list_marker)))
774✔
123
            !isempty(line) && print(io, line[common_indent+1:end])
774✔
124
            l < length(lines) && println(io)
774✔
125
        end
1,410✔
126
        i < length(md.items) && print(io, '\n'^(1 + md.loose))
138✔
127
    end
138✔
128
end
129

130
const _header_underlines = collect("≡=–-⋅ ")
131
# TODO settle on another option with unicode e.g. "≡=≃–∼⋅" ?
132

133
function term(io::AnnotIO, md::Header{l}, columns) where l
21✔
134
    face = Symbol("markdown_h$l")
21✔
135
    underline = _header_underlines[l]
21✔
136
    pre = ' '^margin
21✔
137
    line_width = with_output_annotations(io, :face => face) do io
21✔
138
        headline = annotprint(terminline, md.text)
21✔
139
        lines = wraplines(headline, columns - 4margin)
21✔
140
        for (i, line) in enumerate(lines)
39✔
141
            print(io, pre, line)
18✔
142
            i < length(lines) && println(io)
18✔
143
        end
18✔
144
        if length(lines) == 1
21✔
145
            return min(textwidth(lines[end]), columns)
18✔
146
        elseif length(lines) > 1
3✔
147
            return max(textwidth(lines[end]), div(columns, 3)+length(pre))
×
148
        else
149
            return 0
3✔
150
        end
151
    end
152
    header_width = max(0, line_width)
21✔
153
    if underline != ' ' && header_width > 0
21✔
154
        print(io, '\n', ' '^(margin))
18✔
155
        with_output_annotations(io -> print(io, underline^header_width), io, :face => face)
36✔
156
    end
157
end
158

159
function term(io::IO, md::Code, columns)
12✔
160
    code = if md.language == "julia"
12✔
161
        highlight(md.code)
6✔
162
    elseif md.language == "julia-repl" || Base.startswith(md.language, "jldoctest")
12✔
163
        hl = AnnotatedString(md.code)
×
164
        for (; match) in eachmatch(r"(?:^|\n)julia>", hl)
×
165
            StyledStrings.face!(match, :markdown_julia_prompt)
×
166
            afterprompt = match.offset + ncodeunits(match) + 1
×
167
            _, exprend = Meta.parse(md.code, afterprompt, raise = false)
×
168
            highlight!(hl[afterprompt:prevind(md.code, exprend)])
×
169
            if (nextspace = findnext(' ', md.code, exprend)) |> !isnothing
×
170
                nextword = hl[exprend:prevind(hl, nextspace)]
×
171
                if nextword == "ERROR:"
×
172
                    StyledStrings.face!(nextword, :error)
×
173
                end
174
            end
175
        end
×
176
        hl
×
177
    elseif md.language == "styled"
6✔
178
        styled(md.code)
×
179
    else
180
        styled"{markdown_code:$(md.code)}"
24✔
181
    end
182
    lines = split(code, '\n')
12✔
183
    for (i, line) in enumerate(lines)
24✔
184
        print(io, ' '^margin, line)
12✔
185
        i < length(lines) && println(io)
12✔
186
    end
12✔
187
end
188

189
function term(io::IO, tex::LaTeX, columns)
3✔
190
    print(io, ' '^margin, styled"{markdown_latex:$(tex.formula)}")
3✔
191
end
192

193
term(io::IO, br::LineBreak, columns) = nothing # line breaks already printed between subsequent elements
×
194

195
function term(io::IO, br::HorizontalRule, columns)
×
196
    print(io, ' '^margin, styled"{markdown_hrule:$('─'^(columns - 2margin))}")
×
197
end
198

199
function term(io::IO, md::MarkdownElement, columns)
21✔
200
    a = IOContext(AnnotatedIOBuffer(), io)
21✔
201
    term(a, md, columns)
21✔
202
    print(io, read(seekstart(a.io), AnnotatedString))
21✔
203
end
204

205
term(io::IO, x, _) = show(io, MIME"text/plain"(), x)
3✔
206

207
# Inline Content
208

209
terminline(io::IO, content...) = terminline(io, collect(content))
×
210

211
function terminline(io::IO, content::Vector)
330✔
212
    for md in content
330✔
213
        terminline(io, md)
534✔
214
    end
534✔
215
end
216

217
function terminline(io::IO, md::AbstractString)
369✔
218
    print(io, replace(md, r"[\s\t\n]+" => ' '))
372✔
219
end
220

221
function terminline(io::AnnotIO, md::Bold)
6✔
222
    with_output_annotations(io -> terminline(io, md.text), io, :face => :bold)
12✔
223
end
224

225
function terminline(io::AnnotIO, md::Italic)
15✔
226
    with_output_annotations(io -> terminline(io, md.text), io, :face => :italic)
30✔
227
end
228

229
function terminline(io::AnnotIO, md::Strikethrough)
3✔
230
    with_output_annotations(io -> terminline(io, md.text), io, :face => :strikethrough)
6✔
231
end
232

233
function terminline(io::IO, md::LineBreak)
45✔
234
    println(io)
45✔
235
end
236

237
function terminline(io::IO, md::Image)
3✔
238
    terminline(io, "(Image: $(md.alt))")
3✔
239
end
240

241
function terminline(io::IO, f::Footnote)
×
242
    print(io, styled"{markdown_footnote:[^$(f.id)]}")
×
243
end
244

245
function terminline(io::AnnotIO, md::Link)
39✔
246
    annots = if occursin(r"^(https?|file)://", md.url)
39✔
247
        (:face => :markdown_link, :link => md.url)
3✔
248
    else
249
        (:face => :markdown_link,)
75✔
250
    end
251
    with_output_annotations(io -> terminline(io, md.text), io, annots...)
78✔
252
end
253

254
function terminline(io::IO, code::Code)
51✔
255
    body = if code.language == "styled"
51✔
256
        styled(code.code)
×
257
    else
258
        code.code
102✔
259
    end
260
    print(io, styled"{markdown_inlinecode:$body}")
51✔
261
end
262

263
function terminline(io::IO, tex::LaTeX)
×
264
    print(io, styled"{markdown_latex:$(tex.formula)}")
×
265
end
266

267
function terminline(io::IO, md::MarkdownElement)
×
268
    a = IOContext(AnnotatedIOBuffer(), io)
×
269
    terminline(a, md)
×
270
    print(io, read(seekstart(a.io), AnnotatedString))
×
271
end
272

273
terminline(io::IO, x) = show(io, MIME"text/plain"(), x)
3✔
274

275
# Show in terminal
276
Base.show(io::IO, ::MIME"text/plain", md::MD) = (term(io, md); nothing)
75✔
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