Skip to content

Commit 03211c9

Browse files
committed
Small optimisation to single-interpolation styled
When a single styled"{$face:$content}" form is used, this optimisation makes the macroexpansion equivalent to handwritten code for this case, as the annotatedstring_optimize! call is superfluous when there is only one part. While we're at it, we may as well make annotatedstring_optimize! more efficient for the zero and one annotation cases.
1 parent d080103 commit 03211c9

File tree

2 files changed

+24
-19
lines changed

2 files changed

+24
-19
lines changed

src/styledmarkup.jl

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ Its fields are as follows:
8181
styled string, as markup structures are absorbed.
8282
- `point::Int`, the current index in `content`.
8383
- `escape::Bool`, whether the last character seen was an escape character.
84-
- `interpolated::Bool`, whether any interpolated values have been seen. Knowing whether or not
84+
- `interpolations::Int`, how many interpolated values have been seen. Knowing whether or not
8585
anything needs to be evaluated allows the resulting string to be computed at macroexpansion time,
86-
when possible.
86+
when possible, and knowing how many allows for some micro-optimisations.
8787
- `errors::Vector`, any errors raised during parsing. We collect them instead of immediately throwing
8888
so that we can list as many issues as possible at once, instead of forcing the author of the invalid
8989
styled markup to resolve each issue one at a time. This is expected to be populated by invocations of
@@ -100,7 +100,7 @@ mutable struct State
100100
offset::Int
101101
point::Int
102102
escape::Bool
103-
interpolated::Bool
103+
interpolations::Int
104104
const errors::Vector
105105
end
106106

@@ -111,7 +111,7 @@ function State(content::AbstractString, mod::Union{Module, Nothing}=nothing)
111111
Vector{Tuple{Int, Int, Union{Symbol, Expr, Pair{Symbol, Any}}}}[], # active_styles
112112
Tuple{UnitRange{Int}, Union{Symbol, Expr, Pair{Symbol, Any}}}[], # pending_styles
113113
0, 1, # offset, point
114-
false, false, # escape, interpolated
114+
false, 0, # escape, interpolations
115115
NamedTuple{(:message, :position, :hint), # errors
116116
Tuple{AnnotatedString{String}, <:Union{Int, Nothing}, String}}[])
117117
end
@@ -311,7 +311,7 @@ function interpolated!(state::State, i::Int, _)
311311
state.offset -= ncodeunits('$')
312312
addpart!(state, i, esc(expr), nexti)
313313
state.point = nexti + state.offset
314-
state.interpolated = true
314+
state.interpolations += 1
315315
end
316316

317317
"""
@@ -511,7 +511,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
511511
elseif isnextchar(state, '$') && ismacro(state)
512512
expr, _ = readexpr!(state)
513513
lastchar = last(popfirst!(state.s))
514-
state.interpolated = true
514+
state.interpolations += 1
515515
needseval = true
516516
esc(expr)
517517
else
@@ -525,7 +525,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
525525
if isnextchar(state, '$') && ismacro(state)
526526
expr, _ = readexpr!(state)
527527
lastchar = last(popfirst!(state.s))
528-
state.interpolated = true
528+
state.interpolations += 1
529529
needseval = true
530530
ustyle = esc(expr)
531531
else
@@ -639,7 +639,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
639639
val = if ismacro(state) && isnextchar(state, '$')
640640
expr, _ = readexpr!(state)
641641
lastchar = last(popfirst!(state.s))
642-
state.interpolated = true
642+
state.interpolations += 1
643643
needseval = true
644644
esc(expr)
645645
elseif key == :font
@@ -766,7 +766,7 @@ function read_face_or_keyval!(state::State, i::Int, char::Char, newstyles)
766766
# this isn't the 'last' char yet, but it will be
767767
key = if ismacro(state) && last(peek(state.s)) == '$'
768768
expr, _ = readexpr!(state)
769-
state.interpolated = true
769+
state.interpolations += 1
770770
needseval = true
771771
esc(expr)
772772
else
@@ -788,7 +788,7 @@ function read_face_or_keyval!(state::State, i::Int, char::Char, newstyles)
788788
read_curlywrapped!(state)
789789
elseif ismacro(state) && nextchar == '$'
790790
expr, _ = readexpr!(state)
791-
state.interpolated = true
791+
state.interpolations += 1
792792
needseval = true
793793
esc(expr)
794794
else
@@ -868,6 +868,7 @@ end
868868
Merge contiguous identical annotations in `str`.
869869
"""
870870
function annotatedstring_optimize!(s::AnnotatedString)
871+
length(s.annotations) <= 1 && return s
871872
last_seen = Dict{Pair{Symbol, Any}, Int}()
872873
i = 1
873874
while i <= length(s.annotations)
@@ -988,7 +989,9 @@ macro styled_str(raw_content::String)
988989
run_state_machine!(state)
989990
if !isempty(state.errors)
990991
throw(MalformedStylingMacro(state.content, state.errors))
991-
elseif state.interpolated
992+
elseif state.interpolations == 1 && length(state.parts) == 1
993+
:(annotatedstring($(first(state.parts))))
994+
elseif !iszero(state.interpolations)
992995
:(annotatedstring($(state.parts...)) |> annotatedstring_optimize!)
993996
else
994997
annotatedstring(map(Base.Fix1(hygienic_eval, state), state.parts)...) |> annotatedstring_optimize!

test/runtests.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -404,18 +404,21 @@ end
404404
Pair = GlobalRef(StyledMarkup, :Pair)
405405
Symbol = GlobalRef(StyledMarkup, :Symbol)
406406
Any = GlobalRef(StyledMarkup, :Any)
407-
@test :($chain($annotatedstring(val), $annotatedstring_optimize!)) == @macroexpand styled"$val"
407+
@test :($annotatedstring(val)) == @macroexpand styled"$val"
408408
@test :($chain($annotatedstring("a", val), $annotatedstring_optimize!)) == @macroexpand styled"a$val"
409409
@test :($chain($annotatedstring("a", val, "b"), $annotatedstring_optimize!)) == @macroexpand styled"a$(val)b"
410410
# @test :($annotatedstring(StyledStrings.AnnotatedString(string(val), $(Pair{Symbol, Any}(:face, :style))))) ==
411411
# @macroexpand styled"{style:$val}"
412-
@test :($chain($annotatedstring($AnnotatedString(
413-
"val", [($(1:3), $Pair{$Symbol, $Any}(:face, face))])),
414-
$annotatedstring_optimize!)) ==
412+
@test :($annotatedstring($AnnotatedString(
413+
"val", [($(1:3), $Pair{$Symbol, $Any}(:face, face))]))) ==
415414
@macroexpand styled"{$face:val}"
416415
@test :($chain($annotatedstring($AnnotatedString(
417-
"val", [($(1:3), $Pair{$Symbol, $Any}(key, "val"))])),
416+
"v1v2", [($(1:2), $Pair{$Symbol, $Any}(:face, f1)),
417+
($(3:4), $Pair{$Symbol, $Any}(:face, f2))])),
418418
$annotatedstring_optimize!)) ==
419+
@macroexpand styled"{$f1:v1}{$f2:v2}"
420+
@test :($annotatedstring($AnnotatedString(
421+
"val", [($(1:3), $Pair{$Symbol, $Any}(key, "val"))]))) ==
419422
@macroexpand styled"{$key=val:val}"
420423
@test :($chain($annotatedstring($AnnotatedString(
421424
"val", [($(1:3), $Pair{$Symbol, $Any}(key, val))])),
@@ -424,9 +427,8 @@ end
424427
# @test :($annotatedstring($AnnotatedString(
425428
# string(val), $Pair{$Symbol, $Any}(key, val)))) ==
426429
# @macroexpand styled"{$key=$val:$val}"
427-
@test :($chain($annotatedstring($AnnotatedString(
428-
"val", [($(1:3), $Pair{$Symbol, $Any}(:face, $(Face)(foreground = color)))])),
429-
$annotatedstring_optimize!)) ==
430+
@test :($annotatedstring($AnnotatedString(
431+
"val", [($(1:3), $Pair{$Symbol, $Any}(:face, $(Face)(foreground = color)))]))) ==
430432
@macroexpand styled"{(foreground=$color):val}"
431433
end
432434

0 commit comments

Comments
 (0)