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

JuliaLang / julia / 1572

01 Feb 2026 09:55PM UTC coverage: 76.677% (-0.07%) from 76.749%
1572

push

buildkite

web-flow
docs: clarify 'using A, B' semantics (#60856)

Resolves #36090

62889 of 82018 relevant lines covered (76.68%)

23269256.38 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)
934,180✔
10
    while !eof(io)
467,090✔
11
        c = peek(io, Char)
673,775✔
12
        c in whitespace || (newlines && c == '\n') || break
1,140,727✔
13
        read(io, Char)
206,850✔
14
    end
206,850✔
15
    return io
467,090✔
16
end
17

18
"""
19
Skip any leading blank lines. Returns the number skipped.
20
"""
21
function skipblank(io::IO)
326,145✔
22
    start = position(io)
326,145✔
23
    i = 0
326,145✔
24
    for c in readeach(io, Char)
600,706✔
25
        c == '\n' && (start = position(io); i+=1; continue)
387,918✔
26
        c == '\r' && (start = position(io); i+=1; continue)
370,823✔
27
        c in whitespace || break
370,820✔
28
    end
235,250✔
29
    seek(io, start)
652,290✔
30
    return i
326,145✔
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,
567,154✔
38
                                     eat = true,
39
                                     allowempty = false)
40
    start = position(io)
283,577✔
41
    l = readline(io)
283,577✔
42
    length(l) == 0 && return allowempty
283,577✔
43

44
    result = allowempty
186,297✔
45
    for c in l
372,489✔
46
        c in whitespace && (allow_whitespace ? continue : (result = false; break))
228,543✔
47
        c in chars && (result = true; continue)
186,201✔
48
        result = false; break
186,201✔
49
    end
84,564✔
50
    !(result && eat) && seek(io, start)
372,498✔
51
    return result
186,297✔
52
end
53

54
blankline(io::IO; eat = true) =
567,154✔
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,116,250✔
66
    start = position(stream)
2,558,125✔
67
    padding && skipwhitespace(stream, newlines = newlines)
2,558,125✔
68
    result = true
2,558,125✔
69
    for char in s
5,116,250✔
70
        !eof(stream) && read(stream, Char) == char ||
2,822,524✔
71
            (result = false; break)
2,320,275✔
72
    end
766,648✔
73
    !(result && eat) && seek(stream, start)
4,881,040✔
74
    return result
2,558,125✔
75
end
76

77
function startswith(stream::IO, c::AbstractChar; eat = true)
2,376,240✔
78
    if !eof(stream) && peek(stream) == UInt8(c)
2,367,727✔
79
        eat && read(stream, Char)
540,861✔
80
        return true
540,861✔
81
    else
82
        return false
1,826,866✔
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,049,952✔
91
    @assert Base.startswith(r.pattern, "^")
524,976✔
92
    start = position(stream)
524,976✔
93
    padding && skipwhitespace(stream)
524,976✔
94
    line = readline(stream)
524,976✔
95
    seek(stream, start)
1,049,952✔
96
    m = match(r, line)
524,976✔
97
    if eat && m !== nothing
524,976✔
98
        for i in 1:length(m.match)
147,253✔
99
            read(stream, Char)
185,655✔
100
        end
224,057✔
101
    end
102
    return m
524,976✔
103
end
104

105
function startswith(stream::IO, r::Regex; kws...)
83,873✔
106
    return matchstart(stream, r; kws...) !== nothing
83,873✔
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,860✔
114
    pos = position(stream)
3,316,870✔
115
    result = f()
3,317,149✔
116
    (result ≡ nothing || result ≡ false) && seek(stream, pos)
5,398,424✔
117
    return result
3,316,870✔
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,014,324✔
127
        m = 0
915,454✔
128
        while startswith(io, ' ') m += 1 end
1,322,329✔
129
        return m <= n
915,454✔
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,414✔
140
    withstream(stream) do
195,414✔
141
        buffer = IOBuffer()
192,205✔
142
        count = 0
192,205✔
143
        while !eof(stream)
1,756,030✔
144
            if startswith(stream, delimiter)
1,753,330✔
145
                if count == 0
190,925✔
146
                    return takestring!(buffer)
188,996✔
147
                else
148
                    count -= 1
1,929✔
149
                    write(buffer, delimiter)
1,929✔
150
                    continue
1,929✔
151
                end
152
            end
153
            char = read(stream, Char)
1,562,405✔
154
            char == match && (count += 1)
1,562,405✔
155
            !newlines && char == '\n' && break
1,562,405✔
156
            write(buffer, char)
1,561,896✔
157
        end
1,563,825✔
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,132✔
173
    delimiter, nmin = string(delimiter[1]), length(delimiter)
231,132✔
174
    withstream(stream) do
115,566✔
175
        if position(stream) >= 1
115,566✔
176
            # check the previous byte isn't a delimiter
177
            skip(stream, -1)
128,244✔
178
            (read(stream, Char) in delimiter) && return nothing
64,122✔
179
        end
180
        n = nmin
105,945✔
181
        startswith(stream, delimiter^n) || return nothing
199,321✔
182
        while startswith(stream, delimiter); n += 1; end
19,574✔
183
        !rep && n > nmin && return nothing
12,569✔
184
        !eof(stream) && isspace(peek(stream, Char)) && return nothing
16,462✔
185

186
        buffer = IOBuffer()
8,381✔
187
        for char in readeach(stream, Char)
15,733✔
188
            write(buffer, char)
69,402✔
189
            if !(isspace(char) || char in delimiter) && startswith(stream, delimiter^n)
132,982✔
190
                trailing = 0
6,311✔
191
                while startswith(stream, delimiter); trailing += 1; end
7,403✔
192
                trailing == 0 && return takestring!(buffer)
6,311✔
193
                write(buffer, delimiter ^ (n + trailing))
849✔
194
            end
195
        end
63,940✔
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