@@ -323,14 +323,15 @@ To remove existing `label` annotations, use a value of `nothing`.
323
323
"""
324
324
function annotate! (s:: AnnotatedString , range:: UnitRange{Int} , @nospecialize (labelval:: Pair{Symbol, <:Any} ))
325
325
label, val = labelval
326
- indices = searchsorted (s. annotations, (range,), by= first)
327
326
if val === nothing
327
+ indices = searchsorted (s. annotations, (range,), by= first)
328
328
labelindex = filter (i -> first (s. annotations[i][2 ]) === label, indices)
329
329
for index in Iterators. reverse (labelindex)
330
330
deleteat! (s. annotations, index)
331
331
end
332
332
else
333
- splice! (s. annotations, indices, [(range, Pair {Symbol, Any} (label, val))])
333
+ sortedindex = searchsortedlast (s. annotations, (range,), by= first) + 1
334
+ insert! (s. annotations, sortedindex, (range, Pair {Symbol, Any} (label, val)))
334
335
end
335
336
s
336
337
end
@@ -386,3 +387,126 @@ annotations(s::SubString{<:AnnotatedString}, pos::UnitRange{<:Integer}) =
386
387
Get all annotations of `chr`.
387
388
"""
388
389
annotations (c:: AnnotatedChar ) = c. annotations
390
+
391
+ # # AnnotatedIOBuffer
392
+
393
+ struct AnnotatedIOBuffer <: AbstractPipe
394
+ io:: IOBuffer
395
+ annotations:: Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}}
396
+ end
397
+
398
+ AnnotatedIOBuffer (io:: IOBuffer ) = AnnotatedIOBuffer (io, Vector {Tuple{UnitRange{Int}, Pair{Symbol, Any}}} ())
399
+ AnnotatedIOBuffer () = AnnotatedIOBuffer (IOBuffer ())
400
+
401
+ function show (io:: IO , aio:: AnnotatedIOBuffer )
402
+ show (io, AnnotatedIOBuffer)
403
+ print (io, ' (' , aio. io. size, " byte" , ifelse (aio. io. size == 1 , " " , " s" ), " , " ,
404
+ length (aio. annotations), " annotation" , ifelse (length (aio. annotations) == 1 , " " , " s" ), " )" )
405
+ end
406
+
407
+ pipe_reader (io:: AnnotatedIOBuffer ) = io. io
408
+ pipe_writer (io:: AnnotatedIOBuffer ) = io. io
409
+
410
+ # Useful `IOBuffer` methods that we don't get from `AbstractPipe`
411
+ position (io:: AnnotatedIOBuffer ) = position (io. io)
412
+ seek (io:: AnnotatedIOBuffer , n:: Integer ) = (seek (io. io, n); io)
413
+ seekend (io:: AnnotatedIOBuffer ) = seekend (io. io)
414
+ skip (io:: AnnotatedIOBuffer , n:: Integer ) = (skip (io. io, n); io)
415
+ copy (io:: AnnotatedIOBuffer ) = AnnotatedIOBuffer (copy (io. io), copy (io. annotations))
416
+
417
+ annotations (io:: AnnotatedIOBuffer ) = io. annotations
418
+
419
+ function write (io:: AnnotatedIOBuffer , astr:: Union{AnnotatedString, SubString{<:AnnotatedString}} )
420
+ astr = AnnotatedString (astr)
421
+ offset = position (io. io)
422
+ eof (io) || _clear_annotations_in_region! (io. annotations, offset+ 1 : offset+ ncodeunits (astr))
423
+ _insert_annotations! (io, astr. annotations)
424
+ write (io. io, String (astr))
425
+ end
426
+
427
+ write (io:: AnnotatedIOBuffer , c:: AnnotatedChar ) = write (io, AnnotatedString (c))
428
+ write (io:: AnnotatedIOBuffer , x:: AbstractString ) = write (io. io, x)
429
+ write (io:: AnnotatedIOBuffer , s:: Union{SubString{String}, String} ) = write (io. io, s)
430
+ write (io:: AnnotatedIOBuffer , b:: UInt8 ) = write (io. io, b)
431
+
432
+ function write (dest:: AnnotatedIOBuffer , src:: AnnotatedIOBuffer )
433
+ destpos = position (dest)
434
+ isappending = eof (dest)
435
+ srcpos = position (src)
436
+ nb = write (dest. io, src. io)
437
+ isappending || _clear_annotations_in_region! (dest. annotations, destpos: destpos+ nb)
438
+ srcannots = [(max (1 + srcpos, first (region)): last (region), annot)
439
+ for (region, annot) in src. annotations if first (region) >= srcpos]
440
+ _insert_annotations! (dest, srcannots, destpos - srcpos)
441
+ nb
442
+ end
443
+
444
+ function _clear_annotations_in_region! (annotations:: Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} , span:: UnitRange{Int} )
445
+ # Clear out any overlapping pre-existing annotations.
446
+ filter! (((region, _),) -> first (region) < first (span) || last (region) > last (span), annotations)
447
+ extras = Tuple{UnitRange{Int}, Pair{Symbol, Any}}[]
448
+ for i in eachindex (annotations)
449
+ region, annot = annotations[i]
450
+ # Test for partial overlap
451
+ if first (region) <= first (span) <= last (region) || first (region) <= last (span) <= last (region)
452
+ annotations[i] = (if first (region) < first (span)
453
+ first (region): first (span)- 1
454
+ else last (span)+ 1 : last (region) end , annot)
455
+ # If `span` fits exactly within `region`, then we've only copied over
456
+ # the beginning overhang, but also need to conserve the end overhang.
457
+ if first (region) < first (span) && last (span) < last (region)
458
+ push! (extras, (last (span)+ 1 : last (region), annot))
459
+ end
460
+ end
461
+ # Insert any extra entries in the appropriate position
462
+ for entry in extras
463
+ sortedindex = searchsortedlast (annotations, (first (entry),), by= first) + 1
464
+ insert! (annotations, sortedindex, entry)
465
+ end
466
+ end
467
+ annotations
468
+ end
469
+
470
+ function _insert_annotations! (io:: AnnotatedIOBuffer , annotations:: Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} , offset:: Int = position (io))
471
+ if ! eof (io)
472
+ for (region, annot) in annotations
473
+ region = first (region)+ offset: last (region)+ offset
474
+ sortedindex = searchsortedlast (io. annotations, (region,), by= first) + 1
475
+ insert! (io. annotations, sortedindex, (region, annot))
476
+ end
477
+ else
478
+ for (region, annot) in annotations
479
+ region = first (region)+ offset: last (region)+ offset
480
+ push! (io. annotations, (region, annot))
481
+ end
482
+ end
483
+ end
484
+
485
+ function read (io:: AnnotatedIOBuffer , :: Type{AnnotatedString{T}} ) where {T <: AbstractString }
486
+ if (start = position (io)) == 0
487
+ AnnotatedString (read (io. io, T), copy (io. annotations))
488
+ else
489
+ annots = [(max (1 , first (region) - start): last (region)- start, val)
490
+ for (region, val) in io. annotations if last (region) > start]
491
+ AnnotatedString (read (io. io, T), annots)
492
+ end
493
+ end
494
+ read (io:: AnnotatedIOBuffer , :: Type{AnnotatedString{AbstractString}} ) = read (io, AnnotatedString{String})
495
+ read (io:: AnnotatedIOBuffer , :: Type{AnnotatedString} ) = read (io, AnnotatedString{String})
496
+
497
+ function read (io:: AnnotatedIOBuffer , :: Type{AnnotatedChar{T}} ) where {T <: AbstractChar }
498
+ pos = position (io)
499
+ char = read (io. io, T)
500
+ annots = [annot for (range, annot) in io. annotations if pos+ 1 in range]
501
+ AnnotatedChar (char, annots)
502
+ end
503
+ read (io:: AnnotatedIOBuffer , :: Type{AnnotatedChar{AbstractChar}} ) = read (io, AnnotatedChar{Char})
504
+ read (io:: AnnotatedIOBuffer , :: Type{AnnotatedChar} ) = read (io, AnnotatedChar{Char})
505
+
506
+ function truncate (io:: AnnotatedIOBuffer , size:: Integer )
507
+ truncate (io. io, size)
508
+ filter! (((range, _),) -> first (range) <= size, io. annotations)
509
+ map! (((range, val),) -> (first (range): min (size, last (range)), val),
510
+ io. annotations, io. annotations)
511
+ io
512
+ end
0 commit comments