diff --git a/NEWS.md b/NEWS.md index 7fb9afe908e46..505ef9b8bd15b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -338,6 +338,10 @@ Library improvements * The function `chop` now accepts two arguments `head` and `tail` allowing to specify number of characters to remove from the head and tail of the string ([#24126]). + * `get(io, :color, false)` can now be used to query whether a stream `io` supports + [ANSI color codes](https://en.wikipedia.org/wiki/ANSI_escape_code) ([#25067]), + rather than using the undocumented `Base.have_color` global flag. + * Functions `first` and `last` now accept `nchar` argument for `AbstractString`. If this argument is used they return a string consisting of first/last `nchar` characters from the original string ([#23960]). diff --git a/base/logging.jl b/base/logging.jl index eae786f5b76a2..319c8efd1df54 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -485,16 +485,17 @@ function handle_message(logger::SimpleLogger, level, message, _module, group, id level < Warn ? :cyan : level < Error ? :yellow : :red buf = IOBuffer() - print_with_color(color, buf, first(levelstr), "- ", bold=true) + iob = IOContext(buf, logger.stream) + print_with_color(color, iob, first(levelstr), "- ", bold=true) msglines = split(string(message), '\n') for i in 1:length(msglines)-1 - println(buf, msglines[i]) - print_with_color(color, buf, "| ", bold=true) + println(iob, msglines[i]) + print_with_color(color, iob, "| ", bold=true) end - println(buf, msglines[end], " -", levelstr, ":", _module, ":", basename(filepath), ":", line) + println(iob, msglines[end], " -", levelstr, ":", _module, ":", basename(filepath), ":", line) for (key,val) in pairs(kwargs) - print_with_color(color, buf, "| ", bold=true) - println(buf, key, " = ", val) + print_with_color(color, iob, "| ", bold=true) + println(iob, key, " = ", val) end write(logger.stream, take!(buf)) nothing diff --git a/base/markdown/Markdown.jl b/base/markdown/Markdown.jl index 3794d77d13bd2..6fd3a71343bd9 100644 --- a/base/markdown/Markdown.jl +++ b/base/markdown/Markdown.jl @@ -60,10 +60,4 @@ macro doc_str(s::AbstractString, t...) docexpr(__source__, __module__, s, t...) end -function Base.display(d::Base.REPL.REPLDisplay, mds::Vector{MD}) - for md in mds - display(d, md) - end -end - end diff --git a/base/markdown/render/terminal/formatting.jl b/base/markdown/render/terminal/formatting.jl index 36a4d390a7138..130b9b698ac5b 100644 --- a/base/markdown/render/terminal/formatting.jl +++ b/base/markdown/render/terminal/formatting.jl @@ -18,13 +18,13 @@ const text_formats = Dict( :negative => ("\e[7m", "\e[27m")) function with_output_format(f::Function, formats::Vector{Symbol}, io::IO, args...) - Base.have_color && for format in formats + get(io, :color, false) && for format in formats haskey(text_formats, format) && print(io, text_formats[format][1]) end try f(io, args...) finally - Base.have_color && for format in formats + get(io, :color, false) && for format in formats haskey(text_formats, format) && print(io, text_formats[format][2]) end @@ -58,9 +58,9 @@ words(s) = split(s, " ") lines(s) = split(s, "\n") # This could really be more efficient -function wrapped_lines(s::AbstractString; width = 80, i = 0) +function wrapped_lines(io::IO, s::AbstractString; width = 80, i = 0) if ismatch(r"\n", s) - return vcat(map(s->wrapped_lines(s, width = width, i = i), split(s, "\n"))...) + return vcat(map(s->wrapped_lines(io, s, width = width, i = i), split(s, "\n"))...) end ws = words(s) lines = AbstractString[ws[1]] @@ -78,11 +78,11 @@ function wrapped_lines(s::AbstractString; width = 80, i = 0) return lines end -wrapped_lines(f::Function, args...; width = 80, i = 0) = - wrapped_lines(sprint(f, args...), width = width, i = 0) +wrapped_lines(io::IO, f::Function, args...; width = 80, i = 0) = + wrapped_lines(io, sprint(f, args...; context=io), width = width, i = 0) function print_wrapped(io::IO, s...; width = 80, pre = "", i = 0) - lines = wrapped_lines(s..., width = width, i = i) + lines = wrapped_lines(io, s..., width = width, i = i) println(io, lines[1]) for line in lines[2:end] println(io, pre, line) @@ -93,7 +93,7 @@ end print_wrapped(f::Function, io::IO, args...; kws...) = print_wrapped(io, f, args...; kws...) function print_centred(io::IO, s...; columns = 80, width = columns) - lines = wrapped_lines(s..., width = width) + lines = wrapped_lines(io, s..., width = width) for line in lines print(io, " "^(div(columns-ansi_length(line), 2))) println(io, line) diff --git a/base/markdown/render/terminal/render.jl b/base/markdown/render/terminal/render.jl index c8ba9455a0ec9..40439aafc27df 100644 --- a/base/markdown/render/terminal/render.jl +++ b/base/markdown/render/terminal/render.jl @@ -24,7 +24,7 @@ function term(io::IO, md::Paragraph, columns) end function term(io::IO, md::BlockQuote, columns) - s = sprint(term, md.content, columns - 10) + s = sprint(term, md.content, columns - 10; context=io) for line in split(rstrip(s), "\n") println(io, " "^margin, "|", line) end @@ -34,7 +34,7 @@ function term(io::IO, md::Admonition, columns) print(io, " "^margin, "| ") with_output_format(:bold, print, io, isempty(md.title) ? md.category : md.title) println(io, "\n", " "^margin, "|") - s = sprint(term, md.content, columns - 10) + s = sprint(term, md.content, columns - 10; context=io) for line in split(rstrip(s), "\n") println(io, " "^margin, "|", line) end @@ -44,7 +44,7 @@ function term(io::IO, f::Footnote, columns) print(io, " "^margin, "| ") with_output_format(:bold, print, io, "[^$(f.id)]") println(io, "\n", " "^margin, "|") - s = sprint(term, f.text, columns - 10) + s = sprint(term, f.text, columns - 10; context=io) for line in split(rstrip(s), "\n") println(io, " "^margin, "|", line) end @@ -61,7 +61,7 @@ function term(io::IO, md::List, columns) end function _term_header(io::IO, md, char, columns) - text = terminline(md.text) + text = terminline_string(io, md.text) with_output_format(:bold, io) do io print(io, " "^(margin)) line_no, lastline_width = print_wrapped(io, text, @@ -103,7 +103,7 @@ term(io::IO, x, _) = show(io, MIME"text/plain"(), x) # Inline Content -terminline(md) = sprint(terminline, md) +terminline_string(io::IO, md) = sprint(terminline, md; context=io) terminline(io::IO, content...) = terminline(io, collect(content)) @@ -137,7 +137,7 @@ terminline(io::IO, f::Footnote) = with_output_format(:bold, terminline, io, "[^$ function terminline(io::IO, md::Link) url = !Base.startswith(md.url, "@ref") ? " ($(md.url))" : "" - text = terminline(md.text) + text = terminline_string(io, md.text) terminline(io, text, url) end @@ -148,5 +148,4 @@ end terminline(io::IO, x) = show(io, MIME"text/plain"(), x) # Show in terminal - -Base.display(d::Base.REPL.REPLDisplay, md::MD) = term(Base.REPL.outstream(d.repl), md) +Base.show(io::IO, ::MIME"text/plain", md::MD) = (term(io, md); nothing) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index a3175eb35d901..47175d5982a7b 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -122,7 +122,7 @@ end function display(d::REPLDisplay, mime::MIME"text/plain", x) io = outstream(d.repl) - Base.have_color && write(io, answer_color(d.repl)) + get(io, :color, false) && write(io, answer_color(d.repl)) show(IOContext(io, :limit => true), mime, x) println(io) end diff --git a/base/repl/Terminals.jl b/base/repl/Terminals.jl index 1941043a5e65b..bd882ba5dfdb0 100644 --- a/base/repl/Terminals.jl +++ b/base/repl/Terminals.jl @@ -167,4 +167,7 @@ else end end +# use cached value of have_color +Base.get(::TTYTerminal, k::Symbol, default) = k === :color ? Base.have_color : default + end # module diff --git a/base/replutil.jl b/base/replutil.jl index 00c41d55ff2e1..de8046e4377ff 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -230,7 +230,7 @@ end function showerror(io::IO, ex, bt; backtrace=true) try - with_output_color(have_color ? error_color() : :nothing, io) do io + with_output_color(get(io, :color, false) ? error_color() : :nothing, io) do io showerror(io, ex) end finally @@ -488,6 +488,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na for (func,arg_types_param) in funcs for method in methods(func) buf = IOBuffer() + iob = IOContext(buf, io) tv = Any[] sig0 = method.sig if Base.is_default_method(method) @@ -499,20 +500,20 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na end s1 = sig0.parameters[1] sig = sig0.parameters[2:end] - print(buf, " ") + print(iob, " ") if !isa(func, s1) # function itself doesn't match return else # TODO: use the methodshow logic here use_constructor_syntax = isa(func, Type) - print(buf, use_constructor_syntax ? func : typeof(func).name.mt.name) + print(iob, use_constructor_syntax ? func : typeof(func).name.mt.name) end - print(buf, "(") + print(iob, "(") t_i = copy(arg_types_param) right_matches = 0 for i = 1 : min(length(t_i), length(sig)) - i > 1 && print(buf, ", ") + i > 1 && print(iob, ", ") # If isvarargtype then it checks whether the rest of the input arguments matches # the varargtype if Base.isvarargtype(sig[i]) @@ -529,12 +530,12 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na # the type of the first argument is not matched. t_in === Union{} && special && i == 1 && break if t_in === Union{} - if Base.have_color - Base.with_output_color(Base.error_color(), buf) do buf - print(buf, "::$sigstr") + if get(io, :color, false) + Base.with_output_color(Base.error_color(), iob) do iob + print(iob, "::$sigstr") end else - print(buf, "!Matched::$sigstr") + print(iob, "!Matched::$sigstr") end # If there is no typeintersect then the type signature from the method is # inserted in t_i this ensures if the type at the next i matches the type @@ -542,7 +543,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na t_i[i] = sig[i] else right_matches += j==i ? 1 : 0 - print(buf, "::$sigstr") + print(iob, "::$sigstr") end end special && right_matches==0 && return # continue the do-block @@ -569,14 +570,14 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na sigstr = string(sigtype) end if !((min(length(t_i), length(sig)) == 0) && k==1) - print(buf, ", ") + print(iob, ", ") end - if Base.have_color - Base.with_output_color(Base.error_color(), buf) do buf - print(buf, "::$sigstr") + if get(io, :color, false) + Base.with_output_color(Base.error_color(), iob) do iob + print(iob, "::$sigstr") end else - print(buf, "!Matched::$sigstr") + print(iob, "!Matched::$sigstr") end end end @@ -584,11 +585,11 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na if isdefined(ft.name.mt, :kwsorter) kwsorter_t = typeof(ft.name.mt.kwsorter) kwords = kwarg_decl(method, kwsorter_t) - length(kwords) > 0 && print(buf, "; ", join(kwords, ", ")) + length(kwords) > 0 && print(iob, "; ", join(kwords, ", ")) end - print(buf, ")") - show_method_params(buf, tv) - print(buf, " at ", method.file, ":", method.line) + print(iob, ")") + show_method_params(iob, tv) + print(iob, " at ", method.file, ":", method.line) if !isempty(kwargs) unexpected = Symbol[] if isempty(kwords) || !(any(endswith(string(kword), "...") for kword in kwords)) @@ -599,14 +600,14 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::NamedTuple = Na end end if !isempty(unexpected) - Base.with_output_color(Base.error_color(), buf) do buf + Base.with_output_color(Base.error_color(), iob) do iob plur = length(unexpected) > 1 ? "s" : "" - print(buf, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"") + print(iob, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"") end end end if ex.world < min_world(method) - print(buf, " (method too new to be called from this world context.)") + print(iob, " (method too new to be called from this world context.)") end # TODO: indicate if it's in the wrong world push!(lines, (buf, right_matches)) diff --git a/base/show.jl b/base/show.jl index c7c515bf6efb0..98caad4eb68c3 100644 --- a/base/show.jl +++ b/base/show.jl @@ -20,7 +20,8 @@ struct IOContext{IO_t <: IO} <: AbstractPipe end end -unwrapcontext(io::IO) = io, ImmutableDict{Symbol,Any}() +# (Note that TTY and TTYTerminal io types have a :color property.) +unwrapcontext(io::IO) = io, get(io,:color,false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}() unwrapcontext(io::IOContext) = io.io, io.dict function IOContext(io::IO, dict::ImmutableDict) @@ -67,6 +68,9 @@ The following properties are in common use: can be avoided (e.g. `[Float16(0)]` can be shown as "Float16[0.0]" instead of "Float16[Float16(0.0)]" : while displaying the elements of the array, the `:typeinfo` property will be set to `Float16`). + - `:color`: Boolean specifying whether ANSI color/escape codes are supported/expected. + By default, this is determined by whether `io` is a compatible terminal and by any + `--color` command-line flag when `julia` was launched. # Examples ```jldoctest @@ -715,7 +719,7 @@ function show_expr_type(io::IO, @nospecialize(ty), emph::Bool) end end -emphasize(io, str::AbstractString) = have_color ? +emphasize(io, str::AbstractString) = get(io, :color, false) ? print_with_color(Base.error_color(), io, str; bold = true) : print(io, Unicode.uppercase(str)) @@ -1253,7 +1257,7 @@ end function show_tuple_as_call(io::IO, name::Symbol, sig::Type) # print a method signature tuple for a lambda definition - color = have_color && get(io, :backtrace, false) ? stackframe_function_color() : :nothing + color = get(io, :color, false) && get(io, :backtrace, false) ? stackframe_function_color() : :nothing if sig === Tuple Base.print_with_color(color, io, name, "(...)") return @@ -1274,7 +1278,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type) end end first = true - print_style = have_color && get(io, :backtrace, false) ? :bold : :nothing + print_style = get(io, :color, false) && get(io, :backtrace, false) ? :bold : :nothing print_with_color(print_style, io, "(") for i = 2:length(sig) # fixme (iter): `eachindex` with offset? first || print(io, ", ") diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 030fc4c79d282..9d057c63f5c02 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -305,7 +305,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) elseif frame.func === top_level_scope_sym print(io, "top-level scope") else - print_with_color(Base.have_color && get(io, :backtrace, false) ? Base.stackframe_function_color() : :nothing, io, string(frame.func)) + print_with_color(get(io, :color, false) && get(io, :backtrace, false) ? Base.stackframe_function_color() : :nothing, io, string(frame.func)) end elseif frame.linfo isa Core.MethodInstance if isa(frame.linfo.def, Method) @@ -323,7 +323,7 @@ function show(io::IO, frame::StackFrame; full_path::Bool=false) if frame.file !== empty_sym file_info = full_path ? string(frame.file) : basename(string(frame.file)) print(io, " at ") - Base.with_output_color(Base.have_color && get(io, :backtrace, false) ? Base.stackframe_lineinfo_color() : :nothing, io) do io + Base.with_output_color(get(io, :color, false) && get(io, :backtrace, false) ? Base.stackframe_lineinfo_color() : :nothing, io) do io print(io, file_info, ":") if frame.line >= 0 print(io, frame.line) diff --git a/base/stream.jl b/base/stream.jl index 36316e0688027..6838d7070bcfb 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -403,6 +403,7 @@ function displaysize(io::TTY) return h, w end +get(::TTY, k::Symbol, default) = k === :color ? have_color : default ### Libuv callbacks ### diff --git a/base/util.jl b/base/util.jl index 1d62826bc5bf2..c9849cc4a3ba6 100644 --- a/base/util.jl +++ b/base/util.jl @@ -319,12 +319,13 @@ end function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args...; bold::Bool = false) buf = IOBuffer() - have_color && bold && print(buf, text_colors[:bold]) - have_color && print(buf, get(text_colors, color, color_normal)) + iscolor = get(io, :color, false) + iscolor && bold && print(buf, text_colors[:bold]) + iscolor && print(buf, get(text_colors, color, color_normal)) try f(IOContext(buf, io), args...) finally - have_color && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default])) - have_color && (bold || color == :bold) && print(buf, disable_text_style[:bold]) + iscolor && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default])) + iscolor && (bold || color == :bold) && print(buf, disable_text_style[:bold]) print(io, String(take!(buf))) end end diff --git a/base/version.jl b/base/version.jl index 9e18099faeae4..7ccbf138aa0a7 100644 --- a/base/version.jl +++ b/base/version.jl @@ -255,7 +255,7 @@ function banner(io::IO = STDOUT) end commit_date = !isempty(GIT_VERSION_INFO.date_string) ? " ($(GIT_VERSION_INFO.date_string))" : "" - if have_color + if get(io, :color, false) c = text_colors tx = c[:normal] # text jl = c[:normal] # julia diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index c48300a641307..c7babe9bd5b70 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -527,20 +527,16 @@ See also [`@test_nowarn`](@ref) to check for the absence of error output. """ macro test_warn(msg, expr) quote - let fname = tempname(), have_color = Base.have_color + let fname = tempname() try - @eval Base have_color = false ret = open(fname, "w") do f redirect_stderr(f) do $(esc(expr)) end end - eval(Base, Expr(:(=), :have_color, have_color)) @test ismatch_warn($(esc(msg)), read(fname, String)) - eval(Base, Expr(:(=), :have_color, have_color)) ret finally - eval(Base, Expr(:(=), :have_color, have_color)) rm(fname, force=true) end end diff --git a/test/compile.jl b/test/compile.jl index d4325ba2e98ba..5b26de146e8de 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -200,8 +200,8 @@ try @test Foo.override(UInt(1)) == 2 # issue #12284: - @test stringmime("text/plain", Base.Docs.doc(Foo.foo)) == "foo function\n" - @test stringmime("text/plain", Base.Docs.doc(Foo.Bar.bar)) == "bar function\n" + @test string(Base.Docs.doc(Foo.foo)) == "foo function\n" + @test string(Base.Docs.doc(Foo.Bar.bar)) == "bar function\n" modules, deps, required_modules = Base.parse_cache_header(cachefile) discard_module = mod_fl_mt -> (mod_fl_mt[2], mod_fl_mt[3]) diff --git a/test/docs.jl b/test/docs.jl index 89a2d323a056a..48a9d77a24d4c 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -26,7 +26,8 @@ docstring_startswith(d1::DocStr, d2) = docstring_startswith(parsedoc(d1), d2) @doc "Doc abstract type" -> abstract type C74685{T,N} <: AbstractArray{T,N} end -@test stringmime("text/plain", Docs.doc(C74685))=="Doc abstract type\n" +@test stringmime("text/plain", Docs.doc(C74685))==" Doc abstract type\n" +@test string(Docs.doc(C74685))=="Doc abstract type\n" macro macro_doctest() end @doc "Helps test if macros can be documented with `@doc \"...\" -> @...`." -> @@ -957,15 +958,8 @@ for (line, expr) in Pair[ @test eval(Base, Docs.helpmode(buf, line)) isa Union{Base.Markdown.MD,Void} end -let save_color = Base.have_color - try - @eval Base have_color = false - @test sprint(Base.Docs.repl_latex, "√") == "\"√\" can be typed by \\sqrt\n\n" - @test sprint(Base.Docs.repl_latex, "x̂₂") == "\"x̂₂\" can be typed by x\\hat\\_2\n\n" - finally - @eval Base have_color = $save_color - end -end +@test sprint(Base.Docs.repl_latex, "√") == "\"√\" can be typed by \\sqrt\n\n" +@test sprint(Base.Docs.repl_latex, "x̂₂") == "\"x̂₂\" can be typed by x\\hat\\_2\n\n" # issue #15684 begin diff --git a/test/markdown.jl b/test/markdown.jl index 05785771940ed..218d9fae1fd2b 100644 --- a/test/markdown.jl +++ b/test/markdown.jl @@ -308,23 +308,24 @@ table = md""" # mime output let out = - """ - # Title + @test sprint(show, "text/plain", book) == + " Title\n ≡≡≡≡≡≡≡\n\n Some discussion\n\n | A quote\n\n Section important\n ===================\n\n Some bolded\n\n • list1\n \n • list2\n \n" + @test sprint(show, "text/markdown", book) == + """ + # Title - Some discussion + Some discussion - > A quote + > A quote - ## Section *important* + ## Section *important* - Some **bolded** + Some **bolded** - * list1 - * list2 - """ - @test sprint(show, "text/plain", book) == out - @test sprint(show, "text/markdown", book) == out + * list1 + * list2 + """ end let out = """ @@ -1061,3 +1062,13 @@ let text = @test expected == Markdown.latex(md) end end + +# different output depending on whether color is requested: +let buf = IOBuffer() + show(buf, "text/plain", md"*emph*") + @test String(take!(buf)) == " emph\n" + show(buf, "text/markdown", md"*emph*") + @test String(take!(buf)) == "*emph*\n" + show(IOContext(buf, :color=>true), "text/plain", md"*emph*") + @test String(take!(buf)) == " \e[4memph\e[24m\n" +end diff --git a/test/misc.jl b/test/misc.jl index 84199a6ce063a..fccc13d555a62 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -594,35 +594,21 @@ if Sys.iswindows() end end -let - old_have_color = Base.have_color - try - @eval Base have_color = true - buf = IOBuffer() - print_with_color(:red, buf, "foo") - @test startswith(String(take!(buf)), Base.text_colors[:red]) - finally - @eval Base have_color = $(old_have_color) - end +let buf = IOBuffer() + print_with_color(:red, IOContext(buf, :color=>true), "foo") + @test startswith(String(take!(buf)), Base.text_colors[:red]) end # Test that `print_with_color` accepts non-string values, just as `print` does -let - old_have_color = Base.have_color - try - @eval Base have_color = true - buf_color = IOBuffer() - args = (3.2, "foo", :testsym) - print_with_color(:red, buf_color, args...) - buf_plain = IOBuffer() - print(buf_plain, args...) - expected_str = string(Base.text_colors[:red], - String(take!(buf_plain)), - Base.text_colors[:default]) - @test expected_str == String(take!(buf_color)) - finally - @eval Base have_color = $(old_have_color) - end +let buf_color = IOBuffer() + args = (3.2, "foo", :testsym) + print_with_color(:red, IOContext(buf_color, :color=>true), args...) + buf_plain = IOBuffer() + print(buf_plain, args...) + expected_str = string(Base.text_colors[:red], + String(take!(buf_plain)), + Base.text_colors[:default]) + @test expected_str == String(take!(buf_color)) end let @@ -635,21 +621,15 @@ let @test c_18711 == 1 end -let - old_have_color = Base.have_color - try - @eval Base have_color = true - buf = IOBuffer() - print_with_color(:red, buf, "foo") - # Check that we get back to normal text color in the end - @test String(take!(buf)) == "\e[31mfoo\e[39m" - - # Check that boldness is turned off - print_with_color(:red, buf, "foo"; bold = true) - @test String(take!(buf)) == "\e[1m\e[31mfoo\e[39m\e[22m" - finally - @eval Base have_color = $(old_have_color) - end +let buf = IOBuffer() + buf_color = IOContext(buf, :color => true) + print_with_color(:red, buf_color, "foo") + # Check that we get back to normal text color in the end + @test String(take!(buf)) == "\e[31mfoo\e[39m" + + # Check that boldness is turned off + print_with_color(:red, buf_color, "foo"; bold = true) + @test String(take!(buf)) == "\e[1m\e[31mfoo\e[39m\e[22m" end abstract type DA_19281{T, N} <: AbstractArray{T, N} end diff --git a/test/reflection.jl b/test/reflection.jl index e79374018f4a5..af895c022b55d 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -67,7 +67,7 @@ end pos_stable(x) = x > 0 ? x : zero(x) pos_unstable(x) = x > 0 ? x : 0 -tag = Base.have_color ? Base.text_colors[Base.error_color()] : "UNION" +tag = "UNION" @test warntype_hastag(pos_unstable, Tuple{Float64}, tag) @test !warntype_hastag(pos_stable, Tuple{Float64}, tag) @@ -80,13 +80,13 @@ end Base.getindex(A::Stable, i) = A.A[i] Base.getindex(A::Unstable, i) = A.A[i] -tag = Base.have_color ? Base.text_colors[Base.error_color()] : "ARRAY{FLOAT64,N}" +tag = "ARRAY{FLOAT64,N}" @test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag) @test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag) @test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag) # Make sure emphasis is not used for other functions -tag = Base.have_color ? Base.text_colors[Base.error_color()] : "ANY" +tag = "ANY" iob = IOBuffer() show(iob, Meta.lower(Main, :(x -> x^2))) str = String(take!(iob)) diff --git a/test/replutil.jl b/test/replutil.jl index dbf15cd9dc659..b61eb627e15fe 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -3,42 +3,27 @@ # For curmod_* include("testenv.jl") -function test_have_color(buf, color, no_color) - if Base.have_color - @test String(take!(buf)) == color - else - @test String(take!(buf)) == no_color - end -end - cfile = " at $(@__FILE__):" c1line = @__LINE__() + 1 method_c1(x::Float64, s::AbstractString...) = true buf = IOBuffer() Base.show_method_candidates(buf, Base.MethodError(method_c1,(1, 1, ""))) -no_color = "\nClosest candidates are:\n method_c1(!Matched::Float64, !Matched::AbstractString...)$cfile$c1line" +@test String(take!(buf)) == "\nClosest candidates are:\n method_c1(!Matched::Float64, !Matched::AbstractString...)$cfile$c1line" @test length(methods(method_c1)) <= 3 # because of '...' in candidate printing -test_have_color(buf, - "\e[0m\nClosest candidates are:\n method_c1(\e[1m\e[31m::Float64\e[0m, \e[1m\e[31m::AbstractString...\e[0m)$cfile$c1line\e[0m", - no_color) +Base.show_method_candidates(IOContext(buf, :color => true), Base.MethodError(method_c1,(1, 1, ""))) +@test String(take!(buf)) == "\e[0m\nClosest candidates are:\n method_c1(\e[91m::Float64\e[39m, \e[91m::AbstractString...\e[39m)$cfile$c1line" -no_color = "\nClosest candidates are:\n method_c1(!Matched::Float64, ::AbstractString...)$cfile$c1line" Base.show_method_candidates(buf, Base.MethodError(method_c1,(1, "", ""))) -test_have_color(buf, - "\e[0m\nClosest candidates are:\n method_c1(\e[1m\e[31m::Float64\e[0m, ::AbstractString...)$cfile$c1line\e[0m", - no_color) +@test String(take!(buf)) == "\nClosest candidates are:\n method_c1(!Matched::Float64, ::AbstractString...)$cfile$c1line" # should match -no_color = "\nClosest candidates are:\n method_c1(::Float64, ::AbstractString...)$cfile$c1line" Base.show_method_candidates(buf, Base.MethodError(method_c1,(1., "", ""))) -test_have_color(buf, - "\e[0m\nClosest candidates are:\n method_c1(::Float64, ::AbstractString...)$cfile$c1line\e[0m", - no_color) +@test String(take!(buf)) == "\nClosest candidates are:\n method_c1(::Float64, ::AbstractString...)$cfile$c1line" # Have no matches so should return empty Base.show_method_candidates(buf, Base.MethodError(method_c1,(1, 1, 1))) -test_have_color(buf, "", "") +@test String(take!(buf)) == "" # matches the implicit constructor -> convert method Base.show_method_candidates(buf, Base.MethodError(Tuple{}, (1, 1, 1))) @@ -55,41 +40,33 @@ method_c2(x::Int32, y::Int32, z::Int32) = true method_c2(x::T, y::T, z::T) where {T<:Real} = true Base.show_method_candidates(buf, Base.MethodError(method_c2,(1., 1., 2))) -color = "\e[0m\nClosest candidates are:\n method_c2(\e[1m\e[31m::Int32\e[0m, ::Float64, ::Any...)$cfile$(c2line+2)\n method_c2(\e[1m\e[31m::Int32\e[0m, ::Any...)$cfile$(c2line+1)\n method_c2(::T<:Real, ::T<:Real, \e[1m\e[31m::T<:Real\e[0m)$cfile$(c2line+5)\n ...\e[0m" -no_color = no_color = "\nClosest candidates are:\n method_c2(!Matched::Int32, ::Float64, ::Any...)$cfile$(c2line+2)\n method_c2(!Matched::Int32, ::Any...)$cfile$(c2line+1)\n method_c2(::T<:Real, ::T<:Real, !Matched::T<:Real) where T<:Real$cfile$(c2line+5)\n ..." -test_have_color(buf, color, no_color) +@test String(take!(buf)) == "\nClosest candidates are:\n method_c2(!Matched::Int32, ::Float64, ::Any...)$cfile$(c2line+2)\n method_c2(!Matched::Int32, ::Any...)$cfile$(c2line+1)\n method_c2(::T<:Real, ::T<:Real, !Matched::T<:Real) where T<:Real$cfile$(c2line+5)\n ..." c3line = @__LINE__() + 1 method_c3(x::Float64, y::Float64) = true Base.show_method_candidates(buf, Base.MethodError(method_c3,(1.,))) -color = "\e[0m\nClosest candidates are:\n method_c3(::Float64, \e[1m\e[31m::Float64\e[0m)$cfile$c3line\e[0m" -no_color = no_color = "\nClosest candidates are:\n method_c3(::Float64, !Matched::Float64)$cfile$c3line" -test_have_color(buf, color, no_color) +@test String(take!(buf)) == "\nClosest candidates are:\n method_c3(::Float64, !Matched::Float64)$cfile$c3line" # Test for the method error in issue #8651 c4line = @__LINE__ method_c4() = true method_c4(x::AbstractString) = false Base.show_method_candidates(buf, MethodError(method_c4,("",))) -test_have_color(buf, - "\e[0m\nClosest candidates are:\n method_c4(::AbstractString)$cfile$(c4line+2)\n method_c4()$cfile$(c4line+1)\e[0m", - "\nClosest candidates are:\n method_c4(::AbstractString)$cfile$(c4line+2)\n method_c4()$cfile$(c4line+1)") +@test String(take!(buf)) == "\nClosest candidates are:\n method_c4(::AbstractString)$cfile$(c4line+2)\n method_c4()$cfile$(c4line+1)" c5line = @__LINE__() + 1 method_c5(::Type{Float64}) = true Base.show_method_candidates(buf, MethodError(method_c5,(Float64,))) -test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c5(::Type{Float64})$cfile$c5line\e[0m", - "\nClosest candidates are:\n method_c5(::Type{Float64})$cfile$c5line") +@test String(take!(buf)) == "\nClosest candidates are:\n method_c5(::Type{Float64})$cfile$c5line" Base.show_method_candidates(buf, MethodError(method_c5,(Int32,))) -test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c5(\e[1m\e[31m::Type{Float64}\e[0m)$cfile$c5line\e[0m", - "\nClosest candidates are:\n method_c5(!Matched::Type{Float64})$cfile$c5line") +@test String(take!(buf)) == "\nClosest candidates are:\n method_c5(!Matched::Type{Float64})$cfile$c5line" mutable struct Test_type end test_type = Test_type() for f in [getindex, setindex!] Base.show_method_candidates(buf, MethodError(f,(test_type, 1,1))) - test_have_color(buf, "", "") + @test String(take!(buf)) == "" end PR16155line = @__LINE__() + 2 @@ -101,12 +78,10 @@ PR16155line2 = @__LINE__() + 1 (::Type{T})(arg::Any) where {T<:PR16155} = "replace call-to-convert method from sysimg" Base.show_method_candidates(buf, MethodError(PR16155,(1.0, 2.0, Int64(3)))) -test_have_color(buf, "\e[0m\nClosest candidates are:\n $(curmod_prefix)PR16155(::Any, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(\e[1m\e[31m::Int64\e[0m, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any) where T<:$(curmod_prefix)PR16155$cfile$PR16155line2\e[0m", - "\nClosest candidates are:\n $(curmod_prefix)PR16155(::Any, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(!Matched::Int64, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any) where T<:$(curmod_prefix)PR16155$cfile$PR16155line2") +@test String(take!(buf)) == "\nClosest candidates are:\n $(curmod_prefix)PR16155(::Any, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(!Matched::Int64, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any) where T<:$(curmod_prefix)PR16155$cfile$PR16155line2" Base.show_method_candidates(buf, MethodError(PR16155,(Int64(3), 2.0, Int64(3)))) -test_have_color(buf, "\e[0m\nClosest candidates are:\n $(curmod_prefix)PR16155(::Int64, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any) where T<:$(curmod_prefix)PR16155$cfile$PR16155line2\e[0m", - "\nClosest candidates are:\n $(curmod_prefix)PR16155(::Int64, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any) where T<:$(curmod_prefix)PR16155$cfile$PR16155line2") +@test String(take!(buf)) == "\nClosest candidates are:\n $(curmod_prefix)PR16155(::Int64, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any, ::Any)$cfile$PR16155line\n $(curmod_prefix)PR16155(::Any) where T<:$(curmod_prefix)PR16155$cfile$PR16155line2" c6line = @__LINE__ method_c6(; x=1) = x @@ -130,40 +105,28 @@ m_error = try TestKWError.method_c6_in_module(1, x=1) catch e; e; end showerror(buf, m_error) error_out3 = String(take!(buf)) -if Base.have_color - @test contains(error_out, "method_c6(; x)$cfile$(c6line + 1)\e[1m\e[31m got unsupported keyword argument \"y\"\e[0m") - @test contains(error_out, "method_c6(\e[1m\e[31m::Any\e[0m; y)$cfile$(c6line + 2)") - @test contains(error_out1, "method_c6(::Any; y)$cfile$(c6line + 2)\e[1m\e[31m got unsupported keyword argument \"x\"\e[0m") - @test contains(error_out2, "method_c6_in_module(; x)$cfile$(c6mline + 2)\e[1m\e[31m got unsupported keyword argument \"y\"\e[0m") - @test contains(error_out2, "method_c6_in_module(\e[1m\e[31m::Any\e[0m; y)$cfile$(c6mline + 3)") - @test contains(error_out3, "method_c6_in_module(::Any; y)$cfile$(c6mline + 3)\e[1m\e[31m got unsupported keyword argument \"x\"\e[0m") -else - @test contains(error_out, "method_c6(; x)$cfile$(c6line + 1) got unsupported keyword argument \"y\"") - @test contains(error_out, "method_c6(!Matched::Any; y)$cfile$(c6line + 2)") - @test contains(error_out1, "method_c6(::Any; y)$cfile$(c6line + 2) got unsupported keyword argument \"x\"") - @test contains(error_out2, "method_c6_in_module(; x)$cfile$(c6mline + 2) got unsupported keyword argument \"y\"") - @test contains(error_out2, "method_c6_in_module(!Matched::Any; y)$cfile$(c6mline + 3)") - @test contains(error_out3, "method_c6_in_module(::Any; y)$cfile$(c6mline + 3) got unsupported keyword argument \"x\"") -end +@test contains(error_out, "method_c6(; x)$cfile$(c6line + 1) got unsupported keyword argument \"y\"") +@test contains(error_out, "method_c6(!Matched::Any; y)$cfile$(c6line + 2)") +@test contains(error_out1, "method_c6(::Any; y)$cfile$(c6line + 2) got unsupported keyword argument \"x\"") +@test contains(error_out2, "method_c6_in_module(; x)$cfile$(c6mline + 2) got unsupported keyword argument \"y\"") +@test contains(error_out2, "method_c6_in_module(!Matched::Any; y)$cfile$(c6mline + 3)") +@test contains(error_out3, "method_c6_in_module(::Any; y)$cfile$(c6mline + 3) got unsupported keyword argument \"x\"") c7line = @__LINE__() + 1 method_c7(a, b; kargs...) = a Base.show_method_candidates(buf, MethodError(method_c7, (1, 1)), (x = 1, y = 2)) -test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c7(::Any, ::Any; kargs...)$cfile$c7line\e[0m", - "\nClosest candidates are:\n method_c7(::Any, ::Any; kargs...)$cfile$c7line") +@test String(take!(buf)) == "\nClosest candidates are:\n method_c7(::Any, ::Any; kargs...)$cfile$c7line" c8line = @__LINE__() + 1 method_c8(a, b; y=1, w=1) = a Base.show_method_candidates(buf, MethodError(method_c8, (1, 1)), (x = 1, y = 2, z = 1, w = 1)) -test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c8(::Any, ::Any; y, w)$cfile$c8line\e[1m\e[31m got unsupported keyword arguments \"x\", \"z\"\e[0m\e[0m", - "\nClosest candidates are:\n method_c8(::Any, ::Any; y, w)$cfile$c8line got unsupported keyword arguments \"x\", \"z\"") +@test String(take!(buf)) == "\nClosest candidates are:\n method_c8(::Any, ::Any; y, w)$cfile$c8line got unsupported keyword arguments \"x\", \"z\"" ac15639line = @__LINE__ addConstraint_15639(c::Int32) = c addConstraint_15639(c::Int64; uncset=nothing) = addConstraint_15639(Int32(c), uncset=uncset) Base.show_method_candidates(buf, MethodError(addConstraint_15639, (Int32(1),)), (uncset = nothing,)) -test_have_color(buf, "\e[0m\nClosest candidates are:\n addConstraint_15639(::Int32)$cfile$(ac15639line + 1)\e[1m\e[31m got unsupported keyword argument \"uncset\"\e[0m\n addConstraint_15639(\e[1m\e[31m::Int64\e[0m; uncset)$cfile$(ac15639line + 2)\e[0m", - "\nClosest candidates are:\n addConstraint_15639(::Int32)$cfile$(ac15639line + 1) got unsupported keyword argument \"uncset\"\n addConstraint_15639(!Matched::Int64; uncset)$cfile$(ac15639line + 2)") +@test String(take!(buf)) == "\nClosest candidates are:\n addConstraint_15639(::Int32)$cfile$(ac15639line + 1) got unsupported keyword argument \"uncset\"\n addConstraint_15639(!Matched::Int64; uncset)$cfile$(ac15639line + 2)" macro except_str(expr, err_type) return quote