Skip to content

Commit 8a39bc7

Browse files
authored
Enable color output whenever stdout is TTY (#34347)
Fixes #34317
1 parent 81a044f commit 8a39bc7

File tree

9 files changed

+43
-31
lines changed

9 files changed

+43
-31
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Language changes
6161
Now the result is "a\n b", since the space before `b` is no longer considered to occur
6262
at the start of a line. The old behavior is considered a bug ([#35001]).
6363

64+
* Color now defaults to on when stdout and stderr are TTYs ([#34347])
6465

6566
Multi-threading changes
6667
-----------------------

base/Base.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ include("filesystem.jl")
241241
using .Filesystem
242242
include("cmd.jl")
243243
include("process.jl")
244+
include("ttyhascolor.jl")
244245
include("grisu/grisu.jl")
245246
include("secretbuffer.jl")
246247

base/client.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## client.jl - frontend handling command line options, environment setup,
44
## and REPL
55

6-
have_color = false
6+
have_color = nothing
77
default_color_warn = :yellow
88
default_color_error = :light_red
99
default_color_info = :cyan
@@ -109,6 +109,7 @@ display_error(er, bt=nothing) = display_error(stderr, er, bt)
109109
function eval_user_input(errio, @nospecialize(ast), show_value::Bool)
110110
errcount = 0
111111
lasterr = nothing
112+
have_color = get(stdout, :color, false)
112113
while true
113114
try
114115
if have_color
@@ -220,7 +221,7 @@ function exec_options(opts)
220221
startup = (opts.startupfile != 2)
221222
history_file = (opts.historyfile != 0)
222223
color_set = (opts.color != 0) # --color!=auto
223-
global have_color = (opts.color == 1) # --color=on
224+
global have_color = color_set ? (opts.color == 1) : nothing # --color=on
224225
global is_interactive = (opts.isinteractive != 0)
225226

226227
# pre-process command line argument list
@@ -492,7 +493,7 @@ function _start()
492493
invokelatest(display_error, catch_stack())
493494
exit(1)
494495
end
495-
if is_interactive && have_color
496+
if is_interactive && have_color === true
496497
print(color_normal)
497498
end
498499
end

base/loading.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1169,10 +1169,11 @@ function create_expr_cache(input::String, output::String, concrete_deps::typeof(
11691169
eval(Meta.parse(code))
11701170
end
11711171
"""
1172+
11721173
io = open(pipeline(`$(julia_cmd()) -O0
11731174
--output-ji $output --output-incremental=yes
11741175
--startup-file=no --history-file=no --warn-overwrite=yes
1175-
--color=$(have_color ? "yes" : "no")
1176+
--color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no")
11761177
--eval $code_object`, stderr=stderr),
11771178
"w", stdout)
11781179
in = io.in

base/stream.jl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,6 @@ function displaysize(io::TTY)
485485
return h, w
486486
end
487487

488-
in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === have_color
489-
haskey(::TTY, key::Symbol) = key === :color
490-
getindex(::TTY, key::Symbol) = key === :color ? have_color : throw(KeyError(key))
491-
get(::TTY, key::Symbol, default) = key === :color ? have_color : default
492-
493488
### Libuv callbacks ###
494489

495490
## BUFFER ##

base/ttyhascolor.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
if Sys.iswindows()
2+
ttyhascolor(term_type = nothing) = true
3+
else
4+
function ttyhascolor(term_type = get(ENV, "TERM", ""))
5+
startswith(term_type, "xterm") && return true
6+
try
7+
@static if Sys.KERNEL === :FreeBSD
8+
return success(`tput AF 0`)
9+
else
10+
return success(`tput setaf 0`)
11+
end
12+
catch e
13+
return false
14+
end
15+
end
16+
end
17+
function get_have_color()
18+
global have_color
19+
have_color === nothing && (have_color = ttyhascolor())
20+
return have_color::Bool
21+
end
22+
in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === get_have_color()
23+
haskey(::TTY, key::Symbol) = key === :color
24+
getindex(::TTY, key::Symbol) = key === :color ? get_have_color() : throw(KeyError(key))
25+
get(::TTY, key::Symbol, default) = key === :color ? get_have_color() : default

stdlib/REPL/src/REPL.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ mutable struct BasicREPL <: AbstractREPL
302302
end
303303

304304
outstream(r::BasicREPL) = r.terminal
305+
hascolor(r::BasicREPL) = hascolor(r.terminal)
305306

306307
function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
307308
d = REPLDisplay(repl)
@@ -428,13 +429,14 @@ mutable struct LineEditREPL <: AbstractREPL
428429
interface::ModalInterface
429430
backendref::REPLBackendRef
430431
LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) =
431-
new(t,true,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,
432+
new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,
432433
in_help,envcolors,false,nothing, Options(), nothing)
433434
end
434435
outstream(r::LineEditREPL) = r.t
435436
specialdisplay(r::LineEditREPL) = r.specialdisplay
436437
specialdisplay(r::AbstractREPL) = nothing
437438
terminal(r::LineEditREPL) = r.t
439+
hascolor(r::LineEditREPL) = r.hascolor
438440

439441
LineEditREPL(t::TextTerminal, hascolor::Bool, envcolors::Bool=false) =
440442
LineEditREPL(t, hascolor,
@@ -813,7 +815,7 @@ function respond(f, repl, main; pass_empty = false, suppress_on_semicolon = true
813815
response = (catch_stack(), true)
814816
end
815817
hide_output = suppress_on_semicolon && ends_with_semicolon(line)
816-
print_response(repl, response, !hide_output, Base.have_color)
818+
print_response(repl, response, !hide_output, hascolor(repl))
817819
end
818820
prepare_next(repl)
819821
reset_state(s)
@@ -955,7 +957,7 @@ function setup_interface(
955957
end
956958
hist_from_file(hp, f, hist_path)
957959
catch
958-
print_response(repl, (catch_stack(),true), true, Base.have_color)
960+
print_response(repl, (catch_stack(),true), true, hascolor(repl))
959961
println(outstream(repl))
960962
@info "Disabling history file for this session"
961963
repl.history_file = false
@@ -1155,6 +1157,7 @@ StreamREPL(stream::IO) = StreamREPL(stream, Base.text_colors[:green], Base.input
11551157
run_repl(stream::IO) = run_repl(StreamREPL(stream))
11561158

11571159
outstream(s::StreamREPL) = s.stream
1160+
hascolor(s::StreamREPL) = get(s.stream, :color, false)
11581161

11591162
answer_color(r::LineEditREPL) = r.envcolors ? Base.answer_color() : r.answer_color
11601163
answer_color(r::StreamREPL) = r.answer_color
@@ -1213,7 +1216,7 @@ function ends_with_semicolon(line::AbstractString)
12131216
end
12141217

12151218
function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
1216-
have_color = Base.have_color
1219+
have_color = hascolor(repl)
12171220
Base.banner(repl.stream)
12181221
d = REPLDisplay(repl)
12191222
dopushdisplay = !in(d,Base.Multimedia.displays)

stdlib/REPL/src/Terminals.jl

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,22 +152,7 @@ beep(t::UnixTerminal) = write(t.err_stream,"\x7")
152152

153153
Base.displaysize(t::UnixTerminal) = displaysize(t.out_stream)
154154

155-
if Sys.iswindows()
156-
hascolor(t::TTYTerminal) = true
157-
else
158-
function hascolor(t::TTYTerminal)
159-
startswith(t.term_type, "xterm") && return true
160-
try
161-
@static if Sys.KERNEL === :FreeBSD
162-
return success(`tput AF 0`)
163-
else
164-
return success(`tput setaf 0`)
165-
end
166-
catch
167-
return false
168-
end
169-
end
170-
end
155+
hascolor(t::TTYTerminal) = Base.ttyhascolor(t.term_type)
171156

172157
# use cached value of have_color
173158
Base.in(key_value::Pair, t::TTYTerminal) = in(key_value, pipe_writer(t))

stdlib/REPL/test/repl.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function fake_repl(@nospecialize(f); options::REPL.Options=REPL.Options(confirm_
4747
Base.link_pipe!(output, reader_supports_async=true, writer_supports_async=true)
4848
Base.link_pipe!(err, reader_supports_async=true, writer_supports_async=true)
4949

50-
repl = REPL.LineEditREPL(FakeTerminal(input.out, output.in, err.in), true)
50+
repl = REPL.LineEditREPL(FakeTerminal(input.out, output.in, err.in, options.hascolor), options.hascolor)
5151
repl.options = options
5252

5353
hard_kill = kill_timer(900) # Your debugging session starts now. You have 15 minutes. Go.
@@ -90,7 +90,7 @@ end
9090
# in the mix. If verification needs to be done, keep it to the bare minimum. Basically
9191
# this should make sure nothing crashes without depending on how exactly the control
9292
# characters are being used.
93-
fake_repl() do stdin_write, stdout_read, repl
93+
fake_repl(options = REPL.Options(confirm_exit=false,hascolor=false)) do stdin_write, stdout_read, repl
9494
repl.specialdisplay = REPL.REPLDisplay(repl)
9595
repl.history_file = false
9696

0 commit comments

Comments
 (0)