Repository navigation
Conversation
04651e1 to
bce32f4
Compare
bce32f4 to
a5e0e17
Compare
e40a123 to
e514004
Compare
|
There are a few oddities with CI that I'd appreciate help working out how to resolve: I tried adding This confuses me, since |
Try adding it to julia/contrib/generate_precompile.jl Line 33 in e8bf6c4 |
e514004 to
13bcc37
Compare
13bcc37 to
148c6fd
Compare
topolarity
left a comment
There was a problem hiding this comment.
Thanks for those responses.
I will give another round of "proper" review soon (Monday).. until then, what do you think about restoring to the "default" Face using proper "reset" ANSI codes, instead of "set" codes? I believe the terminal supports this even for individual text attributes.
I am curious if that behaves better under default Face "mismatch" (it should fix my complaint in (2) without affecting your blending argument)
cf7c5a8 to
054a09c
Compare
Annotated strings held their annotation values as `Any`. Nothing about
an `AnnotatedString{String}` said what its annotations held, so code
that read or displayed one could not dispatch on it.
Annotations now have a value type parameter: `AnnotatedString{S, V}`,
and likewise `AnnotatedChar`, `Annotation`, `RegionAnnotation` and
`AnnotatedIOBuffer`. A one-parameter form, such as
`AnnotatedString{String}`, covers every `V`, and the one-parameter
constructors infer it.
- Construction infers `V` from the annotations given. Constructing from
an existing annotated string or char copies its annotations, while
`convert` returns it as it is. Adding annotations to one widens `V`,
as `vcat` does.
- `annotatedstring`, `*` and `replace` widen `V` to the union of the
value types, up to three of them, and then to `Any`. `join` keeps a
shared `V`, and otherwise gives `Any`. Values are not converted to a
common type.
- `annotate!` refuses a value outside `V` with an `ArgumentError`.
Writing or replacing into a typed buffer converts the values to its
`V`, and reading converts them to the requested type.
- An annotated buffer merges adjacent runs only when their values are
identical. Equal but distinct values keep their own runs.
The `invoke_in_world` barrier around every annotated write is replaced by a trait on the value type, `Base.AnnotatedDisplay.AnnotationStyle`. A package declares a style for its value type and implements `awrite(textwriter, style, io, s)`; `write`, `print` and `escape_string` all go through it. A style may add a `text/html` method, which `showable` reports. `NoStyle` writes the plain text and has no MIME form. Only `Any`-valued strings still work out their style from the values they hold, behind one remaining barrier, and `showable` does the same for them. Escaping into an `AnnotatedIOBuffer`, bare or inside an `IOContext`, keeps the annotations, moved to cover the escaped text. `print` goes through `write`, so printing into an `IOContext` around such a buffer keeps them too. When two styles meet, a rule `AnnotationStyle(a, b)` settles which applies. It is tried in both orders, as `promote_rule` is, and a missing rule raises an `ArgumentError` naming the method to define. Both trait functions have `max_methods = 1`. A call whose value type is not known at compile time then sees two applicable methods, so inference leaves it dynamic and records no method-table edge. This means a package's style and writer definitions cannot invalidate Base's compiled callers, which a test checks in a fresh process.
`eachregion` built every region up front: a vector of ranges, and a fresh vector of annotations for each region, all before yielding anything. The iterator now walks the annotation events as it goes, keeping a sorted list of the annotations active over the current region. Each region is yielded as a substring, with its annotations as a view of that list, `ActiveAnnotations`. A view held after the iterator has moved on still works; it finds its annotations again by position, at a cost linear in the number of annotations. Events are packed into integers, so sorting them is a radix sort. The active list is updated by shifting entries within it, and `iterate` is inlined so the sweep's state stays in registers. A region now costs 8 ns in order and 15 ns under a nested annotation, down from 20 and 35, with nothing allocated per region. A subregion or an annotation that starts within a character is moved back to the start of that character, as the events are. The views read the string's annotations when they are used, so a string must not change while its regions are in use. If it does, reads stay in bounds: the iterator keeps the event layout it was built with, and an annotation index that is out of range is a `BoundsError`. Assisted-by: Claude Code (Fable 5.1)
Stdlib: StyledStrings URL: https://github.com/JuliaLang/StyledStrings.jl.git Stdlib branch: face-primacy Old commit: 41c13c2 New commit: b8c9246 Diff: JuliaLang/StyledStrings.jl@41c13c2...b8c9246 Stdlib: JuliaSyntaxHighlighting URL: https://github.com/JuliaLang/JuliaSyntaxHighlighting.jl.git Stdlib branch: faces-value-parameterisation Old commit: dae5817 New commit: 2061af0 Diff: JuliaLang/JuliaSyntaxHighlighting.jl@dae5817...2061af0 StyledStrings displays faces through the annotation style trait, and names faces with `Face` values held in module palettes (JuliaLang/StyledStrings.jl#131). Links are `Link` values, and `StyledValue` names the value type of styled strings. JuliaSyntaxHighlighting highlights with `Face` values from its own palette.
Markdown, Profile and the REPL named their faces with prefixed `Symbol`s, registered with `addface!`. They now declare their faces with `@defpalette`, register them from `__init__`, and refer to them as `face"name"` or by bare name inside `styled""`. The REPL's styling passes, Markdown and Profile build their strings with `StyledValue` values, as styled strings have, so that they share one value type and the code specialised on it, and redisplaying the input takes the static display path. Each styling pass must return such a string, a `StylingPasses.StyledString`. Markdown keeps only the annotations of interpolated text that its buffer can hold, and marks its links with `StyledStrings.Link`. Markdown also never styled `ERROR:` in REPL examples, as the check compared an annotated substring with a plain string. It now compares the plain text. The REPL's history search styled the character before each condition by its byte offset, which threw when that character took more than one byte. It now styles it only where a single-byte prefix or separator can be. Assisted-by: Claude Code (Opus 5.5)
`--trim=safe` reported two dynamic calls when loading a terminfo entry. The parts of `TERMINFO_DIRS` are now converted to `String`s, rather than kept as `SubString`s, and each kind of capability is inserted with its own concrete value type.
The Trimmability program now prints a styled string with ANSI escapes and as HTML. The string has a height, an underline colour and a link, so the writers compare successive faces. The test checks both lines against the same output from an untrimmed process. Assisted-by: Claude Code (Opus 5.5)
`merge_annotations` started from a copy of the first pass's string, then added the annotations of every pass. The first pass's annotations were therefore stored twice. The loop now skips the first pass. Assisted-by: Claude Code (Opus 5.5)
`join(split(s))` of an `AnnotatedString` returned a plain `String`.
`join` asks `_isannotated` about the element type, and only the method
for instances covered `SubString{<:AnnotatedString}`. A method for the
type now sits beside it.
Assisted-by: Claude Code (Opus 5.5)
`eltype(SubString{T})` was `Char` whatever `T`, while iterating a
substring of an annotated string yields `AnnotatedChar`s. So `collect`
of such a substring gave a `Vector{Char}` and dropped the annotations.
A substring's eltype is now that of its string.
Assisted-by: Claude Code (Opus 5.5)
`annotatedstring` summed its arguments' size hints with `mapreduce`, which compiles a fold for each combination of argument types. As the value types of annotations are now mostly defined outside Base, these folds were compiled into each package image that builds annotated strings: about 440 methods across Markdown, Profile and the REPL. A loop, as `string` uses, compiles none. Assisted-by: Claude Code (Opus 5.5)
`shell_completions` joined the arguments it parsed from a `Vector{Any}`,
and `string` and `join` infer that an annotated string may result from
that, so path completion was also compiled for
`AnnotatedString{String, Any}`. The arguments are strings, so they are
now joined as a `Vector{String}`. Likewise, the file name that the
history search's save prompt reads lost its type through a `try`, and
so did the styled message it is interpolated into. Together these take
about 0.5 MB off the REPL image.
Assisted-by: Claude Code (Opus 5.5)
Annotated strings were displayed through a trait: each value type named a style, a union's styles were promoted to one, and the style's writer showed the whole string. Two types without a promotion rule could not be shown together, and an `Any` string chose its style from its contents, so a string could print differently from the same text held more widely, or cut into parts. Now each value type says how its annotations look in a MIME type, with three functions. `context` reads what a display needs once, `reduction` gives a region's state from that type's annotations, and `transition` writes the change between two states. With no annotations, a reduction gives the baseline, where display starts and ends. A type that needs no context can leave it out of its reduction. `render` walks the regions and writes the transitions between their states. For plain text, each type's state changes on its own. For HTML, the types share one stack of nested elements, and an element is kept while its state still fits. A type without methods is not displayed, so a string displays the same whatever union of value types holds it. A known union's states are found by straight-line code. An `Any` string goes through one method, compiled once, which dispatches at runtime in the latest world, so new display methods do not invalidate its callers. `write`, `print`, `escape_string`, and `show` for HTML use `render`. HTML is offered for every annotated string, as `show` can always write it. Plain text without colour, or without annotations, is written as it is. StyledStrings now displays faces and links through these functions (JuliaLang/StyledStrings.jl#131), so it is bumped together: Stdlib: StyledStrings URL: https://github.com/JuliaLang/StyledStrings.jl.git Stdlib branch: face-primacy Old commit: b8c9246 New commit: 64e4cfd Diff: JuliaLang/StyledStrings.jl@b8c9246...64e4cfd Assisted-by: Claude Code (Opus 5.5)
The label of an annotation restated the type of its value: `:face` beside a `Face`, `:link` beside a `Link`. Storing both let them disagree, and labels shared one flat namespace across packages. An annotation is now a region and a value, `(; region, value)`. A char's annotations, and each region's from `eachregion`, are the values themselves. Annotations are given as `(region, value)`, `region => value`, or with `annotate!(str, [range], value)`. A labelled `(region, label, value)` is refused, rather than read as a region and a value. Markdown and the REPL annotate with values. Markdown's `with_output_annotations` is compiled once rather than for each closure it is given, so its call to the closure is dynamic. Markdown's terminal rendering already dispatches on untyped content, so this costs nothing under `--trim` now, but would need specialising if that rendering were made trimmable. StyledStrings and JuliaSyntaxHighlighting annotate with values alone, so they are bumped together: Stdlib: StyledStrings URL: https://github.com/JuliaLang/StyledStrings.jl.git Stdlib branch: face-primacy Old commit: 64e4cfd New commit: d6479d5 Diff: JuliaLang/StyledStrings.jl@64e4cfd...d6479d5 Stdlib: JuliaSyntaxHighlighting URL: https://github.com/JuliaLang/JuliaSyntaxHighlighting.jl.git Stdlib branch: faces-value-parameterisation Old commit: 2061af0 New commit: 9ef9da7 Diff: JuliaLang/JuliaSyntaxHighlighting.jl@2061af0...9ef9da7 Assisted-by: Claude Code (Opus 5.5)
Code written for labelled annotations keeps working. A labelled value
is kept as it is when it has the kind of its label, and otherwise
becomes `label => value`, a `Pair{Symbol, Any}`. The kind comes from
the internal `labelled_kind`, which is the label itself unless a
package gives it a type, as StyledStrings does for `:face` and `:link`.
Deletion with `nothing` removes the annotations of that kind.
Labelled writes with a value are deprecated. Deletion is not, as there
is no other way to delete yet. A vector of labelled annotations is
converted when all of it is labelled. Reading `.label` breaks.
All of it is in `deprecated.jl`, so that it can be removed as one.
StyledStrings gives `:face` and `:link` their kinds, so it is bumped
together:
Stdlib: StyledStrings
URL: https://github.com/JuliaLang/StyledStrings.jl.git
Stdlib branch: face-primacy
Old commit: d6479d5
New commit: 10c37a7
Diff:
JuliaLang/StyledStrings.jl@d6479d5...10c37a7
Assisted-by: Claude Code (Opus 5.5)
A value type with a member that isn't concrete now displays from the types of its values, as `Any` does, so a string displays the same whatever value type holds it. A reduction is called for each concrete type, and the manual and its docstring say that it selects its values with `value isa T`, so a reduction written for several types sees each type's values apart. Strings of `Any` values display through code compiled once. Their types are found without compiling a set for each. They share a known union's methods for plain text and HTML, where each type of state is checked on its own. They call the interface through barriers, so that a new value type's methods invalidate none of it. In HTML, a state with no fields now closes its element when it changes, as a state with fields does. Assisted-by: Claude Code (Opus 5.5)
The labelled forms of annotations were only read in vectors of them alone, and their values only by the kind of their label, so old code lost its links and some of its annotations without a word: - A package now says what its labels' values stand for, as well as their kinds. StyledStrings gives `:link` strings their `Link`s. - A string reads a labelled annotation where it reads each annotation, so it is read in a vector of any values too. - Every labelled form but deletion reads its values through `labelled_value`, which gives the one warning. Base builds its own chars with their value types, so a string whose values only look labelled keeps them as they are. Assisted-by: Claude Code (Opus 5.5)
The manual gains a section on `Base.AnnotatedDisplay`: what `context`, `reduction` and `transition` do, the rules an implementation follows, and a worked example, run as a doctest. Assisted-by: Claude Code (Opus 5.5)
`replace` into an `AnnotatedIOBuffer` takes its pairs as a vararg and
only passes them on. So the body behind its `count` keyword was not
specialised on them, and it boxed every value of the replacement loop.
Taking them as `Vararg{Pair, N}` makes it specialise, as `_replace_`
does for plain strings. Replacing two characters in a short styled
string drops from 5.7 µs to 0.3 µs, and `replace` on an annotated
string from 6.5 µs to 0.9 µs.
Annotations were also built with the type of their own value, then
converted to the buffer's value type as they were pushed. That
allocated whenever the value type was a union. They are now built with
the buffer's value type, which makes inserting them nine times faster.
This removes two REVIEW comments that asked why these were slow.
Assisted-by: Claude Code (Opus 5.5)
Two replace tests appeared twice, once each in the splitting and size tests. They are kept under count and replacing a pattern with itself. Assisted-by: Claude Code (Opus 5.5)
A string was rendered by code compiled for its value type, so each new union compiled the whole region walk and every reduction again, even when the new member does not display. `render` now finds the types that display from their baseline states, those that are not `nothing`, and walks only their annotations. A type that does not display adds just a small filter, and the rest of the rendering is the code already compiled for the types that do. When nothing displays, the text is written as it is. The region iterator can be limited to one type of value, which it reads in place when the string holds nothing else, and otherwise gathers as it makes its events. Annotations that do not display no longer split regions, so a string that mixes them with faces renders faster. A string of `Any` values finds its types from the values, and drops those that do not display in the same way. Every state of a type is of its baseline's type. One of another type, which broke `transition` or the HTML frames, now displays as the baseline. Where the state types are known this check compiles away. A new value type that only adds a non-displayed member now compiles in about 55 ms rather than 190 ms. A long styled string with an `Int` among its values prints 60% faster, and allocates 8 KB rather than 51 KB. Strings of `StyledValue` print as fast as before. Assisted-by: Claude Code (Opus 5.5)
Under Base, note the value type parameter, how it is inferred and widened, that it selects the display, that a type written without it is no longer concrete, and that region iteration is lazy. Under JuliaSyntaxHighlighting, note its palette of faces. Under Markdown, note the renamed faces.toml section. Under StyledStrings, note face primacy, palettes and their use, when face names are resolved, and that `Symbol`-valued face annotations are no longer styled.
The rest of JuliaLang/StyledStrings.jl#131. Links reduce without a context, old string links become `Link`s through `labelled_value`, and internal styled text has the value type of styled strings. The face, markup, and palette code is simpler, and the precompile workload covers what a fresh session first uses. Interpolated variables are read in order, markup keys must be identifiers, a customised default face's resets fall back on the standard ones, and face loops through palette variants are rejected. Interpolating a value of unknown type is faster, and StyledStrings' image holds about 330 KB less native code. Stdlib: StyledStrings URL: https://github.com/JuliaLang/StyledStrings.jl.git Stdlib branch: face-primacy Old commit: 10c37a7 New commit: bc6d6a5 Diff: JuliaLang/StyledStrings.jl@10c37a7...bc6d6a5 Assisted-by: Claude Code (Opus 5.5)
054a09c to
6ea1e4c
Compare
This is part of a large effort to overhaul StyledStrings to address the major pain points that have come up since the initial design/implementation.
This work adds value-parameterisation to annotations, making static compilation more viable, replaces symbol-based face naming with a form of hybrid face-interning (
face""), overhauls thestyled""macro AST generation, adds face namespacing, and includes light/dark theming work mentioned in #59958 (though I still want to land the REPL changes separately in that PR).I'll be fleshing out this description more soon, but for now I just want to use CI-built Julia to make it easier for friendly testers to try this out and provide feedback/bug-hunt.
strings/annotatedtest relies on that StyledStrings have been loaded #54561