@@ -30,21 +30,17 @@ const ANSI_4BIT_COLORS = Dict{Symbol, Int}(
3030 :bright_white => 15 )
3131
3232"""
33- ansi_4bit_color_code(color::Symbol, background::Bool=false)
33+ ansi_4bit(color::Integer, background::Bool=false)
34+
35+ Provide the color code (30-37, 40-47, 90-97, 100-107) for `color` (0–15).
3436
35- Provide the color code (30-37, 40-47, 90-97, 100-107) for `color`, as an integer.
3637When `background` is set the background variant will be provided, otherwise
3738the provided code is for setting the foreground color.
3839"""
39- function ansi_4bit_color_code (color:: Symbol , background:: Bool = false )
40- code = get (ANSI_4BIT_COLORS, color, nothing )
41- if code != = nothing
42- code >= 8 && (code += 52 )
43- background && (code += 10 )
44- code + 30
45- else
46- ifelse (background, 49 , 39 )
47- end
40+ function ansi_4bit (code:: Integer , background:: Bool = false )
41+ code >= 8 && (code += 52 )
42+ background && (code += 10 )
43+ code + 30
4844end
4945
5046"""
@@ -96,6 +92,8 @@ function termcolor24bit(io::IO, color::RGBTuple, category::Char)
9692 string (color. b), ' m' )
9793end
9894
95+ const MAX_COLOR_FORWARDS = 12
96+
9997"""
10098 termcolor(io::IO, color::SimpleColor, category::Char)
10199
@@ -110,31 +108,45 @@ If `color` is a `SimpleColor{Symbol}`, the value should be a a member of
110108
111109If `color` is a `SimpleColor{RGBTuple}` and `get_have_truecolor()` returns true,
11211024-bit color is used. Otherwise, an 8-bit approximation of `color` is used.
111+
112+ If `color` is unknown, no output is produced.
113113"""
114114function termcolor (io:: IO , color:: SimpleColor , category:: Char )
115+ if category == ' 4'
116+ if color. value ∈ (:background , FACES. basecolors[:background ])
117+ return print (io, " \e [" , category, " 9m" )
118+ elseif color. value == :foreground
119+ return print (io, " \e [47m" ) # Technically not quite, but close enough
120+ end
121+ elseif color. value ∈ (:foreground , FACES. basecolors[:foreground ])
122+ return print (io, " \e [" , category, " 9m" )
123+ elseif category == ' 3' && color. value == :background
124+ return print (io, " \e [30m" ) # Technically not quite, but close enough
125+ end
126+ for _ in 1 : MAX_COLOR_FORWARDS
127+ color. value isa RGBTuple && break
128+ fg = get (FACES. current[], color. value, Face ()). foreground
129+ isnothing (fg) && return
130+ color == fg && break
131+ color = fg
132+ end
115133 if color. value isa RGBTuple
116134 if Base. get_have_truecolor ()
117135 termcolor24bit (io, color. value, category)
118136 else
119137 termcolor8bit (io, color. value, category)
120138 end
121- elseif color. value === :default
122- print (io, " \e [" , category, " 9m" )
123- elseif (fg = get (FACES. current[], color. value, getface ()). foreground) != SimpleColor (color. value)
124- termcolor (io, fg, category)
125- else
126- print (io, " \e [" )
127- if category == ' 3' || category == ' 4'
128- print (io, ansi_4bit_color_code (color. value, category == ' 4' ))
129- elseif category == ' 5'
130- if haskey (ANSI_4BIT_COLORS, color. value)
131- print (io, " 58;5;" , ANSI_4BIT_COLORS[color. value])
132- else
133- print (io, " 59" )
134- end
135- end
136- print (io, " m" )
139+ return
140+ end
141+ ansi = get (ANSI_4BIT_COLORS, color. value, nothing )
142+ isnothing (ansi) && return
143+ print (io, " \e [" )
144+ if category == ' 3' || category == ' 4'
145+ print (io, ansi_4bit (ansi, category == ' 4' ))
146+ elseif category == ' 5'
147+ print (io, " 58;5;" , ansi)
137148 end
149+ print (io, ' m' )
138150end
139151
140152"""
@@ -208,7 +220,7 @@ function termstyle(io::IO, face::Face, lastface::Face=getface())
208220 termcolor (io, face. underline, ' 5' )
209221 else
210222 if lastface. underline isa SimpleColor || lastface. underline isa Tuple && first (lastface. underline) isa SimpleColor
211- termcolor (io, SimpleColor (:none ), ' 5' )
223+ termcolor (io, SimpleColor (:foreground ), ' 5' )
212224 end
213225 print (io, ifelse (face. underline == true ,
214226 ANSI_STYLE_CODES. start_underline,
@@ -233,9 +245,10 @@ function _ansi_writer(string_writer::F, io::IO, s::Union{<:AnnotatedString, SubS
233245 # We need to make sure that the customisations are loaded
234246 # before we start outputting any styled content.
235247 load_customisations! ()
248+ default = FACES. default[:default ]
236249 if get (io, :color , false ):: Bool
237250 buf = IOBuffer () # Avoid the overhead in repeatedly printing to `stdout`
238- lastface:: Face = FACES . default[ :default ]
251+ lastface:: Face = default
239252 for (str, styles) in eachregion (s)
240253 face = getface (styles)
241254 link = let idx= findfirst (== (:link ) ∘ first, styles)
@@ -248,7 +261,7 @@ function _ansi_writer(string_writer::F, io::IO, s::Union{<:AnnotatedString, SubS
248261 ! isnothing (link) && write (buf, " \e ]8;;\e\\ " )
249262 lastface = face
250263 end
251- termstyle (buf, FACES . default[ :default ] , lastface)
264+ termstyle (buf, default, lastface)
252265 write (io, seekstart (buf))
253266 elseif s isa AnnotatedString
254267 string_writer (io, s. string)
@@ -296,39 +309,24 @@ Base.AnnotatedDisplay.show_annot(io::IO, ::MIME"text/html", s::Union{<:Annotated
296309# End AnnotatedDisplay hooks
297310# ------------
298311
299- """
300- A mapping between ANSI named colors and 8-bit colors for use in HTML
301- representations.
302- """
303- const HTML_BASIC_COLORS = Dict {Symbol, SimpleColor} (
304- :black => SimpleColor (0x1c , 0x1a , 0x23 ),
305- :red => SimpleColor (0xa5 , 0x1c , 0x2c ),
306- :green => SimpleColor (0x25 , 0xa2 , 0x68 ),
307- :yellow => SimpleColor (0xe5 , 0xa5 , 0x09 ),
308- :blue => SimpleColor (0x19 , 0x5e , 0xb3 ),
309- :magenta => SimpleColor (0x80 , 0x3d , 0x9b ),
310- :cyan => SimpleColor (0x00 , 0x97 , 0xa7 ),
311- :white => SimpleColor (0xdd , 0xdc , 0xd9 ),
312- :bright_black => SimpleColor (0x76 , 0x75 , 0x7a ),
313- :grey => SimpleColor (0x76 , 0x75 , 0x7a ),
314- :gray => SimpleColor (0x76 , 0x75 , 0x7a ),
315- :bright_red => SimpleColor (0xed , 0x33 , 0x3b ),
316- :bright_green => SimpleColor (0x33 , 0xd0 , 0x79 ),
317- :bright_yellow => SimpleColor (0xf6 , 0xd2 , 0x2c ),
318- :bright_blue => SimpleColor (0x35 , 0x83 , 0xe4 ),
319- :bright_magenta => SimpleColor (0xbf , 0x60 , 0xca ),
320- :bright_cyan => SimpleColor (0x26 , 0xc6 , 0xda ),
321- :bright_white => SimpleColor (0xf6 , 0xf5 , 0xf4 ))
322-
323- function htmlcolor (io:: IO , color:: SimpleColor )
312+ function htmlcolor (io:: IO , color:: SimpleColor , background:: Bool = false )
313+ default = getface ()
324314 if color. value isa Symbol
325- if color. value === :default
315+ if background && color. value == :background
316+ print (io, " initial" )
317+ elseif ! background && color. value == :foreground
326318 print (io, " initial" )
327- elseif (fg = get (FACES. current[], color. value, getface () ). foreground) != SimpleColor (color. value)
319+ elseif (fg = get (FACES. current[], color. value, default ). foreground) != SimpleColor (color. value)
328320 htmlcolor (io, fg)
321+ elseif haskey (FACES. basecolors, color. value)
322+ htmlcolor (io, SimpleColor (FACES. basecolors[color. value]))
329323 else
330- htmlcolor (io, get (HTML_BASIC_COLORS, color . value, SimpleColor ( :default )) )
324+ print (io, " inherit " )
331325 end
326+ elseif background && color. value == default. background
327+ htmlcolor (io, SimpleColor (:background ), true )
328+ elseif ! background && color. value == default. foreground
329+ htmlcolor (io, SimpleColor (:foreground ))
332330 else
333331 (; r, g, b) = color. value
334332 print (io, ' #' )
@@ -387,7 +385,7 @@ function cssattrs(io::IO, face::Face, lastface::Face=getface(), escapequotes::Bo
387385 end
388386 if background != lastbackground
389387 printattr (io, " background-color" )
390- htmlcolor (io, background)
388+ htmlcolor (io, background, true )
391389 end
392390 face. underline == lastface. underline ||
393391 if face. underline isa Tuple # Color and style
0 commit comments