Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REPL: Fully populate the dummy Pkg prompt #54759

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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