From 621e6467b64bfc1c9dd59351681a53bc65806ba3 Mon Sep 17 00:00:00 2001 From: TEC Date: Wed, 25 Oct 2023 23:59:05 +0800 Subject: [PATCH 01/15] Update StyledStrings version This gets us two commits of interest: - Replace within-module eval with hygienic eval: which makes it possible to include StyledStrings in the sysimage without running into precompile errors. - Load the JULIA_*_COLOR env vars for compat: which mirrors the current behaviour to the relevant faces. --- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + stdlib/StyledStrings.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/md5 delete mode 100644 deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/sha512 create mode 100644 deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/md5 create mode 100644 deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/sha512 diff --git a/deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/md5 b/deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/md5 deleted file mode 100644 index 5d9fdfb8ebd7e..0000000000000 --- a/deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -311f5b6b7e109fea852303ec09324b00 diff --git a/deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/sha512 b/deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/sha512 deleted file mode 100644 index 8a3c1c9238514..0000000000000 --- a/deps/checksums/StyledStrings-61e7b105b157b40807ed0b4840166a25b0948549.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fbf4b2fdde4fd2c2bb321b915d9833ea34952aec1bf9fdcf51e229e6bae5fc0fbfd30db31e410c634955144c6a4295289a313185621e2c5f16b06f22a049739f diff --git a/deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/md5 b/deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/md5 new file mode 100644 index 0000000000000..9fe7ea9c5e80d --- /dev/null +++ b/deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/md5 @@ -0,0 +1 @@ +f4e49cee0253a6d00f1d057cad2d373a diff --git a/deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/sha512 b/deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/sha512 new file mode 100644 index 0000000000000..01fc4ad19fe7c --- /dev/null +++ b/deps/checksums/StyledStrings-c49ae82f2b176556944929ade88a4fecf1556ccb.tar.gz/sha512 @@ -0,0 +1 @@ +487c8c9f0138710165e207dc198376417b312a4f5d91a8616c5093938a2b314afd2a4fb72ea687a9cdc7ddfac246dde793a5c2e60ce465851f7c83194ae6f7d8 diff --git a/stdlib/StyledStrings.version b/stdlib/StyledStrings.version index bca2e9dca3e67..968ed8a51abba 100644 --- a/stdlib/StyledStrings.version +++ b/stdlib/StyledStrings.version @@ -1,4 +1,4 @@ STYLEDSTRINGS_BRANCH = main -STYLEDSTRINGS_SHA1 = 61e7b105b157b40807ed0b4840166a25b0948549 +STYLEDSTRINGS_SHA1 = c49ae82f2b176556944929ade88a4fecf1556ccb STYLEDSTRINGS_GIT_URL := https://github.com/JuliaLang/StyledStrings.jl.git STYLEDSTRINGS_TAR_URL = https://api.github.com/repos/JuliaLang/StyledStrings.jl/tarball/$1 From 887c3eeb057408cb9a2d3e90c989a3391beb2c22 Mon Sep 17 00:00:00 2001 From: TEC Date: Wed, 25 Oct 2023 23:51:33 +0800 Subject: [PATCH 02/15] Load StyledStrings in REPL By loading the StyledStrings stdlib in REPL, we load the privateered print/show methods for the Annotated{String,Char} types defined there. This is nice to have, because it means that styled annotated strings can be constructed in Base and elsewhere without loading the StyledStrings stdlib, but they will be displayed as intended in the REPL. --- doc/Manifest.toml | 5 ++++- pkgimage.mk | 2 +- stdlib/REPL/Project.toml | 1 + stdlib/REPL/src/REPL.jl | 2 +- stdlib/REPL/src/precompile.jl | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/Manifest.toml b/doc/Manifest.toml index 31eb3634fa709..a2d330413eef3 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 58049e9a48f2b..7040c952f5b4c 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -111,7 +111,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/stdlib/REPL/Project.toml b/stdlib/REPL/Project.toml index 77eca2bfe4240..6318bd0258ab3 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 8c55883a04fb9..30b500bf327c1 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 diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index a1cd746fbeb88..9ecce3247d4dd 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` From 40af7f8179942f5cb8a7ab3c36cc9d76bbe72c6b Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Mon, 23 Oct 2023 16:06:45 +0100 Subject: [PATCH 03/15] start poking around --- stdlib/REPL/src/LineEdit.jl | 51 +++++++++++++++++++++---------------- stdlib/REPL/src/REPL.jl | 29 +++++++++++---------- stdlib/REPL/src/notes.md | 28 ++++++++++++++++++++ 3 files changed, 72 insertions(+), 36 deletions(-) create mode 100644 stdlib/REPL/src/notes.md diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 2fdeb707043b0..2a50808477e8b 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -9,7 +9,8 @@ using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, getY, clear_line, beep -import Base: ensureroom, show, AnyDict, position +import Base: ensureroom, show, AnyDict, position, + AnnotatedString using Base: something using InteractiveUtils: InteractiveUtils @@ -43,15 +44,23 @@ end mutable struct Prompt <: TextInterface # A string or function to be printed as the prompt. - prompt::Union{String,Function} + prompt::Union{AnnotatedString{String},String,Function} # A string or function to be printed before the prompt. May not change the length of the prompt. # This may be used for changing the color, issuing other terminal escape codes, etc. - prompt_prefix::Union{String,Function} + # TODO remove + # prompt_prefix::Union{String,Function} # Same as prefix except after the prompt - prompt_suffix::Union{String,Function} - output_prefix::Union{String,Function} - output_prefix_prefix::Union{String,Function} - output_prefix_suffix::Union{String,Function} + + # TODO remove + # prompt_suffix::Union{String,Function} + + # used for things like IPython mode + output_prefix::Union{AnnotatedString{String},String,Function} + + # TODO remove + # output_prefix_prefix::Union{String,Function} + # TODO remove + # output_prefix_suffix::Union{String,Function} keymap_dict::Dict{Char,Any} repl::Union{AbstractREPL,Nothing} complete::CompletionProvider @@ -201,6 +210,7 @@ function beep(s::PromptState, duration::Real=options(s).beep_duration, errormonitor(@async begin trylock(s.refresh_lock) || return try + # TODO what can be refactored in light of StyledStrings? orig_prefix = s.p.prompt_prefix use_current && push!(colors, prompt_string(orig_prefix)) i = 0 @@ -211,7 +221,7 @@ function beep(s::PromptState, duration::Real=options(s).beep_duration, sleep(blink) s.beeping -= blink end - s.p.prompt_prefix = orig_prefix + # s.p.prompt_prefix = orig_prefix refresh_multi_line(s, beeping=true) s.beeping = 0.0 finally @@ -1534,27 +1544,24 @@ default_enter_cb(_) = true write_prompt(terminal::AbstractTerminal, s::PromptState, color::Bool) = write_prompt(terminal, s.p, color) function write_prompt(terminal::AbstractTerminal, p::Prompt, color::Bool) - prefix = prompt_string(p.prompt_prefix) - suffix = prompt_string(p.prompt_suffix) - write(terminal, prefix) - color && write(terminal, Base.text_colors[:bold]) + # write(terminal, prefix) + # color && write(terminal, Base.text_colors[:bold]) width = write_prompt(terminal, p.prompt, color) - color && write(terminal, Base.text_colors[:normal]) - write(terminal, suffix) + # color && write(terminal, Base.text_colors[:normal]) + # write(terminal, suffix) return width end - +# TODO remove function write_output_prefix(io::IO, p::Prompt, color::Bool) - prefix = prompt_string(p.output_prefix_prefix) - suffix = prompt_string(p.output_prefix_suffix) - print(io, prefix) - color && write(io, Base.text_colors[:bold]) + # prefix = prompt_string(p.output_prefix_prefix) + # suffix = prompt_string(p.output_prefix_suffix) + # print(io, prefix) + # color && write(io, Base.text_colors[:bold]) width = write_prompt(io, p.output_prefix, color) - color && write(io, Base.text_colors[:normal]) - print(io, suffix) + # color && write(io, Base.text_colors[:normal]) + # print(io, suffix) return width end - # On Windows, when launching external processes, we cannot control what assumption they make on the # console mode. We thus forcibly reset the console mode at the start of the prompt to ensure they do # not leave the console mode in a corrupt state. diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 30b500bf327c1..07041ce5b1ba9 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -36,6 +36,7 @@ Base.Experimental.@max_methods 1 using Base.Meta, Sockets, StyledStrings import InteractiveUtils +using StyledStrings export AbstractREPL, @@ -91,10 +92,10 @@ include("docview.jl") answer_color(::AbstractREPL) = "" -const JULIA_PROMPT = "julia> " -const PKG_PROMPT = "pkg> " -const SHELL_PROMPT = "shell> " -const HELP_PROMPT = "help?> " +const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" +const PKG_PROMPT = styled"{repl_prompt_pkg:pkg> }" +const SHELL_PROMPT = styled"{repl_prompt_shell:shell> }" +const HELP_PROMPT = styled"{repl_prompt_help:help?> }" mutable struct REPLBackend "channel for AST" @@ -1024,18 +1025,18 @@ function setup_interface( # Set up the main Julia prompt julia_prompt = Prompt(contextual_prompt(repl, JULIA_PROMPT); # Copy colors from the prompt object - prompt_prefix = hascolor ? repl.prompt_color : "", - prompt_suffix = hascolor ? - (repl.envcolors ? Base.input_color : repl.input_color) : "", + # prompt_prefix = hascolor ? repl.prompt_color : "", + # prompt_suffix = hascolor ? + # (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, on_enter = return_callback) # Setup help mode - help_mode = Prompt(contextual_prompt(repl, "help?> "), - prompt_prefix = hascolor ? repl.help_color : "", - prompt_suffix = hascolor ? - (repl.envcolors ? Base.input_color : repl.input_color) : "", + help_mode = Prompt(contextual_prompt(repl, HELP_PROMPT), + # prompt_prefix = hascolor ? repl.help_color : "", + # prompt_suffix = hascolor ? + # (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, # When we're done transform the entered line into a call to helpmode function @@ -1045,9 +1046,9 @@ function setup_interface( # Set up shell mode shell_mode = Prompt(SHELL_PROMPT; - prompt_prefix = hascolor ? repl.shell_color : "", - prompt_suffix = hascolor ? - (repl.envcolors ? Base.input_color : repl.input_color) : "", + # prompt_prefix = hascolor ? repl.shell_color : "", + # prompt_suffix = hascolor ? + # (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = ShellCompletionProvider(), # Transform "foo bar baz" into `foo bar baz` (shell quoting) diff --git a/stdlib/REPL/src/notes.md b/stdlib/REPL/src/notes.md new file mode 100644 index 0000000000000..2292ab7420174 --- /dev/null +++ b/stdlib/REPL/src/notes.md @@ -0,0 +1,28 @@ +Named Colors from `Base.text_colors`: +``` +link \e[5m +light_white \e[97m +italic \e[3m +white \e[37m +light_cyan \e[96m +light_red \e[91m +blue \e[34m +cyan \e[36m +magenta \e[35m +black \e[30m +light_magenta \e[95m +default \e[39m +light_yellow \e[93m +nothing +hidden \e[8m +red \e[31m +light_black \e[90m +light_blue \e[94m +normal \e[0m +light_green \e[92m +green \e[32m +bold \e[1m +yellow \e[33m +reverse \e[7m +underline \e[4m +``` \ No newline at end of file From 8820b9200779f2f2dd4d2b2106790284d70b1c61 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Mon, 23 Oct 2023 18:15:46 +0100 Subject: [PATCH 04/15] yanking prefixes and suffixes --- stdlib/REPL/src/LineEdit.jl | 40 +++++++------------------------------ stdlib/REPL/src/REPL.jl | 27 ++++--------------------- stdlib/REPL/src/notes.md | 22 +++++++++++++++++++- 3 files changed, 32 insertions(+), 57 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 2a50808477e8b..c92d632988308 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -45,22 +45,8 @@ end mutable struct Prompt <: TextInterface # A string or function to be printed as the prompt. prompt::Union{AnnotatedString{String},String,Function} - # A string or function to be printed before the prompt. May not change the length of the prompt. - # This may be used for changing the color, issuing other terminal escape codes, etc. - # TODO remove - # prompt_prefix::Union{String,Function} - # Same as prefix except after the prompt - - # TODO remove - # prompt_suffix::Union{String,Function} - # used for things like IPython mode output_prefix::Union{AnnotatedString{String},String,Function} - - # TODO remove - # output_prefix_prefix::Union{String,Function} - # TODO remove - # output_prefix_suffix::Union{String,Function} keymap_dict::Dict{Char,Any} repl::Union{AbstractREPL,Nothing} complete::CompletionProvider @@ -211,6 +197,7 @@ function beep(s::PromptState, duration::Real=options(s).beep_duration, trylock(s.refresh_lock) || return try # TODO what can be refactored in light of StyledStrings? + #= orig_prefix = s.p.prompt_prefix use_current && push!(colors, prompt_string(orig_prefix)) i = 0 @@ -221,9 +208,10 @@ function beep(s::PromptState, duration::Real=options(s).beep_duration, sleep(blink) s.beeping -= blink end - # s.p.prompt_prefix = orig_prefix + s.p.prompt_prefix = orig_prefix refresh_multi_line(s, beeping=true) s.beeping = 0.0 + =# finally unlock(s.refresh_lock) end @@ -884,7 +872,7 @@ function edit_insert(s::PromptState, c::StringLike) termbuf = terminal(s) w = width(termbuf) offset = s.ias.curs_row == 1 || s.indent < 0 ? - sizeof(prompt_string(s.p.prompt)::String) : s.indent + sizeof(prompt_string(s.p.prompt)::Union{String,AnnotatedString}) : s.indent offset += position(buf) - beginofline(buf) # size of current line spinner = '\0' delayup = !eof(buf) || old_wait @@ -1544,22 +1532,12 @@ default_enter_cb(_) = true write_prompt(terminal::AbstractTerminal, s::PromptState, color::Bool) = write_prompt(terminal, s.p, color) function write_prompt(terminal::AbstractTerminal, p::Prompt, color::Bool) - # write(terminal, prefix) - # color && write(terminal, Base.text_colors[:bold]) width = write_prompt(terminal, p.prompt, color) - # color && write(terminal, Base.text_colors[:normal]) - # write(terminal, suffix) return width end # TODO remove function write_output_prefix(io::IO, p::Prompt, color::Bool) - # prefix = prompt_string(p.output_prefix_prefix) - # suffix = prompt_string(p.output_prefix_suffix) - # print(io, prefix) - # color && write(io, Base.text_colors[:bold]) width = write_prompt(io, p.output_prefix, color) - # color && write(io, Base.text_colors[:normal]) - # print(io, suffix) return width end # On Windows, when launching external processes, we cannot control what assumption they make on the @@ -1595,7 +1573,7 @@ end # returns the width of the written prompt function write_prompt(terminal::Union{IO, AbstractTerminal}, s::Union{AbstractString,Function}, color::Bool) @static Sys.iswindows() && _reset_console_mode() - promptstr = prompt_string(s)::String + promptstr = prompt_string(s)::Union{String,AnnotatedString} write(terminal, promptstr) return textwidth(promptstr) end @@ -2687,11 +2665,7 @@ const default_keymap_dict = keymap([default_keymap, escape_defaults]) function Prompt(prompt ; - prompt_prefix = "", - prompt_suffix = "", output_prefix = "", - output_prefix_prefix = "", - output_prefix_suffix = "", keymap_dict = default_keymap_dict, repl = nothing, complete = EmptyCompletionProvider(), @@ -2700,8 +2674,8 @@ function Prompt(prompt hist = EmptyHistoryProvider(), sticky = false) - return Prompt(prompt, prompt_prefix, prompt_suffix, output_prefix, output_prefix_prefix, output_prefix_suffix, - keymap_dict, repl, complete, on_enter, on_done, hist, sticky) + return Prompt(prompt, output_prefix, keymap_dict, repl, complete, on_enter, + on_done, hist, sticky) end run_interface(::Prompt) = nothing diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 07041ce5b1ba9..0e288c973f6b1 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -49,7 +49,8 @@ import Base: display, show, AnyDict, - == + ==, + AnnotatedString _displaysize(io::IO) = displaysize(io)::Tuple{Int,Int} @@ -476,11 +477,6 @@ end mutable struct LineEditREPL <: AbstractREPL t::TextTerminal hascolor::Bool - prompt_color::String - input_color::String - answer_color::String - shell_color::String - help_color::String history_file::Bool in_shell::Bool in_help::Bool @@ -511,11 +507,6 @@ hascolor(r::LineEditREPL) = r.hascolor LineEditREPL(t::TextTerminal, hascolor::Bool, envcolors::Bool=false) = LineEditREPL(t, hascolor, - hascolor ? Base.text_colors[:green] : "", - hascolor ? Base.input_color() : "", - hascolor ? Base.answer_color() : "", - hascolor ? Base.text_colors[:red] : "", - hascolor ? Base.text_colors[:yellow] : "", false, false, false, envcolors ) @@ -970,11 +961,11 @@ repl_filename(repl, hp) = "REPL" const JL_PROMPT_PASTE = Ref(true) enable_promptpaste(v::Bool) = JL_PROMPT_PASTE[] = v -function contextual_prompt(repl::LineEditREPL, prompt::Union{String,Function}) +function contextual_prompt(repl::LineEditREPL, prompt::Union{AnnotatedString,String,Function}) function () mod = active_module(repl) prefix = mod == Main ? "" : string('(', mod, ") ") - pr = prompt isa String ? prompt : prompt() + pr = prompt isa AbstractString ? prompt : prompt() prefix * pr end end @@ -1024,19 +1015,12 @@ function setup_interface( # Set up the main Julia prompt julia_prompt = Prompt(contextual_prompt(repl, JULIA_PROMPT); - # Copy colors from the prompt object - # prompt_prefix = hascolor ? repl.prompt_color : "", - # prompt_suffix = hascolor ? - # (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, on_enter = return_callback) # Setup help mode help_mode = Prompt(contextual_prompt(repl, HELP_PROMPT), - # prompt_prefix = hascolor ? repl.help_color : "", - # prompt_suffix = hascolor ? - # (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, # When we're done transform the entered line into a call to helpmode function @@ -1046,9 +1030,6 @@ function setup_interface( # Set up shell mode shell_mode = Prompt(SHELL_PROMPT; - # prompt_prefix = hascolor ? repl.shell_color : "", - # prompt_suffix = hascolor ? - # (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = ShellCompletionProvider(), # Transform "foo bar baz" into `foo bar baz` (shell quoting) diff --git a/stdlib/REPL/src/notes.md b/stdlib/REPL/src/notes.md index 2292ab7420174..eb72b5b3bb465 100644 --- a/stdlib/REPL/src/notes.md +++ b/stdlib/REPL/src/notes.md @@ -25,4 +25,24 @@ bold \e[1m yellow \e[33m reverse \e[7m underline \e[4m -``` \ No newline at end of file +``` + +- [ ] Input color from `Base.input_color()` +- [ ] Answer color from `Base.answer_color()` + +## PR Questions + +In this PR I chose to modify instances of `String` with `Union{String,AnnotatedString}`, but I'm open to feedback regarding whether another choice is better. Perhaps widening it to `AbstractString` instead. I'm not familiar with how the compiler might handle these differently, etc. + +At present this requires running julia with `--compiled-modules=no` in order for the styled prompt string `const`s to be initialized: + +```julia +const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" +const PKG_PROMPT = # ... etc +``` + +When running with incremental precompilation, the above code causes issues with precompilation because it causes side-effects. I figured other stdlib/base macros might require similar precompilation-navigating logic, but after looking into it I realized that `styled` is rather special because it [calls eval](https://github.com/JuliaLang/StyledStrings.jl/blob/4777e6008108ac3b3408403c08d1e76e2e6f4c80/src/stylemacro.jl#L672). + +I'm sure that the `_PROMPT` consts could be declared in a precompile-friendly way, but I wonder if this could be fixed within the `styled` macro. Maybe there are macros beyond base/stdlib which solve the same problem. + +- [ ] report precompilation issue to `StyledStrings.jl` \ No newline at end of file From 045d5e871e6762d6b6705622b6cd52b476e77af6 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Mon, 23 Oct 2023 22:12:02 +0100 Subject: [PATCH 05/15] prompt and prompt prefix stuff --- stdlib/REPL/src/LineEdit.jl | 20 +++++++++++++++----- stdlib/REPL/src/REPL.jl | 29 ++++++++++++++--------------- stdlib/REPL/src/notes.md | 6 +++++- stdlib/REPL/src/precompile.jl | 4 ++++ 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index c92d632988308..6a9e205fc7436 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -1532,13 +1532,22 @@ default_enter_cb(_) = true write_prompt(terminal::AbstractTerminal, s::PromptState, color::Bool) = write_prompt(terminal, s.p, color) function write_prompt(terminal::AbstractTerminal, p::Prompt, color::Bool) - width = write_prompt(terminal, p.prompt, color) - return width + @static Sys.iswindows() && _reset_console_mode() + # width = write_prompt(terminal, p.prompt, color) + + promptstr = prompt_string(p)::AnnotatedString + # TODO this write is not dispatching to `StyledStrings` + write(terminal, promptstr) + return textwidth(promptstr) + # return width end # TODO remove function write_output_prefix(io::IO, p::Prompt, color::Bool) - width = write_prompt(io, p.output_prefix, color) - return width + @static Sys.iswindows() && _reset_console_mode() + promptstr = prompt_string(p.output_prefix)::String + # TODO this write is not dispatching to `StyledStrings` + write(io, promptstr) + return textwidth(promptstr) end # On Windows, when launching external processes, we cannot control what assumption they make on the # console mode. We thus forcibly reset the console mode at the start of the prompt to ensure they do @@ -1573,7 +1582,8 @@ end # returns the width of the written prompt function write_prompt(terminal::Union{IO, AbstractTerminal}, s::Union{AbstractString,Function}, color::Bool) @static Sys.iswindows() && _reset_console_mode() - promptstr = prompt_string(s)::Union{String,AnnotatedString} + promptstr = prompt_string(s)::AbstractString + # TODO this write is not dispatching to `StyledStrings` write(terminal, promptstr) return textwidth(promptstr) end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 0e288c973f6b1..e2807bc92ba41 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -489,14 +489,13 @@ mutable struct LineEditREPL <: AbstractREPL interface::ModalInterface backendref::REPLBackendRef frontend_task::Task - function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) + function LineEditREPL(t,hascolor,history_file,in_shell,in_help,envcolors) opts = Options() opts.hascolor = hascolor if !hascolor opts.beep_colors = [""] end - new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, - in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[]) + new(t,hascolor,history_file,in_shell,in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[]) end end outstream(r::LineEditREPL) = (t = r.t; t isa TTYTerminal ? t.out_stream : t) @@ -961,11 +960,11 @@ repl_filename(repl, hp) = "REPL" const JL_PROMPT_PASTE = Ref(true) enable_promptpaste(v::Bool) = JL_PROMPT_PASTE[] = v -function contextual_prompt(repl::LineEditREPL, prompt::Union{AnnotatedString,String,Function}) +function contextual_prompt(repl::LineEditREPL, prompt::Union{AnnotatedString,Function}) function () mod = active_module(repl) prefix = mod == Main ? "" : string('(', mod, ") ") - pr = prompt isa AbstractString ? prompt : prompt() + pr = prompt isa AnnotatedString ? prompt : prompt() prefix * pr end end @@ -1482,19 +1481,19 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef) dopushdisplay = !in(d,Base.Multimedia.displays) dopushdisplay && pushdisplay(d) while !eof(repl.stream)::Bool - if have_color - print(repl.stream,repl.prompt_color) - end - print(repl.stream, "julia> ") - if have_color - print(repl.stream, input_color(repl)) - end + # if have_color + # print(repl.stream,repl.prompt_color) + # end + print(repl.stream, JULIA_PROMPT) + # if have_color + # print(repl.stream, input_color(repl)) + # end line = readline(repl.stream, keep=true) if !isempty(line) ast = Base.parse_input_line(line) - if have_color - print(repl.stream, Base.color_normal) - end + # if have_color + # print(repl.stream, Base.color_normal) + # end response = eval_with_backend(ast, backend) print_response(repl, response, !ends_with_semicolon(line), have_color) end diff --git a/stdlib/REPL/src/notes.md b/stdlib/REPL/src/notes.md index eb72b5b3bb465..d228556e9e070 100644 --- a/stdlib/REPL/src/notes.md +++ b/stdlib/REPL/src/notes.md @@ -45,4 +45,8 @@ When running with incremental precompilation, the above code causes issues with I'm sure that the `_PROMPT` consts could be declared in a precompile-friendly way, but I wonder if this could be fixed within the `styled` macro. Maybe there are macros beyond base/stdlib which solve the same problem. -- [ ] report precompilation issue to `StyledStrings.jl` \ No newline at end of file +- [ ] report precompilation issue to `StyledStrings.jl` + +Relevant PRs +https://github.com/JuliaLang/julia/pull/36689 +https://github.com/JuliaLang/julia/pull/46474/ \ No newline at end of file diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 9ecce3247d4dd..b1470731ece4d 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -61,6 +61,10 @@ const JULIA_PROMPT = "julia> " const PKG_PROMPT = "pkg> " const SHELL_PROMPT = "shell> " const HELP_PROMPT = "help?> " +# const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" +# const PKG_PROMPT = styled"{repl_prompt_pkg:pkg> }" +# const SHELL_PROMPT = styled"{repl_prompt_shell:shell> }" +# const HELP_PROMPT = styled"{repl_prompt_help:help?> }" blackhole = Sys.isunix() ? "/dev/null" : "nul" procenv = Dict{String,Any}( From 1039f60ea32858fbbb6930f625f04e714de820f6 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Tue, 24 Oct 2023 19:06:49 +0100 Subject: [PATCH 06/15] exploring :color for TerminalBuffer --- stdlib/REPL/src/LineEdit.jl | 17 +++++++++-------- stdlib/REPL/src/Terminals.jl | 9 ++++++++- stdlib/REPL/src/precompile.jl | 17 +++++++++-------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 6a9e205fc7436..1bbfd79f1c529 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -1541,11 +1541,20 @@ function write_prompt(terminal::AbstractTerminal, p::Prompt, color::Bool) return textwidth(promptstr) # return width end +# returns the width of the written prompt +function write_prompt(io::IO, s::Union{AbstractString,Function}, color::Bool) + @static Sys.iswindows() && _reset_console_mode() + promptstr = prompt_string(s)::AbstractString + # TODO this write is not dispatching to `StyledStrings` + write(io, promptstr) + return textwidth(promptstr) +end # TODO remove function write_output_prefix(io::IO, p::Prompt, color::Bool) @static Sys.iswindows() && _reset_console_mode() promptstr = prompt_string(p.output_prefix)::String # TODO this write is not dispatching to `StyledStrings` + # write(io, which(write, (REPL.Terminals.TTYTerminal, AnnotatedString))) write(io, promptstr) return textwidth(promptstr) end @@ -1579,14 +1588,6 @@ function _reset_console_mode() end end -# returns the width of the written prompt -function write_prompt(terminal::Union{IO, AbstractTerminal}, s::Union{AbstractString,Function}, color::Bool) - @static Sys.iswindows() && _reset_console_mode() - promptstr = prompt_string(s)::AbstractString - # TODO this write is not dispatching to `StyledStrings` - write(terminal, promptstr) - return textwidth(promptstr) -end ### Keymap Support diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index dac19406b3fc1..f81db5bf87ea8 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -99,6 +99,13 @@ abstract type UnixTerminal <: TextTerminal end pipe_reader(t::UnixTerminal) = t.in_stream::IO pipe_writer(t::UnixTerminal) = t.out_stream::IO +# TODO somehow pass color context to TerminalBuffer. +# The issue is that :color => false when creating +# a TerminalBuffer like TerminalBuffer(IOBuffer()). +# Also, we're double buffering if you include TerminalBuffer +# and the IOBuffer() in StyledStrings/io.jl/_ansi_writer() +# Another possibility is to replace IOBuffer with a +# Array{AnnotatedChar} mutable struct TerminalBuffer <: UnixTerminal out_stream::IO end @@ -152,7 +159,7 @@ beep(t::UnixTerminal) = write(t.err_stream,"\x7") Base.displaysize(t::UnixTerminal) = displaysize(t.out_stream) -hascolor(t::TTYTerminal) = get(t.out_stream, :color, false)::Bool +hascolor(t::Union{TTYTerminal,TerminalBuffer}) = get(t.out_stream, :color, false)::Bool # use cached value of have_color Base.in(key_value::Pair, t::TTYTerminal) = in(key_value, pipe_writer(t)) diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index b1470731ece4d..4c3a8346c5b6f 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -14,6 +14,7 @@ finally Base._track_dependencies[] = true end using Base.Meta +using StyledStrings import Markdown import StyledStrings @@ -57,14 +58,14 @@ cd("complete_path\t\t$CTRL_C julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) -const JULIA_PROMPT = "julia> " -const PKG_PROMPT = "pkg> " -const SHELL_PROMPT = "shell> " -const HELP_PROMPT = "help?> " -# const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" -# const PKG_PROMPT = styled"{repl_prompt_pkg:pkg> }" -# const SHELL_PROMPT = styled"{repl_prompt_shell:shell> }" -# const HELP_PROMPT = styled"{repl_prompt_help:help?> }" +# const JULIA_PROMPT = "julia> " +# const PKG_PROMPT = "pkg> " +# const SHELL_PROMPT = "shell> " +# const HELP_PROMPT = "help?> " +const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" +const PKG_PROMPT = styled"{repl_prompt_pkg:pkg> }" +const SHELL_PROMPT = styled"{repl_prompt_shell:shell> }" +const HELP_PROMPT = styled"{repl_prompt_help:help?> }" blackhole = Sys.isunix() ? "/dev/null" : "nul" procenv = Dict{String,Any}( From 498cfa8e13f1d5011160f685e56bd4cd7bcf09bb Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Tue, 24 Oct 2023 19:12:04 +0100 Subject: [PATCH 07/15] Note to self --- stdlib/REPL/src/Terminals.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index f81db5bf87ea8..0702c1b35d063 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -159,7 +159,8 @@ beep(t::UnixTerminal) = write(t.err_stream,"\x7") Base.displaysize(t::UnixTerminal) = displaysize(t.out_stream) -hascolor(t::Union{TTYTerminal,TerminalBuffer}) = get(t.out_stream, :color, false)::Bool +hascolor(t::TTYTerminal) = get(t.out_stream, :color, false)::Bool +hascolor(t::TerminalBuffer) = error("TerminalBuffer needs to be modified to have access to the original terminal's IO context") # use cached value of have_color Base.in(key_value::Pair, t::TTYTerminal) = in(key_value, pipe_writer(t)) From a30278a97145ed39f1cd59ab44e0cdee1eb16700 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 26 Oct 2023 13:38:42 +0100 Subject: [PATCH 08/15] Use properties of original IO for `TerminalBuffer` --- stdlib/REPL/src/LineEdit.jl | 10 +++++----- stdlib/REPL/src/Terminals.jl | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 1bbfd79f1c529..3d48821a57eb1 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -612,11 +612,10 @@ function highlight_region(lwrite::Union{String,SubString{String}}, regstart::Int end function refresh_multi_line(terminal::UnixTerminal, args...; kwargs...) - outbuf = IOBuffer() - termbuf = TerminalBuffer(outbuf) + termbuf = TerminalBuffer(terminal) ret = refresh_multi_line(termbuf, terminal, args...;kwargs...) # Output the entire refresh at once - write(terminal, take!(outbuf)) + write(terminal, take!(termbuf)) flush(terminal) return ret end @@ -1546,6 +1545,7 @@ function write_prompt(io::IO, s::Union{AbstractString,Function}, color::Bool) @static Sys.iswindows() && _reset_console_mode() promptstr = prompt_string(s)::AbstractString # TODO this write is not dispatching to `StyledStrings` + error("writing IO") write(io, promptstr) return textwidth(promptstr) end @@ -2628,7 +2628,7 @@ end activate(m::ModalInterface, s::MIState, termbuf::AbstractTerminal, term::TextTerminal) = activate(mode(s), s, termbuf, term) -commit_changes(t::UnixTerminal, termbuf::TerminalBuffer) = (write(t, take!(termbuf.out_stream)); nothing) +commit_changes(t::UnixTerminal, termbuf::TerminalBuffer) = (write(t, take!(termbuf)); nothing) function transition(f::Function, s::MIState, newmode::Union{TextInterface,Symbol}) cancel_beep(s) @@ -2643,8 +2643,8 @@ function transition(f::Function, s::MIState, newmode::Union{TextInterface,Symbol if !haskey(s.mode_state, newmode) s.mode_state[newmode] = init_state(terminal(s), newmode) end - termbuf = TerminalBuffer(IOBuffer()) t = terminal(s) + termbuf = TerminalBuffer(t) s.mode_state[mode(s)] = deactivate(mode(s), state(s), termbuf, t) s.current_mode = newmode f() diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index 0702c1b35d063..5e2238fdaebdb 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -108,8 +108,13 @@ pipe_writer(t::UnixTerminal) = t.out_stream::IO # Array{AnnotatedChar} mutable struct TerminalBuffer <: UnixTerminal out_stream::IO + buf::IOBuffer end +TerminalBuffer(terminal::TextTerminal) = TerminalBuffer(terminal, IOBuffer()) + +Base.take!(termbuf::TerminalBuffer) = take!(termbuf.buf) + mutable struct TTYTerminal <: UnixTerminal term_type::String in_stream::IO @@ -170,4 +175,13 @@ Base.get(t::TTYTerminal, key, default) = get(pipe_writer(t), key, default) Base.peek(t::TTYTerminal, ::Type{T}) where {T} = peek(t.in_stream, T)::T +Base.in(key_value::Pair, t::TerminalBuffer) = in(key_value, t.out_stream) +Base.haskey(t::TerminalBuffer, key) = haskey(t.out_stream, key) +Base.getindex(t::TerminalBuffer, key) = getindex(t.out_stream, key) +Base.get(t::TerminalBuffer, key, default) = get(t.out_stream, key, default) + +Base.peek(t::TerminalBuffer, ::Type{T}) where {T} = error("Not implemented") + + + end # module From 212a1b1aabf27f55a0b0b0c3e6853ee1662b5bfd Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 26 Oct 2023 13:47:19 +0100 Subject: [PATCH 09/15] Clean up excess color parameters --- stdlib/REPL/src/LineEdit.jl | 28 ++++++++-------------------- stdlib/REPL/src/REPL.jl | 2 +- stdlib/REPL/src/Terminals.jl | 8 -------- stdlib/REPL/src/notes.md | 10 +++++++++- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 3d48821a57eb1..7292c50fcaaff 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -514,7 +514,7 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf regstart, regstop = region(buf) written = 0 # Write out the prompt string - lindent = write_prompt(termbuf, prompt, hascolor(terminal))::Int + lindent = write_prompt(termbuf, prompt)::Int # Count the '\n' at the end of the line if the terminal emulator does (specific to DOS cmd prompt) miscountnl = @static Sys.iswindows() ? (isa(Terminals.pipe_reader(terminal), Base.TTY) && !(Base.ispty(Terminals.pipe_reader(terminal)))::Bool) : false @@ -1529,35 +1529,23 @@ refresh_line(s::BufferLike, termbuf::AbstractTerminal) = refresh_multi_line(term default_completion_cb(::IOBuffer) = [] default_enter_cb(_) = true -write_prompt(terminal::AbstractTerminal, s::PromptState, color::Bool) = write_prompt(terminal, s.p, color) -function write_prompt(terminal::AbstractTerminal, p::Prompt, color::Bool) - @static Sys.iswindows() && _reset_console_mode() - # width = write_prompt(terminal, p.prompt, color) +write_prompt(terminal::AbstractTerminal, s::PromptState) = write_prompt(terminal, s.p) - promptstr = prompt_string(p)::AnnotatedString - # TODO this write is not dispatching to `StyledStrings` - write(terminal, promptstr) - return textwidth(promptstr) - # return width -end # returns the width of the written prompt -function write_prompt(io::IO, s::Union{AbstractString,Function}, color::Bool) +function write_prompt(io::IO, p::Union{AbstractString,Function,Prompt}) @static Sys.iswindows() && _reset_console_mode() - promptstr = prompt_string(s)::AbstractString - # TODO this write is not dispatching to `StyledStrings` - error("writing IO") + promptstr = prompt_string(p)::AbstractString write(io, promptstr) return textwidth(promptstr) end -# TODO remove -function write_output_prefix(io::IO, p::Prompt, color::Bool) + +function write_output_prefix(io::IO, p::Prompt) @static Sys.iswindows() && _reset_console_mode() promptstr = prompt_string(p.output_prefix)::String - # TODO this write is not dispatching to `StyledStrings` - # write(io, which(write, (REPL.Terminals.TTYTerminal, AnnotatedString))) write(io, promptstr) return textwidth(promptstr) end + # On Windows, when launching external processes, we cannot control what assumption they make on the # console mode. We thus forcibly reset the console mode at the start of the prompt to ensure they do # not leave the console mode in a corrupt state. @@ -2054,7 +2042,7 @@ end input_string(s::PrefixSearchState) = String(take!(copy(s.response_buffer))) -write_prompt(terminal, s::PrefixSearchState, color::Bool) = write_prompt(terminal, s.histprompt.parent_prompt, color) +write_prompt(terminal, s::PrefixSearchState,) = write_prompt(terminal, s.histprompt.parent_prompt) prompt_string(s::PrefixSearchState) = prompt_string(s.histprompt.parent_prompt.prompt) terminal(s::PrefixSearchState) = s.terminal diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index e2807bc92ba41..1e853b1b3d00c 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -279,7 +279,7 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x) mistate = d.repl.mistate mode = LineEdit.mode(mistate) if mode isa LineEdit.Prompt - LineEdit.write_output_prefix(io, mode, get(io, :color, false)::Bool) + LineEdit.write_output_prefix(io, mode) end end get(io, :color, false)::Bool && write(io, answer_color(d.repl)) diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index 5e2238fdaebdb..149811bb96f6a 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -99,13 +99,6 @@ abstract type UnixTerminal <: TextTerminal end pipe_reader(t::UnixTerminal) = t.in_stream::IO pipe_writer(t::UnixTerminal) = t.out_stream::IO -# TODO somehow pass color context to TerminalBuffer. -# The issue is that :color => false when creating -# a TerminalBuffer like TerminalBuffer(IOBuffer()). -# Also, we're double buffering if you include TerminalBuffer -# and the IOBuffer() in StyledStrings/io.jl/_ansi_writer() -# Another possibility is to replace IOBuffer with a -# Array{AnnotatedChar} mutable struct TerminalBuffer <: UnixTerminal out_stream::IO buf::IOBuffer @@ -165,7 +158,6 @@ beep(t::UnixTerminal) = write(t.err_stream,"\x7") Base.displaysize(t::UnixTerminal) = displaysize(t.out_stream) hascolor(t::TTYTerminal) = get(t.out_stream, :color, false)::Bool -hascolor(t::TerminalBuffer) = error("TerminalBuffer needs to be modified to have access to the original terminal's IO context") # use cached value of have_color Base.in(key_value::Pair, t::TTYTerminal) = in(key_value, pipe_writer(t)) diff --git a/stdlib/REPL/src/notes.md b/stdlib/REPL/src/notes.md index d228556e9e070..f874ed424c804 100644 --- a/stdlib/REPL/src/notes.md +++ b/stdlib/REPL/src/notes.md @@ -49,4 +49,12 @@ I'm sure that the `_PROMPT` consts could be declared in a precompile-friendly wa Relevant PRs https://github.com/JuliaLang/julia/pull/36689 -https://github.com/JuliaLang/julia/pull/46474/ \ No newline at end of file +https://github.com/JuliaLang/julia/pull/46474/ + + +## PR +Btw, you mentioned running into some trouble with `get(io, :color, false)`. Could I get your perspective on the existing color-related logic? Specifically regarding how the `:color` attribute is first initialized, down to it being + +My impression while working around this code is that the color "propagation" throughout the REPL code down to the final `write` to the terminal is quite fragile, and I'm wondering if the complexity is necessary (to reflect a complex. Here are a few examples of the brittleness, and I'd like your input on which direction might be the most appropriate solution: +- It took me quite a while to debug why the final `io::IO` struct in `StyledStrings/src/io.jl#_ansi_writer` never had `:color = true`. It turns out that somewhere in the callstack above in [LineEdit.jl](https://github.com/JuliaLang/julia/blob/c54a3f2b83bfca5f79a1b482abaa9b8e7f0da9ce/stdlib/REPL/src/LineEdit.jl#L617-L622) the soon-to-be-written output is first written to a new `IOBuffer`. So instead of a `UnixTerminal` being passed to `_ansi_writer` (where `:color = true`), an `IOBuffer` is passed, and anything which might behave in a way which would correctly say that `:color = true`, such as a `IOContext`, `REPL.Terminals.UnixTerminal` or other `IO` subtype is lost. +- methods throughout `REPL.jl` and `LineEdit.jl` have a `color` parameter that must be propagated all the way from `REPL.run_frontend` down through `print_response` \ No newline at end of file From 36b2a217dab58907c959d5e0d37460713f309163 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 26 Oct 2023 17:10:42 +0100 Subject: [PATCH 10/15] Re-implement `beep` with StyledStrings --- stdlib/REPL/src/LineEdit.jl | 65 ++++++++++++++++++++----------------- stdlib/REPL/src/REPL.jl | 3 -- stdlib/REPL/src/options.jl | 6 ++-- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 7292c50fcaaff..5e87a6d9055f5 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -5,12 +5,16 @@ module LineEdit import ..REPL using REPL: AbstractREPL, Options +# TODO remove after https://github.com/JuliaLang/StyledStrings.jl/pull/17 is merged +import StyledStrings: Face, loadface! +import StyledStrings: @styled_str + using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, getY, clear_line, beep -import Base: ensureroom, show, AnyDict, position, - AnnotatedString +import Base: ensureroom, show, AnyDict, position, + AnnotatedString, annotations using Base: something using InteractiveUtils: InteractiveUtils @@ -184,39 +188,40 @@ complete_line(c::CompletionProvider, s, ::Module) = complete_line(c, s) terminal(s::IO) = s terminal(s::PromptState) = s.terminal - function beep(s::PromptState, duration::Real=options(s).beep_duration, - blink::Real=options(s).beep_blink, - maxduration::Real=options(s).beep_maxduration; - colors=options(s).beep_colors, + blink::Real=options(s).beep_blink, + maxduration::Real=options(s).beep_maxduration; + beep_face=options(s).beep_face, use_current::Bool=options(s).beep_use_current) isinteractive() || return # some tests fail on some platforms s.beeping = min(s.beeping + duration, maxduration) - let colors = Base.copymutable(colors) - errormonitor(@async begin - trylock(s.refresh_lock) || return - try - # TODO what can be refactored in light of StyledStrings? - #= - orig_prefix = s.p.prompt_prefix - use_current && push!(colors, prompt_string(orig_prefix)) - i = 0 - while s.beeping > 0.0 - prefix = colors[mod1(i+=1, end)] - s.p.prompt_prefix = prefix - refresh_multi_line(s, beeping=true) - sleep(blink) - s.beeping -= blink - end - s.p.prompt_prefix = orig_prefix - refresh_multi_line(s, beeping=true) - s.beeping = 0.0 - =# - finally - unlock(s.refresh_lock) + errormonitor(@async begin + trylock(s.refresh_lock) || return + try + # TODO remove after https://github.com/JuliaLang/StyledStrings.jl/pull/17 is merged + loadface!(:repl_prompt_beep => Face(foreground=:shadow, inherit=[:repl_prompt])) + og_prompt = s.p.prompt + beep_prompt = styled"{$beep_face:$(prompt_string(og_prompt))}" + s.p.prompt = beep_prompt + # @timothy: do you have feedback on this approach, or + # guidance for a better one? the issue is that we need to + # override the styling of `s.p.prompt` with :repl_prompt_beep. + # `withfaces` could work, altough that requires overriding all + # of `:repl_prompt_*` faces, and hardcoding those would + # 1) make future modifications slightly more fragile and + # 2) exclude user-defined REPL modes. + refresh_multi_line(s, beeping=true) + while s.beeping > 0.0 + sleep(blink) + s.beeping -= blink end - end) - end + s.p.prompt = og_prompt + refresh_multi_line(s, beeping=true) + s.beeping = 0.0 + finally + unlock(s.refresh_lock) + end + end) nothing end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 1e853b1b3d00c..7766d035f719a 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -492,9 +492,6 @@ mutable struct LineEditREPL <: AbstractREPL function LineEditREPL(t,hascolor,history_file,in_shell,in_help,envcolors) opts = Options() opts.hascolor = hascolor - if !hascolor - opts.beep_colors = [""] - end new(t,hascolor,history_file,in_shell,in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[]) end end diff --git a/stdlib/REPL/src/options.jl b/stdlib/REPL/src/options.jl index 1fb2c654c7df2..73a8c511ca01d 100644 --- a/stdlib/REPL/src/options.jl +++ b/stdlib/REPL/src/options.jl @@ -15,7 +15,7 @@ mutable struct Options beep_duration::Float64 beep_blink::Float64 beep_maxduration::Float64 - beep_colors::Vector{String} + beep_face::Symbol beep_use_current::Bool backspace_align::Bool backspace_adjust::Bool @@ -39,7 +39,7 @@ Options(; kill_ring_max = 100, region_animation_duration = 0.2, beep_duration = 0.2, beep_blink = 0.2, beep_maxduration = 1.0, - beep_colors = ["\e[90m"], # gray (text_colors not yet available) + beep_face = :repl_prompt_beep, beep_use_current = true, backspace_align = true, backspace_adjust = backspace_align, confirm_exit = false, @@ -53,7 +53,7 @@ Options(; Options(hascolor, extra_keymap, tabwidth, kill_ring_max, region_animation_duration, beep_duration, beep_blink, beep_maxduration, - beep_colors, beep_use_current, + beep_face, beep_use_current, backspace_align, backspace_adjust, confirm_exit, auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste, auto_indent_time_threshold, auto_refresh_time_delay, From 763190185438a4585c116c640bf15b30b9f1f330 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 26 Oct 2023 17:19:02 +0100 Subject: [PATCH 11/15] Remove notes file --- stdlib/REPL/src/notes.md | 60 ---------------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 stdlib/REPL/src/notes.md diff --git a/stdlib/REPL/src/notes.md b/stdlib/REPL/src/notes.md deleted file mode 100644 index f874ed424c804..0000000000000 --- a/stdlib/REPL/src/notes.md +++ /dev/null @@ -1,60 +0,0 @@ -Named Colors from `Base.text_colors`: -``` -link \e[5m -light_white \e[97m -italic \e[3m -white \e[37m -light_cyan \e[96m -light_red \e[91m -blue \e[34m -cyan \e[36m -magenta \e[35m -black \e[30m -light_magenta \e[95m -default \e[39m -light_yellow \e[93m -nothing -hidden \e[8m -red \e[31m -light_black \e[90m -light_blue \e[94m -normal \e[0m -light_green \e[92m -green \e[32m -bold \e[1m -yellow \e[33m -reverse \e[7m -underline \e[4m -``` - -- [ ] Input color from `Base.input_color()` -- [ ] Answer color from `Base.answer_color()` - -## PR Questions - -In this PR I chose to modify instances of `String` with `Union{String,AnnotatedString}`, but I'm open to feedback regarding whether another choice is better. Perhaps widening it to `AbstractString` instead. I'm not familiar with how the compiler might handle these differently, etc. - -At present this requires running julia with `--compiled-modules=no` in order for the styled prompt string `const`s to be initialized: - -```julia -const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" -const PKG_PROMPT = # ... etc -``` - -When running with incremental precompilation, the above code causes issues with precompilation because it causes side-effects. I figured other stdlib/base macros might require similar precompilation-navigating logic, but after looking into it I realized that `styled` is rather special because it [calls eval](https://github.com/JuliaLang/StyledStrings.jl/blob/4777e6008108ac3b3408403c08d1e76e2e6f4c80/src/stylemacro.jl#L672). - -I'm sure that the `_PROMPT` consts could be declared in a precompile-friendly way, but I wonder if this could be fixed within the `styled` macro. Maybe there are macros beyond base/stdlib which solve the same problem. - -- [ ] report precompilation issue to `StyledStrings.jl` - -Relevant PRs -https://github.com/JuliaLang/julia/pull/36689 -https://github.com/JuliaLang/julia/pull/46474/ - - -## PR -Btw, you mentioned running into some trouble with `get(io, :color, false)`. Could I get your perspective on the existing color-related logic? Specifically regarding how the `:color` attribute is first initialized, down to it being - -My impression while working around this code is that the color "propagation" throughout the REPL code down to the final `write` to the terminal is quite fragile, and I'm wondering if the complexity is necessary (to reflect a complex. Here are a few examples of the brittleness, and I'd like your input on which direction might be the most appropriate solution: -- It took me quite a while to debug why the final `io::IO` struct in `StyledStrings/src/io.jl#_ansi_writer` never had `:color = true`. It turns out that somewhere in the callstack above in [LineEdit.jl](https://github.com/JuliaLang/julia/blob/c54a3f2b83bfca5f79a1b482abaa9b8e7f0da9ce/stdlib/REPL/src/LineEdit.jl#L617-L622) the soon-to-be-written output is first written to a new `IOBuffer`. So instead of a `UnixTerminal` being passed to `_ansi_writer` (where `:color = true`), an `IOBuffer` is passed, and anything which might behave in a way which would correctly say that `:color = true`, such as a `IOContext`, `REPL.Terminals.UnixTerminal` or other `IO` subtype is lost. -- methods throughout `REPL.jl` and `LineEdit.jl` have a `color` parameter that must be propagated all the way from `REPL.run_frontend` down through `print_response` \ No newline at end of file From 30545b7ae209043f4dfb563a955dfc6d3aa94b87 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Sat, 28 Oct 2023 21:03:16 +0100 Subject: [PATCH 12/15] Clean up some mess --- stdlib/REPL/src/LineEdit.jl | 2 +- stdlib/REPL/src/REPL.jl | 9 --------- stdlib/REPL/src/precompile.jl | 4 ---- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 5e87a6d9055f5..154d6c5c9004d 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -2047,7 +2047,7 @@ end input_string(s::PrefixSearchState) = String(take!(copy(s.response_buffer))) -write_prompt(terminal, s::PrefixSearchState,) = write_prompt(terminal, s.histprompt.parent_prompt) +write_prompt(terminal, s::PrefixSearchState) = write_prompt(terminal, s.histprompt.parent_prompt) prompt_string(s::PrefixSearchState) = prompt_string(s.histprompt.parent_prompt.prompt) terminal(s::PrefixSearchState) = s.terminal diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 7766d035f719a..634f271f01a6e 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1478,19 +1478,10 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef) dopushdisplay = !in(d,Base.Multimedia.displays) dopushdisplay && pushdisplay(d) while !eof(repl.stream)::Bool - # if have_color - # print(repl.stream,repl.prompt_color) - # end print(repl.stream, JULIA_PROMPT) - # if have_color - # print(repl.stream, input_color(repl)) - # end line = readline(repl.stream, keep=true) if !isempty(line) ast = Base.parse_input_line(line) - # if have_color - # print(repl.stream, Base.color_normal) - # end response = eval_with_backend(ast, backend) print_response(repl, response, !ends_with_semicolon(line), have_color) end diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 4c3a8346c5b6f..aad5d282108bc 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -58,10 +58,6 @@ cd("complete_path\t\t$CTRL_C julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) -# const JULIA_PROMPT = "julia> " -# const PKG_PROMPT = "pkg> " -# const SHELL_PROMPT = "shell> " -# const HELP_PROMPT = "help?> " const JULIA_PROMPT = styled"{repl_prompt_julia:julia> }" const PKG_PROMPT = styled"{repl_prompt_pkg:pkg> }" const SHELL_PROMPT = styled"{repl_prompt_shell:shell> }" From 51309a6ce38604343f22b4006012ae0a2f4c80d0 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 2 Nov 2023 17:52:36 +0000 Subject: [PATCH 13/15] remove duplicate statements from merge --- stdlib/REPL/src/LineEdit.jl | 4 +--- stdlib/REPL/src/REPL.jl | 1 - stdlib/REPL/src/precompile.jl | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 154d6c5c9004d..a4ab039d515bb 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -5,9 +5,7 @@ module LineEdit import ..REPL using REPL: AbstractREPL, Options -# TODO remove after https://github.com/JuliaLang/StyledStrings.jl/pull/17 is merged -import StyledStrings: Face, loadface! -import StyledStrings: @styled_str +import StyledStrings: Face, loadface!, @styled_str using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 634f271f01a6e..59186f26ff89a 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -36,7 +36,6 @@ Base.Experimental.@max_methods 1 using Base.Meta, Sockets, StyledStrings import InteractiveUtils -using StyledStrings export AbstractREPL, diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index aad5d282108bc..2c3d16019608e 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -14,7 +14,6 @@ finally Base._track_dependencies[] = true end using Base.Meta -using StyledStrings import Markdown import StyledStrings From dfbf5523d4c544fe1305a2b575f9792c46e81e9d Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 2 Nov 2023 18:23:01 +0000 Subject: [PATCH 14/15] Remove comments --- stdlib/REPL/src/LineEdit.jl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index a4ab039d515bb..2597007e82d6d 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -196,18 +196,9 @@ function beep(s::PromptState, duration::Real=options(s).beep_duration, errormonitor(@async begin trylock(s.refresh_lock) || return try - # TODO remove after https://github.com/JuliaLang/StyledStrings.jl/pull/17 is merged - loadface!(:repl_prompt_beep => Face(foreground=:shadow, inherit=[:repl_prompt])) og_prompt = s.p.prompt beep_prompt = styled"{$beep_face:$(prompt_string(og_prompt))}" s.p.prompt = beep_prompt - # @timothy: do you have feedback on this approach, or - # guidance for a better one? the issue is that we need to - # override the styling of `s.p.prompt` with :repl_prompt_beep. - # `withfaces` could work, altough that requires overriding all - # of `:repl_prompt_*` faces, and hardcoding those would - # 1) make future modifications slightly more fragile and - # 2) exclude user-defined REPL modes. refresh_multi_line(s, beeping=true) while s.beeping > 0.0 sleep(blink) From 25f50f6e7e0d9de3604030de0e03a705e4d0fa39 Mon Sep 17 00:00:00 2001 From: caleb-allen Date: Thu, 2 Nov 2023 18:26:01 +0000 Subject: [PATCH 15/15] Remove some whitespace --- stdlib/REPL/src/Terminals.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index 149811bb96f6a..a014594a91373 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -174,6 +174,4 @@ Base.get(t::TerminalBuffer, key, default) = get(t.out_stream, key, default) Base.peek(t::TerminalBuffer, ::Type{T}) where {T} = error("Not implemented") - - end # module