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

JuliaLang / julia / 1411

16 Jan 2026 09:37PM UTC coverage: 76.844% (+0.2%) from 76.656%
1411

push

buildkite

web-flow
CI: Add `backport-label-audit`, which calls out to reusable workflow (#60724)

The reusable workflows will live here:
https://github.com/JuliaLang/backporter-github-actions-workflows

This PR only adds `backport-label-audit`, because
`backport-label-cleanup` hasn't been tested out on Pkg.jl yet.

Alternative to #60717

62919 of 81879 relevant lines covered (76.84%)

23502754.36 hits per line

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

93.69
/stdlib/Markdown/src/parse/util.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
const whitespace = " \t\r"
4

5
"""
6
Skip any leading whitespace. Returns io.
7
If `newlines=true` then also skip line ends.
8
"""
9
function skipwhitespace(io::IO; newlines = true)
939,694✔
10
    while !eof(io)
469,847✔
11
        c = peek(io, Char)
675,243✔
12
        c in whitespace || (newlines && c == '\n') || break
1,144,952✔
13
        read(io, Char)
205,561✔
14
    end
205,561✔
15
    return io
469,847✔
16
end
17

18
"""
19
Skip any leading blank lines. Returns the number skipped.
20
"""
21
function skipblank(io::IO)
327,641✔
22
    start = position(io)
327,641✔
23
    i = 0
327,641✔
24
    for c in readeach(io, Char)
603,626✔
25
        c == '\n' && (start = position(io); i+=1; continue)
389,049✔
26
        c == '\r' && (start = position(io); i+=1; continue)
371,676✔
27
        c in whitespace || break
371,673✔
28
    end
234,610✔
29
    seek(io, start)
655,282✔
30
    return i
327,641✔
31
end
32

33
"""
34
Return true if the line contains only (and, unless allowempty,
35
at least one of) the characters given.
36
"""
37
function linecontains(io::IO, chars; allow_whitespace = true,
569,230✔
38
                                     eat = true,
39
                                     allowempty = false)
40
    start = position(io)
284,615✔
41
    l = readline(io)
284,615✔
42
    length(l) == 0 && return allowempty
284,615✔
43

44
    result = allowempty
187,176✔
45
    for c in l
374,247✔
46
        c in whitespace && (allow_whitespace ? continue : (result = false; break))
227,824✔
47
        c in chars && (result = true; continue)
187,080✔
48
        result = false; break
187,080✔
49
    end
81,368✔
50
    !(result && eat) && seek(io, start)
374,256✔
51
    return result
187,176✔
52
end
53

54
blankline(io::IO; eat = true) =
569,230✔
55
    linecontains(io, "",
56
                 allow_whitespace = true,
57
                 allowempty = true,
58
                 eat = eat)
59

60
"""
61
Test if the stream starts with the given string.
62
`eat` specifies whether to advance on success (true by default).
63
`padding` specifies whether leading whitespace should be ignored.
64
"""
65
function startswith(stream::IO, s::AbstractString; eat = true, padding = false, newlines = true)
5,149,082✔
66
    start = position(stream)
2,574,541✔
67
    padding && skipwhitespace(stream, newlines = newlines)
2,574,541✔
68
    result = true
2,574,541✔
69
    for char in s
5,149,082✔
70
        !eof(stream) && read(stream, Char) == char ||
2,839,565✔
71
            (result = false; break)
2,335,645✔
72
    end
768,944✔
73
    !(result && eat) && seek(stream, start)
4,912,832✔
74
    return result
2,574,541✔
75
end
76

77
function startswith(stream::IO, c::AbstractChar; eat = true)
2,379,304✔
78
    if !eof(stream) && peek(stream) == UInt8(c)
2,369,958✔
79
        eat && read(stream, Char)
537,794✔
80
        return true
537,794✔
81
    else
82
        return false
1,832,164✔
83
    end
84
end
85

86
function startswith(stream::IO, ss::Vector{<:AbstractString}; kws...)
×
87
    any(s->startswith(stream, s; kws...), ss)
×
88
end
89

90
function matchstart(stream::IO, r::Regex; eat = true, padding = false)
1,054,664✔
91
    @assert Base.startswith(r.pattern, "^")
527,332✔
92
    start = position(stream)
527,332✔
93
    padding && skipwhitespace(stream)
527,332✔
94
    line = readline(stream)
527,332✔
95
    seek(stream, start)
1,054,664✔
96
    m = match(r, line)
527,332✔
97
    if eat && m !== nothing
527,332✔
98
        for i in 1:length(m.match)
148,136✔
99
            read(stream, Char)
186,360✔
100
        end
224,584✔
101
    end
102
    return m
527,332✔
103
end
104

105
function startswith(stream::IO, r::Regex; kws...)
84,112✔
106
    return matchstart(stream, r; kws...) !== nothing
84,112✔
107
end
108

109
"""
110
Executes the block of code, and if the return value is `nothing` or `false`,
111
returns the stream to its initial position.
112
"""
113
function withstream(f, stream)
206,988✔
114
    pos = position(stream)
3,332,519✔
115
    result = f()
3,332,804✔
116
    (result ≡ nothing || result ≡ false) && seek(stream, pos)
5,423,046✔
117
    return result
3,332,519✔
118
end
119

120
"""
121
Consume the standard allowed markdown indent of
122
three spaces. Returns false if there are more than
123
three present.
124
"""
125
function eatindent(io::IO, n = 3)
126
    withstream(io) do
2,023,402✔
127
        m = 0
920,521✔
128
        while startswith(io, ' ') m += 1 end
1,324,928✔
129
        return m <= n
920,521✔
130
    end
131
end
132

133
"""
134
Read the stream until startswith(stream, delim)
135
The delimiter is consumed but not included.
136
Returns nothing and resets the stream if delim is
137
not found.
138
"""
139
function readuntil(stream::IO, delimiter; newlines = false, match = nothing)
195,709✔
140
    withstream(stream) do
195,709✔
141
        buffer = IOBuffer()
192,514✔
142
        count = 0
192,514✔
143
        while !eof(stream)
1,762,657✔
144
            if startswith(stream, delimiter)
1,759,965✔
145
                if count == 0
191,317✔
146
                    return takestring!(buffer)
189,319✔
147
                else
148
                    count -= 1
1,998✔
149
                    write(buffer, delimiter)
1,998✔
150
                    continue
1,998✔
151
                end
152
            end
153
            char = read(stream, Char)
1,568,648✔
154
            char == match && (count += 1)
1,568,648✔
155
            !newlines && char == '\n' && break
1,568,648✔
156
            write(buffer, char)
1,568,145✔
157
        end
1,570,143✔
158
    end
159
end
160

161
# TODO: refactor this. If we're going to assume
162
# the delimiter is a single character + a minimum
163
# repeat we may as well just pass that into the
164
# function.
165

166
"""
167
Parse a symmetrical delimiter which wraps words.
168
i.e. `*word word*` but not `*word * word`.
169
`repeat` specifies whether the delimiter can be repeated.
170
Escaped delimiters are not yet supported.
171
"""
172
function parse_inline_wrapper(stream::IO, delimiter::AbstractString; rep = false)
231,612✔
173
    delimiter, nmin = string(delimiter[1]), length(delimiter)
231,612✔
174
    withstream(stream) do
115,806✔
175
        if position(stream) >= 1
115,806✔
176
            # check the previous byte isn't a delimiter
177
            skip(stream, -1)
128,602✔
178
            (read(stream, Char) in delimiter) && return nothing
64,301✔
179
        end
180
        n = nmin
106,185✔
181
        startswith(stream, delimiter^n) || return nothing
199,751✔
182
        while startswith(stream, delimiter); n += 1; end
19,624✔
183
        !rep && n > nmin && return nothing
12,619✔
184
        !eof(stream) && isspace(peek(stream, Char)) && return nothing
16,589✔
185

186
        buffer = IOBuffer()
8,458✔
187
        for char in readeach(stream, Char)
15,887✔
188
            write(buffer, char)
69,866✔
189
            if !(isspace(char) || char in delimiter) && startswith(stream, delimiter^n)
133,920✔
190
                trailing = 0
6,347✔
191
                while startswith(stream, delimiter); trailing += 1; end
7,439✔
192
                trailing == 0 && return takestring!(buffer)
6,347✔
193
                write(buffer, delimiter ^ (n + trailing))
849✔
194
            end
195
        end
64,368✔
196
    end
197
end
198

199
function showrest(io::IO)
×
200
    start = position(io)
×
201
    show(read(io, String))
×
202
    println()
×
203
    seek(io, start)
×
204
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