Skip to content

Commit 1ab0e32

Browse files
committed
Make the REPL banner resize to avoid wrapping
We also spritz it up a bit using the new StyledStrings library, namely: - colouring the Help and Pkg key prompts - making the docs link a terminal link, and Pkg a link to the Pkg docs - using box drawing characters for the dividing line - making branch status more colourful - making the official version text more subdued With these change the four banners (from smallest to largest) are: 1. A one-liner, for extreme circumstances (new) 2. The short banner 3. The large banner, with the description stacked vertically (new) 4. The large banner, with the description stacked horizontally Because precompilation is a little flakey at the moment, I add a bunch of precompile statements to prevent latency from regressing.
1 parent 2c653ac commit 1ab0e32

File tree

6 files changed

+134
-81
lines changed

6 files changed

+134
-81
lines changed

base/client.jl

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ end
484484
function run_std_repl(REPL::Module, quiet::Bool, banner::Symbol, history_file::Bool)
485485
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
486486
term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr)
487-
banner == :no || REPL.banner(term, short=banner==:short)
487+
banner == :no || REPL.banner(term, banner)
488488
if term.term_type == "dumb"
489489
repl = REPL.BasicREPL(term)
490490
quiet || @warn "Terminal not fully functional"
@@ -607,12 +607,26 @@ end
607607
function repl_main(_)
608608
opts = Base.JLOptions()
609609
interactiveinput = isa(stdin, Base.TTY)
610-
b = opts.banner
611-
auto = b == -1
612-
banner = b == 0 || (auto && !interactiveinput) ? :no :
613-
b == 1 || (auto && interactiveinput) ? :yes :
614-
:short # b == 2
615-
610+
bval = if opts.banner == -1 # Auto
611+
Int(interactiveinput)
612+
else
613+
opts.banner
614+
end
615+
# All the options produced by `jloptions.c`'s `case opt_banner`.
616+
# 0=off, 1=largest, ..., N=smallest
617+
banner = if bval == 0
618+
:no
619+
elseif bval == 1
620+
:full
621+
elseif bval == 2
622+
:narrow
623+
elseif bval == 3
624+
:short
625+
elseif bval == 4
626+
:tiny
627+
else # For type stability of `banner`
628+
:unreachable
629+
end
616630
quiet = (opts.quiet != 0)
617631
history_file = (opts.historyfile != 0)
618632
return run_main_repl(interactiveinput, quiet, banner, history_file)

src/jloptions.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ static const char opts[] =
235235
" -i, --interactive Interactive mode; REPL runs and\n"
236236
" `isinteractive()` is true.\n"
237237
" -q, --quiet Quiet startup: no banner, suppress REPL warnings\n"
238-
" --banner={yes|no|short|auto*} Enable or disable startup banner\n"
238+
" --banner={yes|no|auto*|<size>} Enable or disable startup banner, or specify a\n"
239+
" preferred <size> (tiny, short, narrow, or full).\n"
239240
" --color={yes|no|auto*} Enable or disable color text\n"
240241
" --history-file={yes*|no} Load or save history\n\n"
241242

@@ -611,16 +612,22 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
611612
jl_options.banner = 0;
612613
break;
613614
case opt_banner: // banner
614-
if (!strcmp(optarg, "yes"))
615-
jl_options.banner = 1;
615+
if (!strcmp(optarg, "auto"))
616+
jl_options.banner = -1;
616617
else if (!strcmp(optarg, "no"))
617618
jl_options.banner = 0;
618-
else if (!strcmp(optarg, "auto"))
619-
jl_options.banner = -1;
620-
else if (!strcmp(optarg, "short"))
619+
else if (!strcmp(optarg, "yes"))
620+
jl_options.banner = 1;
621+
else if (!strcmp(optarg, "full"))
622+
jl_options.banner = 1; // Same as "yes".
623+
else if (!strcmp(optarg, "narrow"))
621624
jl_options.banner = 2;
625+
else if (!strcmp(optarg, "short"))
626+
jl_options.banner = 3;
627+
else if (!strcmp(optarg, "tiny"))
628+
jl_options.banner = 4;
622629
else
623-
jl_errorf("julia: invalid argument to --banner={yes|no|auto|short} (%s)", optarg);
630+
jl_errorf("julia: invalid argument to --banner={yes|no|auto|full|narrow|short|tiny} (%s)", optarg);
624631
break;
625632
case opt_experimental_features:
626633
jl_options.use_experimental_features = JL_OPTIONS_USE_EXPERIMENTAL_FEATURES_YES;

stdlib/REPL/src/REPL.jl

Lines changed: 68 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,11 +1764,21 @@ function ends_with_semicolon(code)
17641764
return semi
17651765
end
17661766

1767-
function banner(io::IO = stdout; short = false)
1768-
if Base.GIT_VERSION_INFO.tagged_commit
1769-
commit_string = Base.TAGGED_RELEASE_BANNER
1767+
"""
1768+
banner(io::IO = stdout, preferred::Symbol = :full)
1769+
1770+
Print the "Julia" informative banner to `io`, using the `preferred` variant
1771+
if reasonable and known.
1772+
1773+
!!! warning
1774+
The particular banner selected by `preferred` is liable to being changed
1775+
without warning. The current variants are: `:tiny`, `:short`, `:narrow`, and `:full`.
1776+
"""
1777+
function banner(io::IO = stdout, preferred::Symbol = :full)
1778+
commit_string = if Base.GIT_VERSION_INFO.tagged_commit
1779+
Base.AnnotatedString(TAGGED_RELEASE_BANNER, :face => :shadow)
17701780
elseif isempty(Base.GIT_VERSION_INFO.commit)
1771-
commit_string = ""
1781+
styled""
17721782
else
17731783
days = Int(floor((ccall(:jl_clock_now, Float64, ()) - Base.GIT_VERSION_INFO.fork_master_timestamp) / (60 * 60 * 24)))
17741784
days = max(0, days)
@@ -1777,60 +1787,65 @@ function banner(io::IO = stdout; short = false)
17771787
commit = Base.GIT_VERSION_INFO.commit_short
17781788

17791789
if distance == 0
1780-
commit_string = "Commit $(commit) ($(days) $(unit) old master)"
1790+
styled"""Commit {grey:$commit} \
1791+
({warning:⌛ {italic:$days $unit}} old master)"""
17811792
else
17821793
branch = Base.GIT_VERSION_INFO.branch
1783-
commit_string = "$(branch)/$(commit) (fork: $(distance) commits, $(days) $(unit))"
1794+
styled"""{emphasis:$branch}/{grey:$commit} \
1795+
({italic:{success:{bold,(slant=normal):↑} $distance commits}, \
1796+
{warning:{(slant=normal):⌛} $days $unit}})"""
17841797
end
17851798
end
17861799

1787-
commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : " ($(split(Base.GIT_VERSION_INFO.date_string)[1]))"
1788-
1789-
if get(io, :color, false)::Bool
1790-
c = Base.text_colors
1791-
tx = c[:normal] # text
1792-
jl = c[:normal] # julia
1793-
d1 = c[:bold] * c[:blue] # first dot
1794-
d2 = c[:bold] * c[:red] # second dot
1795-
d3 = c[:bold] * c[:green] # third dot
1796-
d4 = c[:bold] * c[:magenta] # fourth dot
1797-
1798-
if short
1799-
print(io,"""
1800-
$(d3)o$(tx) | Version $(VERSION)$(commit_date)
1801-
$(d2)o$(tx) $(d4)o$(tx) | $(commit_string)
1802-
""")
1803-
else
1804-
print(io,""" $(d3)_$(tx)
1805-
$(d1)_$(tx) $(jl)_$(tx) $(d2)_$(d3)(_)$(d4)_$(tx) | Documentation: https://docs.julialang.org
1806-
$(d1)(_)$(jl) | $(d2)(_)$(tx) $(d4)(_)$(tx) |
1807-
$(jl)_ _ _| |_ __ _$(tx) | Type \"?\" for help, \"]?\" for Pkg help.
1808-
$(jl)| | | | | | |/ _` |$(tx) |
1809-
$(jl)| | |_| | | | (_| |$(tx) | Version $(VERSION)$(commit_date)
1810-
$(jl)_/ |\\__'_|_|_|\\__'_|$(tx) | $(commit_string)
1811-
$(jl)|__/$(tx) |
1812-
1813-
""")
1814-
end
1815-
else
1816-
if short
1817-
print(io,"""
1818-
o | Version $(VERSION)$(commit_date)
1819-
o o | $(commit_string)
1820-
""")
1821-
else
1822-
print(io,"""
1823-
_
1824-
_ _ _(_)_ | Documentation: https://docs.julialang.org
1825-
(_) | (_) (_) |
1826-
_ _ _| |_ __ _ | Type \"?\" for help, \"]?\" for Pkg help.
1827-
| | | | | | |/ _` | |
1828-
| | |_| | | | (_| | | Version $(VERSION)$(commit_date)
1829-
_/ |\\__'_|_|_|\\__'_| | $(commit_string)
1830-
|__/ |
1831-
1832-
""")
1833-
end
1800+
commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : styled" {light:($(split(Base.GIT_VERSION_INFO.date_string)[1]))}"
1801+
doclink = styled"{bold:Documentation:} {(underline=grey),link={https://docs.julialang.org}:https://docs.julialang.org}"
1802+
help = styled"Type {repl_prompt_help:?} for help, {repl_prompt_pkg:]?} for {(underline=grey),link={https://pkgdocs.julialang.org/}:Pkg} help."
1803+
1804+
sizenames = (:tiny, :short, :narrow, :full)
1805+
maxsize = something(findfirst(==(preferred), sizenames), length(sizenames))
1806+
size = min(if all(displaysize(io) .>= (8, 70)); 4 # Full size
1807+
elseif all(displaysize(io) .>= (8, 45)); 3 # Narrower
1808+
elseif all(displaysize(io) .>= (3, 50)); 2 # Tiny
1809+
else 1 end,
1810+
max(0, maxsize))
1811+
1812+
if size == 4 # Full size
1813+
print(io, styled"""
1814+
{bold,green:_}
1815+
{bold,blue:_} _ {bold:{red:_}{green:(_)}{magenta:_}} {shadow:│} $doclink
1816+
{bold,blue:(_)} | {bold:{red:(_)} {magenta:(_)}} {shadow:│}
1817+
_ _ _| |_ __ _ {shadow:│} $help
1818+
| | | | | | |/ _` | {shadow:│}
1819+
| | |_| | | | (_| | {shadow:│} Version {bold:$VERSION}$commit_date
1820+
_/ |\\__'_|_|_|\\__'_| {shadow:│} $commit_string
1821+
|__/ {shadow:│}
1822+
\n""")
1823+
elseif size == 3 # Rotated
1824+
print(io, styled"""
1825+
{bold,green:_}
1826+
{bold,blue:_} _ {bold:{red:_}{green:(_)}{magenta:_}}
1827+
{bold,blue:(_)} | {bold:{red:(_)} {magenta:(_)}}
1828+
_ _ _| |_ __ _
1829+
| | | | | | |/ _` |
1830+
| | |_| | | | (_| |
1831+
_/ |\\__'_|_|_|\\__'_|
1832+
|__/
1833+
1834+
$doclink
1835+
$help
1836+
1837+
Version {bold:$VERSION}$commit_date
1838+
$commit_string
1839+
\n""")
1840+
elseif size == 2 # Tiny
1841+
print(io, styled"""
1842+
{bold,green:o} {shadow:│} Version {bold:$VERSION}$commit_date
1843+
{bold:{red:o} {magenta:o}} {shadow:│} $commit_string
1844+
""", ifelse(displaysize(io) > (12, 0), "\n", ""))
1845+
elseif size == 1 && Base.GIT_VERSION_INFO.tagged_commit # Text only
1846+
print(io, styled"""{bold:{blue:∴} {magenta:Julia} $VERSION}$commit_date\n""")
1847+
elseif size == 1 # Text only
1848+
print(io, styled"""{bold:{blue:∴} {magenta:Julia} $VERSION}$commit_date $commit_string\n""")
18341849
end
18351850
end
18361851

stdlib/REPL/src/precompile.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,20 @@ let
211211
precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Any, Int})
212212
precompile(Tuple{typeof(Base.delete!), Base.Set{Any}, String})
213213
precompile(Tuple{typeof(Base.:(==)), Char, String})
214+
# For the banner
215+
# TODO: Fix precompilation so this is no longer needed
216+
precompile(Tuple{typeof(Base.AnnotatedDisplay.ansi_write), typeof(Base.write), Base.TTY, Base.AnnotatedString{String}})
217+
precompile(Tuple{typeof(Base.all), Tuple{Bool, Bool}})
218+
precompile(Tuple{typeof(Base.get), Base.Dict{Tuple{Symbol, Any}, Int64}, Tuple{Symbol, REPL.StyledStrings.Face}, Int64})
219+
precompile(Tuple{typeof(Base.get), Base.Dict{Tuple{Symbol, Any}, Int64}, Tuple{Symbol, String}, Int64})
220+
precompile(Tuple{typeof(Base.get), Base.Dict{Tuple{Symbol, Any}, Int64}, Tuple{Symbol, Symbol}, Int64})
221+
precompile(Tuple{typeof(Base.hashindex), Tuple{Symbol, String}, Int64})
222+
precompile(Tuple{typeof(Base.isempty), Base.Dict{String, Any}})
223+
precompile(Tuple{typeof(Base.print), Base.TTY, Base.AnnotatedString{String}})
224+
precompile(Tuple{typeof(Base.setindex!), Base.Dict{Tuple{Symbol, Any}, Int64}, Int64, Tuple{Symbol, REPL.StyledStrings.Face}})
225+
precompile(Tuple{typeof(Base.setindex!), Base.Dict{Tuple{Symbol, Any}, Int64}, Int64, Tuple{Symbol, String}})
226+
precompile(Tuple{typeof(Base.setindex!), Base.Dict{Tuple{Symbol, Any}, Int64}, Int64, Tuple{Symbol, Symbol}})
227+
precompile(Tuple{typeof(REPL.banner), Base.TTY})
214228
finally
215229
ccall(:jl_tag_newly_inferred_disable, Cvoid, ())
216230
end

stdlib/REPL/test/repl.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,9 +1912,9 @@ let io = IOBuffer()
19121912
seek(io, 0)
19131913
@test countlines(io) == 9
19141914
take!(io)
1915-
@test REPL.banner(io; short=true) === nothing
1915+
@test REPL.banner(io, :tiny) === nothing
19161916
seek(io, 0)
1917-
@test countlines(io) == 2
1917+
@test countlines(io) == 1
19181918
end
19191919

19201920
@testset "Docstrings" begin

test/cmdlineargs.jl

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -315,18 +315,21 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no`
315315

316316
# --quiet, --banner
317317
let p = "print((Base.JLOptions().quiet, Base.JLOptions().banner))"
318-
@test read(`$exename -e $p`, String) == "(0, -1)"
319-
@test read(`$exename -q -e $p`, String) == "(1, 0)"
320-
@test read(`$exename --quiet -e $p`, String) == "(1, 0)"
321-
@test read(`$exename --banner=no -e $p`, String) == "(0, 0)"
322-
@test read(`$exename --banner=yes -e $p`, String) == "(0, 1)"
323-
@test read(`$exename --banner=short -e $p`, String) == "(0, 2)"
324-
@test read(`$exename -q --banner=no -e $p`, String) == "(1, 0)"
325-
@test read(`$exename -q --banner=yes -e $p`, String) == "(1, 1)"
326-
@test read(`$exename -q --banner=short -e $p`, String) == "(1, 2)"
327-
@test read(`$exename --banner=no -q -e $p`, String) == "(1, 0)"
328-
@test read(`$exename --banner=yes -q -e $p`, String) == "(1, 1)"
329-
@test read(`$exename --banner=short -q -e $p`, String) == "(1, 2)"
318+
@test read(`$exename -e $p`, String) == "(0, -1)"
319+
@test read(`$exename -q -e $p`, String) == "(1, 0)"
320+
@test read(`$exename --quiet -e $p`, String) == "(1, 0)"
321+
@test read(`$exename --banner=auto -e $p`, String) == "(0, -1)"
322+
@test read(`$exename --banner=no -e $p`, String) == "(0, 0)"
323+
@test read(`$exename --banner=yes -e $p`, String) == "(0, 1)"
324+
@test read(`$exename --banner=full -e $p`, String) == "(0, 1)"
325+
@test read(`$exename -q --banner=no -e $p`, String) == "(1, 0)"
326+
@test read(`$exename -q --banner=yes -e $p`, String) == "(1, 1)"
327+
@test read(`$exename -q --banner=tiny -e $p`, String) == "(1, 4)"
328+
@test read(`$exename --banner=no -q -e $p`, String) == "(1, 0)"
329+
@test read(`$exename --banner=yes -q -e $p`, String) == "(1, 1)"
330+
@test read(`$exename --banner=narrow -q -e $p`, String) == "(1, 2)"
331+
@test read(`$exename --banner=short -q -e $p`, String) == "(1, 3)"
332+
@test read(`$exename --banner=tiny -q -e $p`, String) == "(1, 4)"
330333
end
331334

332335
# --home

0 commit comments

Comments
 (0)