Skip to content

Commit

Permalink
REPL: Fully populate the dummy Pkg prompt (#54759)
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth authored Jun 11, 2024
1 parent 68fe512 commit 3fc3577
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 14 deletions.
121 changes: 121 additions & 0 deletions stdlib/REPL/src/Pkg_beforeload.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
## Pkg stuff needed before Pkg has loaded

const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")
const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt")

function load_pkg()
@lock Base.require_lock begin
REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt")
# require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async
# but we need to wait for the repl mode to be set up
lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing)
lock !== nothing && wait(lock[2])
return REPLExt
end
end

## Below here copied/tweaked from Pkg Types.jl so that the dummy Pkg prompt
# can populate the env correctly before Pkg loads

function safe_realpath(path)
isempty(path) && return path
if ispath(path)
try
return realpath(path)
catch
return path
end
end
a, b = splitdir(path)
return joinpath(safe_realpath(a), b)
end

function find_project_file(env::Union{Nothing,String}=nothing)
project_file = nothing
if env isa Nothing
project_file = Base.active_project()
project_file === nothing && return nothing # in the Pkg version these are pkgerrors
elseif startswith(env, '@')
project_file = Base.load_path_expand(env)
project_file === nothing && return nothing
elseif env isa String
if isdir(env)
isempty(readdir(env)) || return nothing
project_file = joinpath(env, Base.project_names[end])
else
project_file = endswith(env, ".toml") ? abspath(env) :
abspath(env, Base.project_names[end])
end
end
@assert project_file isa String &&
(isfile(project_file) || !ispath(project_file) ||
isdir(project_file) && isempty(readdir(project_file)))
return safe_realpath(project_file)
end

function find_root_base_project(start_project::String)
project_file = start_project
while true
base_project_file = Base.base_project(project_file)
base_project_file === nothing && return project_file
project_file = base_project_file
end
end

function relative_project_path(project_file::String, path::String)
# compute path relative the project
# realpath needed to expand symlinks before taking the relative path
return relpath(safe_realpath(abspath(path)), safe_realpath(dirname(project_file)))
end

function projname(project_file::String)
p = Base.TOML.Parser()
Base.TOML.reinit!(p, read(project_file, String); filepath=project_file)
proj = Base.TOML.parse(p)
name = get(proj, "name", nothing)
if name === nothing
name = basename(dirname(project_file))
end
for depot in Base.DEPOT_PATH
envdir = joinpath(depot, "environments")
if startswith(abspath(project_file), abspath(envdir))
return "@" * name
end
end
return name
end

prev_project_file = nothing
prev_project_timestamp = nothing
prev_prefix = ""

function Pkg_promptf()
global prev_project_timestamp, prev_prefix, prev_project_file
project_file = find_project_file()
prefix = ""
if project_file !== nothing
if prev_project_file == project_file && prev_project_timestamp == mtime(project_file)
prefix = prev_prefix
else
project_name = projname(project_file)
if project_name !== nothing
root = find_root_base_project(project_file)
rootname = projname(root)
if root !== project_file
path_prefix = "/" * dirname(relative_project_path(root, project_file))
else
path_prefix = ""
end
if textwidth(rootname) > 30
rootname = first(rootname, 27) * "..."
end
prefix = "($(rootname)$(path_prefix)) "
prev_prefix = prefix
prev_project_timestamp = mtime(project_file)
prev_project_file = project_file
end
end
end
# Note no handling of Pkg.offline, as the Pkg version does here
return "$(prefix)pkg> "
end
17 changes: 3 additions & 14 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ using .REPLCompletions
include("TerminalMenus/TerminalMenus.jl")
include("docview.jl")

include("Pkg_beforeload.jl")

@nospecialize # use only declared type signatures

answer_color(::AbstractREPL) = ""
Expand Down Expand Up @@ -1082,19 +1084,6 @@ setup_interface(
extra_repl_keymap::Any = repl.options.extra_keymap
) = setup_interface(repl, hascolor, extra_repl_keymap)

const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")
const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt")

function load_pkg()
@lock Base.require_lock begin
REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt")
# require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async
# but we need to wait for the repl mode to be set up
lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing)
lock !== nothing && wait(lock[2])
return REPLExt
end
end

# This non keyword method can be precompiled which is important
function setup_interface(
Expand Down Expand Up @@ -1173,7 +1162,7 @@ function setup_interface(

# Set up dummy Pkg mode that will be replaced once Pkg is loaded
# use 6 dots to occupy the same space as the most likely "@v1.xx" env name
dummy_pkg_mode = Prompt("(......) $PKG_PROMPT",
dummy_pkg_mode = Prompt(Pkg_promptf,
prompt_prefix = hascolor ? repl.pkg_color : "",
prompt_suffix = hascolor ?
(repl.envcolors ? Base.input_color : repl.input_color) : "",
Expand Down

0 comments on commit 3fc3577

Please sign in to comment.