Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 96b83ad

Browse files
committedNov 17, 2015
reimplement color as a property of printing to a particular stream, instead of a global variable
1 parent 03bd951 commit 96b83ad

File tree

6 files changed

+146
-55
lines changed

6 files changed

+146
-55
lines changed
 

‎base/REPL.jl

+22-31
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import ..LineEdit:
3434

3535
abstract AbstractREPL
3636

37-
answer_color(::AbstractREPL) = ""
37+
answer_color_symbol(::AbstractREPL) = :plain
3838

3939
type REPLBackend
4040
repl_channel::Channel
@@ -109,18 +109,18 @@ end
109109
==(a::REPLDisplay, b::REPLDisplay) = a.repl === b.repl
110110

111111
function display(d::REPLDisplay, ::MIME"text/plain", x)
112-
io = outstream(d.repl)
113-
Base.have_color && write(io, answer_color(d.repl))
114-
writemime(io, MIME("text/plain"), x)
115-
println(io)
112+
Base.with_output_color(answer_color_symbol(d.repl), outstream(d.repl)) do io
113+
writemime(io, MIME("text/plain"), x)
114+
println(io)
115+
end
116116
end
117117
display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x)
118118

119-
function print_response(repl::AbstractREPL, val::ANY, bt, show_value::Bool, have_color::Bool)
119+
function print_response(repl::AbstractREPL, val::ANY, bt, show_value::Bool)
120120
repl.waserror = bt !== nothing
121-
print_response(outstream(repl), val, bt, show_value, have_color, specialdisplay(repl))
121+
print_response(outstream(repl), val, bt, show_value, specialdisplay(repl))
122122
end
123-
function print_response(errio::IO, val::ANY, bt, show_value::Bool, have_color::Bool, specialdisplay=nothing)
123+
function print_response(errio::IO, val::ANY, bt, show_value::Bool, specialdisplay=nothing)
124124
while true
125125
try
126126
if bt !== nothing
@@ -215,7 +215,7 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
215215
put!(repl_channel, (ast, 1))
216216
val, bt = take!(response_channel)
217217
if !ends_with_semicolon(line)
218-
print_response(repl, val, bt, true, false)
218+
print_response(repl, val, bt, true)
219219
end
220220
end
221221
write(repl.terminal, '\n')
@@ -233,7 +233,7 @@ type LineEditREPL <: AbstractREPL
233233
hascolor::Bool
234234
prompt_color::AbstractString
235235
input_color::AbstractString
236-
answer_color::AbstractString
236+
answer_color_symbol::Symbol
237237
shell_color::AbstractString
238238
help_color::AbstractString
239239
history_file::Bool
@@ -248,16 +248,16 @@ type LineEditREPL <: AbstractREPL
248248
new(t,true,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,
249249
in_help,envcolors,false,nothing)
250250
end
251-
outstream(r::LineEditREPL) = r.t
251+
outstream(r::LineEditREPL) = r.hascolor ? IOContext(r.t, :ansi => true) : r.t
252252
specialdisplay(r::LineEditREPL) = r.specialdisplay
253253
specialdisplay(r::AbstractREPL) = nothing
254254
terminal(r::LineEditREPL) = r.t
255255

256256
LineEditREPL(t::TextTerminal, envcolors = false) = LineEditREPL(t,
257257
true,
258-
julia_green,
258+
Base.text_colors[:green],
259259
Base.input_color(),
260-
Base.answer_color(),
260+
Base.answer_color_symbol(),
261261
Base.text_colors[:red],
262262
Base.text_colors[:yellow],
263263
false, false, false, envcolors)
@@ -579,8 +579,6 @@ function history_reset_state(hist::REPLHistoryProvider)
579579
end
580580
LineEdit.reset_state(hist::REPLHistoryProvider) = history_reset_state(hist)
581581

582-
const julia_green = "\033[1m\033[32m"
583-
584582
function return_callback(s)
585583
ast = Base.syntax_deprecation_warnings(false) do
586584
Base.parse_input_line(bytestring(LineEdit.buffer(s)))
@@ -621,7 +619,7 @@ function respond(f, repl, main; pass_empty = false)
621619
reset(repl)
622620
val, bt = send_to_backend(f(line), backend(repl))
623621
if !ends_with_semicolon(line) || bt !== nothing
624-
print_response(repl, val, bt, true, Base.have_color)
622+
print_response(repl, val, bt, true)
625623
end
626624
end
627625
prepare_next(repl)
@@ -748,7 +746,7 @@ function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_rep
748746
finalizer(replc, replc->close(f))
749747
hist_from_file(hp, f)
750748
catch e
751-
print_response(repl, e, catch_backtrace(), true, Base.have_color)
749+
print_response(repl, e, catch_backtrace(), true)
752750
println(outstream(repl))
753751
info("Disabling history file for this session.")
754752
repl.history_file = false
@@ -865,30 +863,23 @@ function run_frontend(repl::LineEditREPL, backend)
865863
dopushdisplay && popdisplay(d)
866864
end
867865

868-
if isdefined(Base, :banner_color)
869-
banner(io, t) = banner(io, hascolor(t))
870-
banner(io, x::Bool) = print(io, x ? Base.banner_color : Base.banner_plain)
871-
else
872-
banner(io,t) = Base.banner(io)
873-
end
874-
875866
## StreamREPL ##
876867

877868
type StreamREPL <: AbstractREPL
878869
stream::IO
879870
prompt_color::AbstractString
880871
input_color::AbstractString
881-
answer_color::AbstractString
872+
answer_color_symbol::Symbol
882873
waserror::Bool
883874
StreamREPL(stream,pc,ic,ac) = new(stream,pc,ic,ac,false)
884875
end
885876

886877
outstream(s::StreamREPL) = s.stream
887878

888-
StreamREPL(stream::IO) = StreamREPL(stream, julia_green, Base.text_colors[:white], Base.answer_color())
879+
StreamREPL(stream::IO) = StreamREPL(stream, Base.text_colors[:green], Base.text_colors[:white], Base.answer_color_symbol())
889880

890-
answer_color(r::LineEditREPL) = r.envcolors ? Base.answer_color() : r.answer_color
891-
answer_color(r::StreamREPL) = r.answer_color
881+
answer_color_symbol(r::LineEditREPL) = r.envcolors ? Base.answer_color_symbol() : r.answer_color_symbol
882+
answer_color_symbol(r::StreamREPL) = r.answer_color_symbol
892883
input_color(r::LineEditREPL) = r.envcolors ? Base.input_color() : r.input_color
893884
input_color(r::StreamREPL) = r.input_color
894885

@@ -917,14 +908,14 @@ end
917908

918909
function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
919910
have_color = Base.have_color
920-
banner(repl.stream, have_color)
911+
Base.banner(repl.stream)
921912
d = REPLDisplay(repl)
922913
dopushdisplay = !in(d,Base.Multimedia.displays)
923914
dopushdisplay && pushdisplay(d)
924915
repl_channel, response_channel = backend.repl_channel, backend.response_channel
925916
while repl.stream.open
926917
if have_color
927-
print(repl.stream,repl.prompt_color)
918+
print(repl.stream, repl.prompt_color)
928919
end
929920
print(repl.stream, "julia> ")
930921
if have_color
@@ -939,7 +930,7 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
939930
put!(repl_channel, (ast, 1))
940931
val, bt = take!(response_channel)
941932
if !ends_with_semicolon(line)
942-
print_response(repl, val, bt, true, have_color)
933+
print_response(repl, val, bt, true)
943934
end
944935
end
945936
end

‎base/Terminals.jl

+4
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,9 @@ eof(t::UnixTerminal) = eof(t.in_stream)
181181

182182
@unix_only hascolor(t::TTYTerminal) = (startswith(t.term_type, "xterm") || success(`tput setaf 0`))
183183
@windows_only hascolor(t::TTYTerminal) = true
184+
""" hascolor(t::TTYTerminal)
185+
Returns whether terminal `t` supports ANSI formatting codes
186+
(however, this does not indicate whether they should be used during printing)"""
187+
hascolor
184188

185189
end # module

‎base/client.jl

+5-3
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ end
3434

3535
warn_color() = repl_color("JULIA_WARN_COLOR", default_color_warn)
3636
info_color() = repl_color("JULIA_INFO_COLOR", default_color_info)
37-
input_color() = text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)]
38-
answer_color() = text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)]
37+
input_color_symbol() = repl_color("JULIA_INPUT_COLOR", default_color_input)
38+
input_color() = text_colors[input_color_symbol()]
39+
answer_color_symbol() = repl_color("JULIA_ANSWER_COLOR", default_color_answer)
40+
answer_color() = text_colors[answer_color_symbol()]
3941

4042
exit(n) = ccall(:jl_exit, Void, (Int32,), n)
4143
exit() = exit(0)
@@ -387,7 +389,7 @@ function _start()
387389
term = Terminals.TTYTerminal(get(ENV,"TERM",@windows? "" : "dumb"),STDIN,STDOUT,STDERR)
388390
global is_interactive = true
389391
color_set || (global have_color = Terminals.hascolor(term))
390-
quiet || REPL.banner(term,term)
392+
quiet || banner(term)
391393
if term.term_type == "dumb"
392394
active_repl = REPL.BasicREPL(term)
393395
quiet || warn("Terminal not fully functional")

‎base/replutil.jl

+6-14
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ function show_method_candidates(io::IO, ex::MethodError)
248248
name = isgeneric(func) ? func.env.name : :anonymous
249249
for method in methods(func)
250250
haskey(UNSHOWN_METHODS, method) && continue
251-
buf = IOBuffer()
251+
buf = with_formatter(io, :normal)[1]
252252
sig = method.sig.parameters
253253
use_constructor_syntax = func == call && !isempty(sig) && isa(sig[1], DataType) &&
254254
!isempty(sig[1].parameters) && isa(sig[1].parameters[1], DataType)
@@ -283,12 +283,8 @@ function show_method_candidates(io::IO, ex::MethodError)
283283
if use_constructor_syntax && i == 1
284284
right_matches += i
285285
elseif t_in === Union{}
286-
if Base.have_color
287-
Base.with_output_color(:red, buf) do buf
288-
print(buf, "::$sigstr")
289-
end
290-
else
291-
print(buf, "!Matched::$sigstr")
286+
Base.print_with_color(:red, buf, "::$sigstr") do buf, sig
287+
print(buf, "!Matched$sig")
292288
end
293289
# If there is no typeintersect then the type signature from the method is
294290
# inserted in t_i this ensures if the type at the next i matches the type
@@ -322,12 +318,8 @@ function show_method_candidates(io::IO, ex::MethodError)
322318
sigstr = string(sigtype)
323319
end
324320
print(buf, ", ")
325-
if Base.have_color
326-
Base.with_output_color(:red, buf) do buf
327-
print(buf, "::$sigstr")
328-
end
329-
else
330-
print(buf, "!Matched::$sigstr")
321+
Base.print_with_color(:red, buf, "::$sigstr") do buf, sig
322+
print(buf, "!Matched$sig")
331323
end
332324
end
333325
end
@@ -350,7 +342,7 @@ function show_method_candidates(io::IO, ex::MethodError)
350342
break
351343
end
352344
i += 1
353-
print(io, takebuf_string(line[1]))
345+
finalize_formatter(io, line[1])
354346
end
355347
end
356348
end

‎base/show.jl

+3-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ pipe_reader(io::IOContext) = io.io
6161
pipe_writer(io::IOContext) = io.io
6262
lock(io::IOContext) = lock(io.io)
6363
unlock(io::IOContext) = unlock(io.io)
64+
takebuf_string(io::IOContext) = takebuf_string(io.io)
65+
takebuf_array(io::IOContext) = takebuf_array(io.io)
6466

6567
function in(key_value::Pair, io::IOContext)
6668
key, value = key_value
@@ -450,7 +452,7 @@ function show_expr_type(io::IO, ty, emph)
450452
end
451453
end
452454

453-
emphasize(io, str::AbstractString) = have_color ? print_with_color(:red, io, str) : print(io, uppercase(str))
455+
emphasize(io, str::AbstractString) = print_with_color(:red, io, str) do io, str; print(io, uppercase(str)); end
454456

455457
show_linenumber(io::IO, file, line) = print(io," # ", file,", line ",line,':')
456458

‎base/util.jl

+106-6
Original file line numberDiff line numberDiff line change
@@ -307,25 +307,125 @@ end
307307

308308
## printing with color ##
309309

310-
function with_output_color(f::Function, color::Symbol, io::IO, args...)
311-
buf = IOBuffer()
312-
have_color && print(buf, get(text_colors, color, color_normal))
313-
try f(buf, args...)
310+
with_output_color(f::Function, color::Symbol, io::IO, args...) = with_output_color(f, f, color, io, args...)
311+
function with_output_color(f::Function, alt::Function, color::Symbol, io::IO, args...)
312+
buf, known = with_formatter(io, color)
313+
try
314+
known ? f(buf, args...) : alt(buf, args...)
314315
finally
315-
have_color && print(buf, color_normal)
316-
print(io, takebuf_string(buf))
316+
finalize_formatter(io, buf)
317317
end
318318
end
319319

320320
print_with_color(color::Symbol, io::IO, msg::AbstractString...) =
321321
with_output_color(print, color, io, msg...)
322+
print_with_color(alt::Function, color::Symbol, io::IO, msg::AbstractString...) =
323+
with_output_color(print, alt, color, io, msg...)
322324
print_with_color(color::Symbol, msg::AbstractString...) =
323325
print_with_color(color, STDOUT, msg...)
326+
324327
println_with_color(color::Symbol, io::IO, msg::AbstractString...) =
325328
with_output_color(println, color, io, msg...)
329+
println_with_color(alt::Function, color::Symbol, io::IO, msg::AbstractString...) =
330+
with_output_color(println, alt, color, io, msg...)
326331
println_with_color(color::Symbol, msg::AbstractString...) =
327332
println_with_color(color, STDOUT, msg...)
328333

334+
# Basic text formatter (ignores most fmt tags)
335+
function with_formatter(io::IO, fmt::Symbol)
336+
buf = IOBuffer()
337+
fmt === :para && print(buf, "\n")
338+
return buf, fmt === :plain || fmt === :para
339+
end
340+
function finalize_formatter(io::IO, fmt_io::IO)
341+
print(io, takebuf_string(fmt_io))
342+
end
343+
344+
# ANSI text formatter (or pass-through)
345+
ansi_formatted(io::IOContext) = get(io, :ansi, false) === true
346+
function with_formatter(io::IOContext, fmt::Symbol)
347+
# try the formatter for io.io
348+
buf, known = with_formatter(io.io, fmt)
349+
buf = IOContext(buf, io)
350+
if ansi_formatted(io)
351+
if known
352+
# skip ansi formatting if it was handled by the inner formatter
353+
fmt = ""
354+
else
355+
fmt = get(text_colors, fmt, "")
356+
# non-existent format
357+
isempty(fmt) && return buf, false
358+
print(buf, fmt)
359+
end
360+
# record current format string
361+
return IOContext(buf, :ansi_previous => fmt), true
362+
end
363+
return buf, known
364+
end
365+
function finalize_formatter(io::IOContext, fmt_io::IO)
366+
if ansi_formatted(io)
367+
# if ansi formatting was enabled, restore the previous ansi state
368+
prev = get(io, :ansi_previous, color_normal)
369+
isempty(prev) || print(fmt_io, prev)
370+
end
371+
# commit and finalize the result
372+
finalize_formatter(io.io, fmt_io)
373+
end
374+
375+
# HTML text streaming formatter
376+
const html_tags = Dict{Symbol, Tuple{ASCIIString, ASCIIString}}([
377+
:para => ("<div>", "</div>"),
378+
:plain => ("<span>", "</span>"),
379+
:bold => ("<b>", "</b>"),
380+
:italic => ("<i>", "</i>"),
381+
:blue => ("<span style='color:blue'>", "</span>"),
382+
:red => ("<span style='color:red'>", "</span>"),
383+
:green => ("<span style='color:green'>", "</span>"),
384+
:white => ("<span style='color:white'>", "</span>"),
385+
:black => ("<span style='color:black'>", "</span>"),
386+
:yellow => ("<span style='color:yellow'>", "</span>"),
387+
:magenta => ("<span style='color:magenta'>", "</span>"),
388+
:cyan => ("<span style='color:cyan'>", "</span>"),
389+
])
390+
type HTMLBuilder <: AbstractPipe
391+
tag::Symbol
392+
children::Vector{Union{ByteString, HTMLBuilder}}
393+
buf::IOBuffer
394+
HTMLBuilder(tag::Symbol) = new(tag, fieldtype(HTMLBuilder, :children)(), IOBuffer())
395+
end
396+
pipe_writer(io::HTMLBuilder) = io.buf
397+
pipe_reader(io::HTMLBuilder) = error("HTMLBuffer not byte-readable")
398+
takebuf_string(io::HTMLBuilder) = io
399+
function with_formatter(io::HTMLBuilder, fmt::Symbol)
400+
return HTMLBuilder(fmt), fmt in keys(html_tags)
401+
end
402+
function finalize_formatter(io::HTMLBuilder, fmt_io::IO)
403+
print(io, takebuf_string(fmt_io))
404+
end
405+
html_escape(str::ByteString) = replace(replace(replace(str, "<", "&lt;"), ">", "&gt;"), "\n", "<br>") # TODO
406+
function flush!(io::HTMLBuilder)
407+
nb_available(io.buf) > 0 && push!(io.children, html_escape(takebuf_string(io.buf)))
408+
nothing
409+
end
410+
function print(io::HTMLBuilder, html::HTMLBuilder)
411+
flush!(io)
412+
push!(io.children, html)
413+
end
414+
function print(io::HTMLBuilder, html::ByteString)
415+
flush!(io)
416+
push!(io.children, html_escape(html))
417+
end
418+
function print(io::IO, html::HTMLBuilder)
419+
tags = get(html_tags, html.tag, ("", ""))
420+
print(io, tags[1])
421+
for child in html.children
422+
print(io, child)
423+
end
424+
print(io, html_escape(takebuf_string(html.buf)))
425+
print(io, tags[2])
426+
end
427+
428+
329429
## warnings and messages ##
330430

331431
function info(io::IO, msg...; prefix="INFO: ")

0 commit comments

Comments
 (0)
Please sign in to comment.