From 75f46546d58d2fc400fad48008e24ae79032f69a Mon Sep 17 00:00:00 2001 From: TEC Date: Sat, 21 Oct 2023 19:48:18 +0800 Subject: [PATCH] Introduce AnnotatedIOBuffer This allows for styled content to be constructed incrementally, without resorting to repeated concatenation. It operates very similarly to IOContext, just with a special `write` method and specifically wrapping an IOBuffer. --- base/strings/annotated.jl | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/base/strings/annotated.jl b/base/strings/annotated.jl index c0f3623f416963..0f7f0a8b8c1290 100644 --- a/base/strings/annotated.jl +++ b/base/strings/annotated.jl @@ -386,3 +386,47 @@ annotations(s::SubString{<:AnnotatedString}, pos::UnitRange{<:Integer}) = Get all annotations of `chr`. """ annotations(c::AnnotatedChar) = c.annotations + +## AnnotatedIOBuffer + +struct AnnotatedIOBuffer <: IO + io::IOBuffer + annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} +end + +AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}()) +AnnotatedIOBuffer() = AnnotatedIOBuffer(IOBuffer()) + +function show(io::IO, annio::AnnotatedIOBuffer) + show(io, AnnotatedIOBuffer) + print(io, '(', annio.io.size, " bytes)") +end + +position(io::AnnotatedIOBuffer) = position(io.io) +lock(io::AnnotatedIOBuffer) = lock(io.io) +unlock(io::AnnotatedIOBuffer) = unlock(io.io) + +function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}}) + astr = AnnotatedString(astr) + offset = position(io.io) + for (region, annot) in astr.annotations + start, stop = first(region), last(region) + push!(io.annotations, (start+offset:stop+offset, annot)) + end + write(io.io, astr) +end +write(io::AnnotatedIOBuffer, achr::AnnotatedChar) = write(io, AnnotatedString(achr)) +write(io::AnnotatedIOBuffer, x::AbstractString) = write(io.io, x) +write(io::AnnotatedIOBuffer, s::Union{SubString{String}, String}) = write(io.io, x) +write(io::AnnotatedIOBuffer, x::UInt8) = write(io.io, x) + +function take!(aiob::AnnotatedIOBuffer) + str = String(take!(aiob.io)) + annot = copy(aiob.annotations) + empty!(aiob.annotations) + seekstart(aiob.io) + str, annot +end + +AnnotatedString((str, annots)::Tuple{<:AbstractString, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}}) = + AnnotatedString(str, annots)