Skip to content

Commit 5aed3c2

Browse files
committed
Move AnnotatedIOBuffer implementation to annotated_io.jl
Re-organizing these definitions - no changes to the code.
1 parent d9441ac commit 5aed3c2

File tree

3 files changed

+202
-199
lines changed

3 files changed

+202
-199
lines changed

base/strings/annotated.jl

Lines changed: 0 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -459,202 +459,3 @@ function annotated_chartransform(f::Function, str::AnnotatedString, state=nothin
459459
end
460460
AnnotatedString(String(take!(outstr)), annots)
461461
end
462-
463-
## AnnotatedIOBuffer
464-
465-
struct AnnotatedIOBuffer <: AbstractPipe
466-
io::IOBuffer
467-
annotations::Vector{RegionAnnotation}
468-
end
469-
470-
AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{RegionAnnotation}())
471-
AnnotatedIOBuffer() = AnnotatedIOBuffer(IOBuffer())
472-
473-
function show(io::IO, aio::AnnotatedIOBuffer)
474-
show(io, AnnotatedIOBuffer)
475-
size = filesize(aio.io)
476-
print(io, '(', size, " byte", ifelse(size == 1, "", "s"), ", ",
477-
length(aio.annotations), " annotation", ifelse(length(aio.annotations) == 1, "", "s"), ")")
478-
end
479-
480-
pipe_reader(io::AnnotatedIOBuffer) = io.io
481-
pipe_writer(io::AnnotatedIOBuffer) = io.io
482-
483-
# Useful `IOBuffer` methods that we don't get from `AbstractPipe`
484-
position(io::AnnotatedIOBuffer) = position(io.io)
485-
seek(io::AnnotatedIOBuffer, n::Integer) = (seek(io.io, n); io)
486-
seekend(io::AnnotatedIOBuffer) = (seekend(io.io); io)
487-
skip(io::AnnotatedIOBuffer, n::Integer) = (skip(io.io, n); io)
488-
copy(io::AnnotatedIOBuffer) = AnnotatedIOBuffer(copy(io.io), copy(io.annotations))
489-
490-
annotations(io::AnnotatedIOBuffer) = io.annotations
491-
492-
annotate!(io::AnnotatedIOBuffer, range::UnitRange{Int}, label::Symbol, @nospecialize(val::Any)) =
493-
(_annotate!(io.annotations, range, label, val); io)
494-
495-
function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}})
496-
astr = AnnotatedString(astr)
497-
offset = position(io.io)
498-
eof(io) || _clear_annotations_in_region!(io.annotations, offset+1:offset+ncodeunits(astr))
499-
_insert_annotations!(io, astr.annotations)
500-
write(io.io, String(astr))
501-
end
502-
503-
write(io::AnnotatedIOBuffer, c::AnnotatedChar) =
504-
write(io, AnnotatedString(string(c), [(region=1:ncodeunits(c), a...) for a in c.annotations]))
505-
write(io::AnnotatedIOBuffer, x::AbstractString) = write(io.io, x)
506-
write(io::AnnotatedIOBuffer, s::Union{SubString{String}, String}) = write(io.io, s)
507-
write(io::AnnotatedIOBuffer, b::UInt8) = write(io.io, b)
508-
509-
function write(dest::AnnotatedIOBuffer, src::AnnotatedIOBuffer)
510-
destpos = position(dest)
511-
isappending = eof(dest)
512-
srcpos = position(src)
513-
nb = write(dest.io, src.io)
514-
isappending || _clear_annotations_in_region!(dest.annotations, destpos:destpos+nb)
515-
srcannots = [setindex(annot, max(1 + srcpos, first(annot.region)):last(annot.region), :region)
516-
for annot in src.annotations if first(annot.region) >= srcpos]
517-
_insert_annotations!(dest, srcannots, destpos - srcpos)
518-
nb
519-
end
520-
521-
# So that read/writes with `IOContext` (and any similar `AbstractPipe` wrappers)
522-
# work as expected.
523-
function write(io::AbstractPipe, s::Union{AnnotatedString, SubString{<:AnnotatedString}})
524-
if pipe_writer(io) isa AnnotatedIOBuffer
525-
write(pipe_writer(io), s)
526-
else
527-
invoke(write, Tuple{IO, typeof(s)}, io, s)
528-
end::Int
529-
end
530-
# Can't be part of the `Union` above because it introduces method ambiguities
531-
function write(io::AbstractPipe, c::AnnotatedChar)
532-
if pipe_writer(io) isa AnnotatedIOBuffer
533-
write(pipe_writer(io), c)
534-
else
535-
invoke(write, Tuple{IO, typeof(c)}, io, c)
536-
end::Int
537-
end
538-
539-
"""
540-
_clear_annotations_in_region!(annotations::Vector{$RegionAnnotation}, span::UnitRange{Int})
541-
542-
Erase the presence of `annotations` within a certain `span`.
543-
544-
This operates by removing all elements of `annotations` that are entirely
545-
contained in `span`, truncating ranges that partially overlap, and splitting
546-
annotations that subsume `span` to just exist either side of `span`.
547-
"""
548-
function _clear_annotations_in_region!(annotations::Vector{RegionAnnotation}, span::UnitRange{Int})
549-
# Clear out any overlapping pre-existing annotations.
550-
filter!(ann -> first(ann.region) < first(span) || last(ann.region) > last(span), annotations)
551-
extras = Tuple{Int, RegionAnnotation}[]
552-
for i in eachindex(annotations)
553-
annot = annotations[i]
554-
region = annot.region
555-
# Test for partial overlap
556-
if first(region) <= first(span) <= last(region) || first(region) <= last(span) <= last(region)
557-
annotations[i] =
558-
setindex(annot,
559-
if first(region) < first(span)
560-
first(region):first(span)-1
561-
else
562-
last(span)+1:last(region)
563-
end,
564-
:region)
565-
# If `span` fits exactly within `region`, then we've only copied over
566-
# the beginning overhang, but also need to conserve the end overhang.
567-
if first(region) < first(span) && last(span) < last(region)
568-
push!(extras, (i, setindex(annot, last(span)+1:last(region), :region)))
569-
end
570-
end
571-
end
572-
# Insert any extra entries in the appropriate position
573-
for (offset, (i, entry)) in enumerate(extras)
574-
insert!(annotations, i + offset, entry)
575-
end
576-
annotations
577-
end
578-
579-
"""
580-
_insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{$RegionAnnotation}, offset::Int = position(io))
581-
582-
Register new `annotations` in `io`, applying an `offset` to their regions.
583-
584-
The largely consists of simply shifting the regions of `annotations` by `offset`
585-
and pushing them onto `io`'s annotations. However, when it is possible to merge
586-
the new annotations with recent annotations in accordance with the semantics
587-
outlined in [`AnnotatedString`](@ref), we do so. More specifically, when there
588-
is a run of the most recent annotations that are also present as the first
589-
`annotations`, with the same value and adjacent regions, the new annotations are
590-
merged into the existing recent annotations by simply extending their range.
591-
592-
This is implemented so that one can say write an `AnnotatedString` to an
593-
`AnnotatedIOBuffer` one character at a time without needlessly producing a
594-
new annotation for each character.
595-
"""
596-
function _insert_annotations!(io::AnnotatedIOBuffer, annotations::Vector{RegionAnnotation}, offset::Int = position(io))
597-
run = 0
598-
if !isempty(io.annotations) && last(last(io.annotations).region) == offset
599-
for i in reverse(axes(annotations, 1))
600-
annot = annotations[i]
601-
first(annot.region) == 1 || continue
602-
i <= length(io.annotations) || continue
603-
if annot.label == last(io.annotations).label && annot.value == last(io.annotations).value
604-
valid_run = true
605-
for runlen in 1:i
606-
new = annotations[begin+runlen-1]
607-
old = io.annotations[end-i+runlen]
608-
if last(old.region) != offset || first(new.region) != 1 || old.label != new.label || old.value != new.value
609-
valid_run = false
610-
break
611-
end
612-
end
613-
if valid_run
614-
run = i
615-
break
616-
end
617-
end
618-
end
619-
end
620-
for runindex in 0:run-1
621-
old_index = lastindex(io.annotations) - run + 1 + runindex
622-
old = io.annotations[old_index]
623-
new = annotations[begin+runindex]
624-
io.annotations[old_index] = setindex(old, first(old.region):last(new.region)+offset, :region)
625-
end
626-
for index in run+1:lastindex(annotations)
627-
annot = annotations[index]
628-
start, stop = first(annot.region), last(annot.region)
629-
push!(io.annotations, setindex(annotations[index], start+offset:stop+offset, :region))
630-
end
631-
end
632-
633-
function read(io::AnnotatedIOBuffer, ::Type{AnnotatedString{T}}) where {T <: AbstractString}
634-
if (start = position(io)) == 0
635-
AnnotatedString(read(io.io, T), copy(io.annotations))
636-
else
637-
annots = [setindex(annot, UnitRange{Int}(max(1, first(annot.region) - start), last(annot.region)-start), :region)
638-
for annot in io.annotations if last(annot.region) > start]
639-
AnnotatedString(read(io.io, T), annots)
640-
end
641-
end
642-
read(io::AnnotatedIOBuffer, ::Type{AnnotatedString{AbstractString}}) = read(io, AnnotatedString{String})
643-
read(io::AnnotatedIOBuffer, ::Type{AnnotatedString}) = read(io, AnnotatedString{String})
644-
645-
function read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar{T}}) where {T <: AbstractChar}
646-
pos = position(io)
647-
char = read(io.io, T)
648-
annots = [NamedTuple{(:label, :value)}(annot) for annot in io.annotations if pos+1 in annot.region]
649-
AnnotatedChar(char, annots)
650-
end
651-
read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar{AbstractChar}}) = read(io, AnnotatedChar{Char})
652-
read(io::AnnotatedIOBuffer, ::Type{AnnotatedChar}) = read(io, AnnotatedChar{Char})
653-
654-
function truncate(io::AnnotatedIOBuffer, size::Integer)
655-
truncate(io.io, size)
656-
filter!(ann -> first(ann.region) <= size, io.annotations)
657-
map!(ann -> setindex(ann, first(ann.region):min(size, last(ann.region)), :region),
658-
io.annotations, io.annotations)
659-
io
660-
end

0 commit comments

Comments
 (0)