Skip to content

Commit 002f07a

Browse files
authored
Improve the annotated join method and dispatch (#51914)
With the initial implementation, join could work for AnnotatedStrings, however only when the eltype of the iterator or delim was itself a Annotated{String,Char}. This was better than nothing, but seems inconsistent in the face of mixed iterators. Furthermore, the implementation of an annotated join was far from optimised, relying on zipping and then calling annotatedstring(xs...). By contrast, the non-annotated implementation relies on printing to IO and even has manually defined alternative methods for optional arguments to minimise code generation. With the advent of AnnotatedIOBuffer and _isannotated, we can improve on both those aspects. The new AnnotatedIOBuffer type allows us to re-use the optimised join(::IO, ...) methods, and we can more reliably dispatch to them with _isannotated. Since this is a type-based decision, the Julia compiler is kind enough to work out which branch is taken at compile-time, making this zero-overhead in the unannotated case.
1 parent efa77cc commit 002f07a

File tree

2 files changed

+10
-19
lines changed

2 files changed

+10
-19
lines changed

base/strings/io.jl

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -353,28 +353,19 @@ function join(io::IO, iterator, delim="")
353353
end
354354
end
355355

356-
# TODO: If/when we have `AnnotatedIO`, we can revisit this and
357-
# implement it more nicely.
358-
function join_annotated(iterator, delim="", last=delim)
359-
xs = zip(iterator, Iterators.repeated(delim)) |> Iterators.flatten |> collect
360-
xs = xs[1:end-1]
361-
if length(xs) > 1
362-
xs[end-1] = last
363-
end
364-
annotatedstring(xs...)::AnnotatedString{String}
365-
end
366-
367-
function _join_maybe_annotated(args...)
368-
if any(_isannotated eltype, args)
369-
join_annotated(args...)
356+
function _join_preserve_annotations(iterator, args...)
357+
if _isannotated(eltype(iterator)) || any(_isannotated, args)
358+
io = AnnotatedIOBuffer()
359+
join(io, iterator, args...)
360+
read(seekstart(io), AnnotatedString{String})
370361
else
371-
sprint(join, args...)
362+
sprint(join, iterator, args...)
372363
end
373364
end
374365

375-
join(iterator) = _join_maybe_annotated(iterator)
376-
join(iterator, delim) = _join_maybe_annotated(iterator, delim)
377-
join(iterator, delim, last) = _join_maybe_annotated(iterator, delim, last)
366+
join(iterator) = _join_preserve_annotations(iterator)
367+
join(iterator, delim) = _join_preserve_annotations(iterator, delim)
368+
join(iterator, delim, last) = _join_preserve_annotations(iterator, delim, last)
378369

379370
## string escaping & unescaping ##
380371

test/strings/annotated.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ end
9191
(6:11, :other => 0x02)])
9292
str1 = Base.AnnotatedString("test", [(1:4, :label => 5)])
9393
str2 = Base.AnnotatedString("case", [(2:3, :label => "oomph")])
94-
@test join([str1, str1], Base.AnnotatedString(" ")) ==
94+
@test join([str1, str1], ' ') ==
9595
Base.AnnotatedString("test test",
9696
[(1:4, :label => 5),
9797
(6:9, :label => 5)])

0 commit comments

Comments
 (0)