diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index f84a85eaf62ab..5089ea85be770 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -314,28 +314,30 @@ reverse(s::SubString{<:AnnotatedString}) = reverse(AnnotatedString(s)) ## End AbstractString interface ## -""" - annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value) - annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value) - -Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`). -To remove existing `label` annotations, use a value of `nothing`. -""" -function annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) +function _annotate!(annlist::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) label, val = labelval if val === nothing - indices = searchsorted(s.annotations, (range,), by=first) - labelindex = filter(i -> first(s.annotations[i][2]) === label, indices) + indices = searchsorted(annlist, (range,), by=first) + labelindex = filter(i -> first(annlist[i][2]) === label, indices) for index in Iterators.reverse(labelindex) - deleteat!(s.annotations, index) + deleteat!(annlist, index) end else - sortedindex = searchsortedlast(s.annotations, (range,), by=first) + 1 - insert!(s.annotations, sortedindex, (range, Pair{Symbol, Any}(label, val))) + sortedindex = searchsortedlast(annlist, (range,), by=first) + 1 + insert!(annlist, sortedindex, (range, Pair{Symbol, Any}(label, val))) end - s end +""" + annotate!(str::AnnotatedString, [range::UnitRange{Int}], label::Symbol => value) + annotate!(str::SubString{AnnotatedString}, [range::UnitRange{Int}], label::Symbol => value) + +Annotate a `range` of `str` (or the entire string) with a labeled value (`label` => `value`). +To remove existing `label` annotations, use a value of `nothing`. +""" +annotate!(s::AnnotatedString, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = + (_annotate!(s.annotations, range, labelval); s) + annotate!(ss::AnnotatedString, @nospecialize(labelval::Pair{Symbol, <:Any})) = annotate!(ss, firstindex(ss):lastindex(ss), labelval) @@ -416,6 +418,9 @@ copy(io::AnnotatedIOBuffer) = AnnotatedIOBuffer(copy(io.io), copy(io.annotations annotations(io::AnnotatedIOBuffer) = io.annotations +annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, @nospecialize(labelval::Pair{Symbol, <:Any})) = + (_annotate!(io.annotations, range, labelval); io) + function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}}) astr = AnnotatedString(astr) offset = position(io.io) diff --git a/test/strings/annotated.jl b/test/strings/annotated.jl index 74898e09eb008..d906e3d36c9a4 100644 --- a/test/strings/annotated.jl +++ b/test/strings/annotated.jl @@ -115,6 +115,12 @@ end @test write(aio, ' ') == 1 @test write(aio, Base.AnnotatedString("world", [(1:5, :tag => 2)])) == 5 @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] + # Check `annotate!`, including region sorting + @test truncate(aio, 0).io.size == 0 + @test write(aio, "hello world") == ncodeunits("hello world") + @test Base.annotate!(aio, 7:11, :tag => 2) === aio + @test Base.annotate!(aio, 1:5, :tag => 1) === aio + @test Base.annotations(aio) == [(1:5, :tag => 1), (7:11, :tag => 2)] # Reading @test read(seekstart(deepcopy(aio.io)), String) == "hello world" @test read(seekstart(deepcopy(aio)), String) == "hello world"