Skip to content

Commit b67d93b

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 f71228d commit b67d93b

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

base/show.jl

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

432+
AnnotatedIOBuffer(io::IOContext{IOBuffer}) = AnnotatedIOBuffer(io.io, io.dict)
433+
IOContext(io::AnnotatedIOBuffer) = IOContext(io.io, io.dict)
434+
432435
"""
433436
show([io::IO = stdout], x)
434437

base/strings/annotated.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,60 @@ 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+
dict::ImmutableDict{Symbol, Any}
396+
end
397+
398+
AnnotatedIOBuffer(io::IOBuffer, dict::ImmutableDict = ImmutableDict{Symbol,Any}()) =
399+
AnnotatedIOBuffer(io, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}(), dict)
400+
401+
function AnnotatedIOBuffer(io::IOBuffer, (k, v)::Pair{Symbol, Any}, kvs::Pair{Symbol, Any}...)
402+
dict = ImmutableDict{Symbol,Any}(k, v)
403+
for (k, v) in kvs
404+
dict = ImmutableDict{Symbol,Any}(dict, k, v)
405+
end
406+
AnnotatedIOBuffer(io, dict)
407+
end
408+
409+
AnnotatedIOBuffer() = AnnotatedIOBuffer(IOBuffer())
410+
411+
function show(io::IO, annio::AnnotatedIOBuffer)
412+
show(io, AnnotatedIOBuffer)
413+
print(io, '(', annio.io.size, " bytes)")
414+
end
415+
416+
position(io::AnnotatedIOBuffer) = position(io.io)
417+
lock(io::AnnotatedIOBuffer) = lock(io.io)
418+
unlock(io::AnnotatedIOBuffer) = unlock(io.io)
419+
get(io::AnnotatedIOBuffer, key, default) = key == :color || get(io.dict, key, default)
420+
displaysize(io::AnnotatedIOBuffer) =
421+
if haskey(io.dict, :displaysize) io.dict[:displaysize]::Tuple{Int,Int} else displaysize(io.io) end
422+
423+
function write(io::AnnotatedIOBuffer, astr::Union{AnnotatedString, SubString{<:AnnotatedString}})
424+
astr = AnnotatedString(astr)
425+
offset = position(io.io)
426+
for (region, annot) in astr.annotations
427+
start, stop = first(region), last(region)
428+
push!(io.annotations, (start+offset:stop+offset, annot))
429+
end
430+
write(io.io, astr)
431+
end
432+
write(io::AnnotatedIOBuffer, achr::AnnotatedChar) = write(aio, AnnotatedString(achr))
433+
write(io::AnnotatedIOBuffer, x::AbstractString) = write(io.io, x)
434+
write(io::AnnotatedIOBuffer, x::UInt8) = write(io.io, x)
435+
436+
function take!(aiob::AnnotatedIOBuffer)
437+
str = String(take!(aiob.io))
438+
annot = copy(aiob.annotations)
439+
empty!(aiob.annotations)
440+
seekstart(aiob.io)
441+
str, annot
442+
end
443+
444+
AnnotatedString((str, annots)::Tuple{<:AbstractString, Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}}) =
445+
AnnotatedString(str, annots)

0 commit comments

Comments
 (0)