Skip to content

Commit 87a832a

Browse files
committed
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.
1 parent f99e6bf commit 87a832a

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

base/show.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,11 @@ function show_circular(io::IOContext, @nospecialize(x))
429429
return false
430430
end
431431

432+
# An AnnotatedIOBuffer specialisation that needs to be made
433+
# here as `annotated.jl` is loaded earlier.
434+
get(io::IOContext{AnnotatedIOBuffer}, key, default) =
435+
if key === :color false else get(io.dict, key, default) end
436+
432437
"""
433438
show([io::IO = stdout], x)
434439

base/strings/annotated.jl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,53 @@ annotations(s::SubString{<:AnnotatedString}, pos::UnitRange{<:Integer}) =
386386
Get all annotations of `chr`.
387387
"""
388388
annotations(c::AnnotatedChar) = c.annotations
389+
390+
## AnnotatedIOBuffer
391+
392+
struct AnnotatedIOBuffer <: IO
393+
io::IOBuffer
394+
annotations::Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}
395+
end
396+
397+
AnnotatedIOBuffer(io::IOBuffer) = AnnotatedIOBuffer(io, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}())
398+
AnnotatedIOBuffer() = AnnotatedIOBuffer(IOBuffer())
399+
400+
function show(io::IO, annio::AnnotatedIOBuffer)
401+
show(io, AnnotatedIOBuffer)
402+
print(io, '(', annio.io.size, " bytes)")
403+
end
404+
405+
position(io::AnnotatedIOBuffer) = position(io.io)
406+
lock(io::AnnotatedIOBuffer) = lock(io.io)
407+
unlock(io::AnnotatedIOBuffer) = unlock(io.io)
408+
409+
function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}})
410+
astr = AnnotatedString(astr)
411+
offset = position(io.io)
412+
for (region, annot) in astr.annotations
413+
start, stop = first(region), last(region)
414+
push!(io.annotations, (start+offset:stop+offset, annot))
415+
end
416+
write(io.io, astr)
417+
end
418+
write(io::AnnotatedIOBuffer, c::AnnotatedChar) = write(io, AnnotatedString(c))
419+
write(io::AnnotatedIOBuffer, x::AbstractString) = write(io.io, x)
420+
write(io::AnnotatedIOBuffer, s::Union{SubString{String}, String}) = write(io.io, s)
421+
write(io::AnnotatedIOBuffer, x::UInt8) = write(io.io, x)
422+
423+
"""
424+
read(io::AnnotatedIOBuffer, AnnotatedString)
425+
426+
Read the entirety of `io`, as an `AnnotatedString`. This preserves the
427+
annotations of any `AnnotatedString`s written to `io` and otherwise acts like
428+
`read(io::IO, String)`.
429+
"""
430+
function read(io::AnnotatedIOBuffer, ::Type{AnnotatedString{String}})
431+
str = String(take!(io.io))
432+
annots = copy(io.annotations)
433+
empty!(io.annotations)
434+
seekstart(io.io)
435+
AnnotatedString(str, annots)
436+
end
437+
read(io::AnnotatedIOBuffer, ::Type{AnnotatedString{AbstractString}}) = read(io, AnnotatedString{String})
438+
read(io::AnnotatedIOBuffer, ::Type{AnnotatedString}) = read(io, AnnotatedString{String})

0 commit comments

Comments
 (0)