From cfc3230c898a4fe331d7d77c32b9996fe7a1a9e4 Mon Sep 17 00:00:00 2001 From: TEC Date: Sun, 22 Oct 2023 03:13:21 +0800 Subject: [PATCH] Implement resizing banner, with StyledStrings This supplants the --banner=short option in favour of a set of banner sizes, chosen such that it fits within the terminal frame. --- base/client.jl | 15 ++-- doc/Manifest.toml | 5 +- pkgimage.mk | 2 +- src/jloptions.c | 15 ++-- stdlib/REPL/Project.toml | 1 + stdlib/REPL/src/REPL.jl | 132 ++++++++++++++++++++-------------- stdlib/REPL/src/precompile.jl | 4 ++ test/cmdlineargs.jl | 5 +- test/precompile.jl | 1 + 9 files changed, 109 insertions(+), 71 deletions(-) diff --git a/base/client.jl b/base/client.jl index c69935c4599926..f570a70422253b 100644 --- a/base/client.jl +++ b/base/client.jl @@ -411,7 +411,7 @@ end global active_repl # run the requested sort of evaluation loop on stdio -function run_main_repl(interactive::Bool, quiet::Bool, banner::Symbol, history_file::Bool, color_set::Bool) +function run_main_repl(interactive::Bool, quiet::Bool, banner::Int, history_file::Bool, color_set::Bool) fallback_repl = parse(Bool, get(ENV, "JULIA_FALLBACK_REPL", "false")) if !fallback_repl && interactive load_InteractiveUtils() @@ -424,7 +424,7 @@ function run_main_repl(interactive::Bool, quiet::Bool, banner::Symbol, history_f invokelatest(REPL_MODULE_REF[]) do REPL term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr) - banner == :no || REPL.banner(term, short=banner==:short) + banner > 0 && REPL.banner(term, banner) if term.term_type == "dumb" repl = REPL.BasicREPL(term) quiet || @warn "Terminal not fully functional" @@ -587,12 +587,11 @@ end function repl_main(_) opts = Base.JLOptions() interactiveinput = isa(stdin, Base.TTY) - b = opts.banner - auto = b == -1 - banner = b == 0 || (auto && !interactiveinput) ? :no : - b == 1 || (auto && interactiveinput) ? :yes : - :short # b == 2 - + banner = if opts.banner == -1 + 10 * Int(interactiveinput) + else + opts.banner + end quiet = (opts.quiet != 0) history_file = (opts.historyfile != 0) color_set = (opts.color != 0) # --color!=auto diff --git a/doc/Manifest.toml b/doc/Manifest.toml index 31eb3634fa7093..a2d330413eef31 100644 --- a/doc/Manifest.toml +++ b/doc/Manifest.toml @@ -94,7 +94,7 @@ deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" [[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.Random]] @@ -111,6 +111,9 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" + [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/pkgimage.mk b/pkgimage.mk index 0b46531cfa137f..d77962bfe45a18 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -107,7 +107,7 @@ $(eval $(call stdlib_builder,InteractiveUtils,Markdown)) # 3-depth packages $(eval $(call stdlib_builder,LibGit2_jll,MbedTLS_jll LibSSH2_jll Artifacts Libdl)) $(eval $(call stdlib_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl)) -$(eval $(call stdlib_builder,REPL,InteractiveUtils Markdown Sockets Unicode)) +$(eval $(call stdlib_builder,REPL,InteractiveUtils Markdown Sockets StyledStrings Unicode)) $(eval $(call stdlib_builder,SharedArrays,Distributed Mmap Random Serialization)) $(eval $(call stdlib_builder,TOML,Dates)) $(eval $(call stdlib_builder,Test,Logging Random Serialization InteractiveUtils)) diff --git a/src/jloptions.c b/src/jloptions.c index f9443ed8f57046..631ebc16080174 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -440,15 +440,20 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) break; case opt_banner: // banner if (!strcmp(optarg, "yes")) - jl_options.banner = 1; + jl_options.banner = 10; else if (!strcmp(optarg, "no")) jl_options.banner = 0; else if (!strcmp(optarg, "auto")) jl_options.banner = -1; - else if (!strcmp(optarg, "short")) - jl_options.banner = 2; - else - jl_errorf("julia: invalid argument to --banner={yes|no|auto|short} (%s)", optarg); + else { + errno = 0; + int bval = strtol(optarg, NULL, 10); + if (errno == ERANGE) + jl_errorf("julia: invalid argument to --banner={yes|no|0-100|auto} (%s)", optarg); + else + jl_options.banner = bval; + break; + } break; case opt_sysimage_native_code: if (!strcmp(optarg,"yes")) diff --git a/stdlib/REPL/Project.toml b/stdlib/REPL/Project.toml index 77eca2bfe42409..6318bd0258ab32 100644 --- a/stdlib/REPL/Project.toml +++ b/stdlib/REPL/Project.toml @@ -6,6 +6,7 @@ version = "1.11.0" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" +StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b" Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [extras] diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 4bf2987966a1c2..357548d68ab1d1 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -34,7 +34,7 @@ end Base.Experimental.@optlevel 1 Base.Experimental.@max_methods 1 -using Base.Meta, Sockets +using Base.Meta, Sockets, StyledStrings import InteractiveUtils export @@ -1422,11 +1422,17 @@ ends_with_semicolon(code::AbstractString) = ends_with_semicolon(String(code)) ends_with_semicolon(code::Union{String,SubString{String}}) = contains(_rm_strings_and_comments(code), r";\s*$") -function banner(io::IO = stdout; short = false) - if Base.GIT_VERSION_INFO.tagged_commit - commit_string = Base.TAGGED_RELEASE_BANNER +""" + banner(io::IO = stdout, maxsize:Int = 10) + +Print the "Julia" informative banner to `io`, using a size variant no larger +than `maxsize`. +""" +function banner(io::IO = stdout, maxsize::Int = 10) + commit_string = if Base.GIT_VERSION_INFO.tagged_commit + Base.AnnotatedString(TAGGED_RELEASE_BANNER, :face => :shadow) elseif isempty(Base.GIT_VERSION_INFO.commit) - commit_string = "" + styled"" else days = Int(floor((ccall(:jl_clock_now, Float64, ()) - Base.GIT_VERSION_INFO.fork_master_timestamp) / (60 * 60 * 24))) days = max(0, days) @@ -1435,60 +1441,78 @@ function banner(io::IO = stdout; short = false) commit = Base.GIT_VERSION_INFO.commit_short if distance == 0 - commit_string = "Commit $(commit) ($(days) $(unit) old master)" + styled"""Commit {grey:$commit} \ + ({warning:⌛ {italic:$days $unit}} old master)""" else branch = Base.GIT_VERSION_INFO.branch - commit_string = "$(branch)/$(commit) (fork: $(distance) commits, $(days) $(unit))" + styled"""{emphasis:$branch}/{grey:$commit} \ + ({italic:{success:{bold,(slant=normal):↑} $distance commits}, \ + {warning:{(slant=normal):⌛} $days $unit}})""" end end - commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : " ($(split(Base.GIT_VERSION_INFO.date_string)[1]))" - - if get(io, :color, false)::Bool - c = Base.text_colors - tx = c[:normal] # text - jl = c[:normal] # julia - d1 = c[:bold] * c[:blue] # first dot - d2 = c[:bold] * c[:red] # second dot - d3 = c[:bold] * c[:green] # third dot - d4 = c[:bold] * c[:magenta] # fourth dot - - if short - print(io,""" - $(d3)o$(tx) | Version $(VERSION)$(commit_date) - $(d2)o$(tx) $(d4)o$(tx) | $(commit_string) - """) - else - print(io,""" $(d3)_$(tx) - $(d1)_$(tx) $(jl)_$(tx) $(d2)_$(d3)(_)$(d4)_$(tx) | Documentation: https://docs.julialang.org - $(d1)(_)$(jl) | $(d2)(_)$(tx) $(d4)(_)$(tx) | - $(jl)_ _ _| |_ __ _$(tx) | Type \"?\" for help, \"]?\" for Pkg help. - $(jl)| | | | | | |/ _` |$(tx) | - $(jl)| | |_| | | | (_| |$(tx) | Version $(VERSION)$(commit_date) - $(jl)_/ |\\__'_|_|_|\\__'_|$(tx) | $(commit_string) - $(jl)|__/$(tx) | - - """) - end - else - if short - print(io,""" - o | Version $(VERSION)$(commit_date) - o o | $(commit_string) - """) - else - print(io,""" - _ - _ _ _(_)_ | Documentation: https://docs.julialang.org - (_) | (_) (_) | - _ _ _| |_ __ _ | Type \"?\" for help, \"]?\" for Pkg help. - | | | | | | |/ _` | | - | | |_| | | | (_| | | Version $(VERSION)$(commit_date) - _/ |\\__'_|_|_|\\__'_| | $(commit_string) - |__/ | - - """) - end + commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : styled" {(weight=light):($(split(Base.GIT_VERSION_INFO.date_string)[1]))}" + doclink = styled"{bold:Documentation\:} {(underline=grey),link={https://docs.julialang.org}:https\://docs.julialang.org}" + help = styled"Type {region,repl_prompt_help:?} for help, {region,repl_prompt_pkg:]?} for {(underline=grey),link={https://pkgdocs.julialang.org/}:Pkg} help." + + size = clamp(if all(displaysize(io) .>= (20, 70)); 6 # Full size + elseif all(displaysize(io) .>= (15, 70)); 5 # Shorter + elseif all(displaysize(io) .>= (25, 45)); 4 # Narrower + elseif all(displaysize(io) .>= (8, 55)); 3 # Tiny + elseif all(displaysize(io) .>= (5, 25)); 2 # Tinier + else 1 end, + 0, maxsize) + + if size == 6 # Full size + print(io, styled""" + {bold,green:_} + {bold,blue:_} _ {bold:{red:_}{green:(_)}{magenta:_}} {shadow:│} $doclink + {bold,blue:(_)} | {bold:{red:(_)} {magenta:(_)}} {shadow:│} + _ _ _| |_ __ _ {shadow:│} $help + | | | | | | |/ _` | {shadow:│} + | | |_| | | | (_| | {shadow:│} Version {bold:$VERSION}$commit_date + _/ |\\__'_|_|_|\\__'_| {shadow:│} $commit_string + |__/ {shadow:│} + \n""") + elseif size == 5 # Shorter + print(io, styled""" + {bold,green:_} + {bold,blue:_} _ {bold:{red:_}{green:(_)}{magenta:_}} {shadow:│} $doclink + {bold,blue:(_)} _ _ | |{bold:{red:(_)} {magenta:(_)}}_ {shadow:│} $help + | || || || || |/ _` | {shadow:│} + _/ | \\_._||_||_|\\__/_| {shadow:│} Version {bold:$VERSION}$commit_date + |__/ {shadow:│} $commit_string + \n""") + elseif size == 4 # Narrower + print(io, styled""" + {bold,green:_} + {bold,blue:_} _ {bold:{red:_}{green:(_)}{magenta:_}} + {bold,blue:(_)} _ _ | |{bold:{red:(_)} {magenta:(_)}}_ + | || || || || |/ _` | + _/ | \\_._||_||_|\\__/_| + |__/ + + $doclink + $help + + Version {bold:$VERSION}$commit_date + $commit_string + \n""") + elseif size == 3 # Tiny + print(io, styled""" + {bold,blue:o} ╷{green:∴} {shadow:│} $doclink + │╷ ╷││╭─┐ {shadow:│} Version {bold:$VERSION}$commit_date + │╰─┘╵╵╰─╰ {shadow:│} $commit_string + ╯ + """) + elseif size == 2 # Tinier + print(io, styled""" + {bold,blue:o} |{green:∴} _ + ||_|||(_| + ╯{grey:{bold:$VERSION}$commit_date} + \n""") + elseif size == 1 # Text only + print(io, styled"""{bold:{blue:∴} {magenta:Julia} $VERSION}$commit_date\n""") end end diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 1b22eb77953a3a..6b66e0cfbe4ec2 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -16,6 +16,7 @@ end using Base.Meta import Markdown +import StyledStrings ## Debugging options # Disable parallel precompiles generation by setting `false` @@ -31,11 +32,14 @@ UP_ARROW = "\e[A" DOWN_ARROW = "\e[B" repl_script = """ +Base.load_terminfo("xterm") 2+2 print("") printstyled("a", "b") display([1]) display([1 2; 3 4]) +import REPL.banner +banner() foo(x) = 1 @time @eval foo(1) ; pwd diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index aa54efab2c8354..5bf119182a0401 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -235,9 +235,10 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` @test read(`$exename -e $p`, String) == "(0, -1)" @test read(`$exename -q -e $p`, String) == "(1, 0)" @test read(`$exename --quiet -e $p`, String) == "(1, 0)" + @test read(`$exename --banner=auto -e $p`, String) == "(0, -1)" @test read(`$exename --banner=no -e $p`, String) == "(0, 0)" - @test read(`$exename --banner=yes -e $p`, String) == "(0, 1)" - @test read(`$exename --banner=short -e $p`, String) == "(0, 2)" + @test read(`$exename --banner=yes -e $p`, String) == "(0, 10)" + @test read(`$exename --banner=4 -e $p`, String) == "(0, 4)" @test read(`$exename -q --banner=no -e $p`, String) == "(1, 0)" @test read(`$exename -q --banner=yes -e $p`, String) == "(1, 1)" @test read(`$exename -q --banner=short -e $p`, String) == "(1, 2)" diff --git a/test/precompile.jl b/test/precompile.jl index fc4ab2490c4a88..1072fe4affea32 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -418,6 +418,7 @@ precompile_test_harness(false) do dir Dict(Base.PkgId(Base.root_module(Base, :Markdown)) => Base.module_build_id(Base.root_module(Base, :Markdown))), # and their dependencies Dict(Base.PkgId(Base.root_module(Base, :Base64)) => Base.module_build_id(Base.root_module(Base, :Base64))), + Dict(Base.PkgId(Base.root_module(Base, :StyledStrings)) => Base.module_build_id(Base.root_module(Base, :StyledStrings))), ) @test Dict(modules) == modules_ok