Skip to content

Commit 2a0c9e2

Browse files
committed
Add annotate! method for AnnotatedIOBuffer
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 af90dac commit 2a0c9e2

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
@@ -314,28 +314,30 @@ reverse(s::SubString{<:AnnotatedString}) = reverse(AnnotatedString(s))
314314

315315
## End AbstractString interface ##
316316

317-
"""
318-
annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value)
319-
annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value)
320-
321-
Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`).
322-
To remove existing `label` annotations, use a value of `nothing`.
323-
"""
324-
function annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any}))
317+
function _annotate!(annlist::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any}))
325318
label, val = labelval
326319
if val === nothing
327-
indices = searchsorted(s.annotations, (range,), by=first)
328-
labelindex = filter(i -> first(s.annotations[i][2]) === label, indices)
320+
indices = searchsorted(annlist, (range,), by=first)
321+
labelindex = filter(i -> first(annlist[i][2]) === label, indices)
329322
for index in Iterators.reverse(labelindex)
330-
deleteat!(s.annotations, index)
323+
deleteat!(annlist, index)
331324
end
332325
else
333-
sortedindex = searchsortedlast(s.annotations, (range,), by=first) + 1
334-
insert!(s.annotations, sortedindex, (range, Pair{Symbol, Any}(label, val)))
326+
sortedindex = searchsortedlast(annlist, (range,), by=first) + 1
327+
insert!(annlist, sortedindex, (range, Pair{Symbol, Any}(label, val)))
335328
end
336-
s
337329
end
338330

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

@@ -416,6 +418,9 @@ copy(io::AnnotatedIOBuffer) = AnnotatedIOBuffer(copy(io.io), copy(io.annotations
416418

417419
annotations(io::AnnotatedIOBuffer) = io.annotations
418420

421+
annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) =
422+
(_annotate!(io.annotations, range, labelval); io)
423+
419424
function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}})
420425
astr = AnnotatedString(astr)
421426
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)