Skip to content

Commit 6e1d062

Browse files
authored
Add annotate! method for AnnotatedIOBuffer (#53284)
The annotate! function provides a convenient way of adding annotations to an AnnotatedString/AnnotatedChar without accessing any of the implementation details of the Annotated* types. When AnnotatedIOBuffer was introduced, an appropriate annotations method was added, but annotate! was missed. To correct that, we refactor the current annotate! method for AnnotatedString to a more general helper function _annotate! that operates on the annotation vector itself, and use this new helper method to easily provide an annotate! method for AnnotatedIOBuffer.
1 parent fee198b commit 6e1d062

File tree

2 files changed

+25
-14
lines changed

2 files changed

+25
-14
lines changed

base/strings/annotated.jl

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -316,28 +316,30 @@ reverse(s::SubString{<:AnnotatedString}) = reverse(AnnotatedString(s))
316316

317317
## End AbstractString interface ##
318318

319-
"""
320-
annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value)
321-
annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value)
322-
323-
Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`).
324-
To remove existing `label` annotations, use a value of `nothing`.
325-
"""
326-
function annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any}))
319+
function _annotate!(annlist::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any}))
327320
label, val = labelval
328321
if val === nothing
329-
indices = searchsorted(s.annotations, (range,), by=first)
330-
labelindex = filter(i -> first(s.annotations[i][2]) === label, indices)
322+
indices = searchsorted(annlist, (range,), by=first)
323+
labelindex = filter(i -> first(annlist[i][2]) === label, indices)
331324
for index in Iterators.reverse(labelindex)
332-
deleteat!(s.annotations, index)
325+
deleteat!(annlist, index)
333326
end
334327
else
335-
sortedindex = searchsortedlast(s.annotations, (range,), by=first) + 1
336-
insert!(s.annotations, sortedindex, (range, Pair{Symbol, Any}(label, val)))
328+
sortedindex = searchsortedlast(annlist, (range,), by=first) + 1
329+
insert!(annlist, sortedindex, (range, Pair{Symbol, Any}(label, val)))
337330
end
338-
s
339331
end
340332

333+
"""
334+
annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value)
335+
annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value)
336+
337+
Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`).
338+
To remove existing `label` annotations, use a value of `nothing`.
339+
"""
340+
annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) =
341+
(_annotate!(s.annotations, range, labelval); s)
342+
341343
annotate!(ss::AnnotatedString, @nospecialize(labelval::Pair{Symbol, <:Any})) =
342344
annotate!(ss, firstindex(ss):lastindex(ss), labelval)
343345

@@ -426,6 +428,9 @@ copy(io::AnnotatedIOBuffer) = AnnotatedIOBuffer(copy(io.io), copy(io.annotations
426428

427429
annotations(io::AnnotatedIOBuffer) = io.annotations
428430

431+
annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) =
432+
(_annotate!(io.annotations, range, labelval); io)
433+
429434
function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}})
430435
astr = AnnotatedString(astr)
431436
offset = position(io.io)

test/strings/annotated.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ end
115115
@test write(aio, ' ') == 1
116116
@test write(aio, Base.AnnotatedString("world", [(1:5, :tag => 2)])) == 5
117117
@test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)]
118+
# Check `annotate!`, including region sorting
119+
@test truncate(aio, 0).io.size == 0
120+
@test write(aio, "hello world") == ncodeunits("hello world")
121+
@test Base.annotate!(aio, 7:11, :tag => 2) === aio
122+
@test Base.annotate!(aio, 1:5, :tag => 1) === aio
123+
@test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)]
118124
# Reading
119125
@test read(seekstart(deepcopy(aio.io)), String) == "hello world"
120126
@test read(seekstart(deepcopy(aio)), String) == "hello world"

0 commit comments

Comments
 (0)