Skip to content

Commit

Permalink
add --compat to status and tab completion
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth committed Sep 5, 2021
1 parent 0b7d714 commit 6940b68
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 91 deletions.
84 changes: 26 additions & 58 deletions src/API.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1523,8 +1523,13 @@ end

@deprecate status(mode::PackageMode) status(mode=mode)

function status(ctx::Context, pkgs::Vector{PackageSpec}; diff::Bool=false, mode=PKGMODE_PROJECT, outdated::Bool=false, io::IO=stdout, kwargs...)
Operations.status(ctx.env, ctx.registries, pkgs; mode, git_diff=diff, io, outdated)
function status(ctx::Context, pkgs::Vector{PackageSpec}; diff::Bool=false, mode=PKGMODE_PROJECT, outdated::Bool=false, compat::Bool=false, io::IO=stdout, kwargs...)
if compat
(diff || outdated) && pkgerror("Cannot show compat status with `diff` or `outdated` options enabled")
Operations.print_compat(ctx, pkgs; io)
else
Operations.status(ctx.env, ctx.registries, pkgs; mode, git_diff=diff, io, outdated)
end
return nothing
end

Expand Down Expand Up @@ -1609,61 +1614,21 @@ function activate(f::Function, new_project::AbstractString)
end
end

function compat_line(io, pkg, uuid, compat_str, longest_dep_len; indent = " ")
iob = IOBuffer()
ioc = IOContext(iob, :color => get(io, :color, false))
if isnothing(uuid)
print(ioc, "$indent ")
else
printstyled(ioc, "$indent[", string(uuid)[1:8], "] "; color = :light_black)
end
print(ioc, rpad(pkg, longest_dep_len))
if isnothing(compat_str)
printstyled(ioc, " none"; color = :light_black)
else
print(ioc, " ", compat_str)
end
return String(take!(iob))
end

function compat(ctx::Context; io = nothing)
io = something(io, ctx.io)
printpkgstyle(io, :Compat, pathrepr(ctx.env.project_file))
longest_dep_len = reduce(max, map(length, collect(keys(ctx.env.project.deps))))
for (dep, uuid) in ctx.env.project.deps
compat_str = compat(ctx, dep)
println(io, compat_line(io, dep, uuid, compat_str, longest_dep_len))
end
compat_str = compat(ctx, "julia")
println(io, compat_line(io, "julia", nothing, compat_str, longest_dep_len))
end
function compat(ctx::Context, pkg::String)
pkg = pkg == "Julia" ? "julia" : pkg
if haskey(ctx.env.project.deps, pkg) || pkg == "julia"
Operations.get_compat_str(ctx.env.project, pkg)
else
pkgerror("No package named $pkg in current Project")
end
end
compat(pkg::String; kwargs...) = compat(Context(), pkg; kwargs...)
compat(; kwargs...) = compat(Context(); kwargs...)

function compat!(ctx::Context; list = false, io = nothing)
list && return compat(ctx; io)
io = something(io, ctx.io)
can_fancyprint(io) || pkgerror("Pkg.compat! cannot be run interactively in this terminal")
can_fancyprint(io) || pkgerror("Pkg.compat cannot be run interactively in this terminal")
printpkgstyle(io, :Compat, pathrepr(ctx.env.project_file))
longest_dep_len = reduce(max, map(length, collect(keys(ctx.env.project.deps))))
opt_strs = String[]
opt_pkgs = String[]
compat_str = Operations.compat(ctx, "julia")
push!(opt_strs, Operations.compat_line(io, "julia", nothing, compat_str, longest_dep_len, indent = ""))
push!(opt_pkgs, "julia")
for (dep, uuid) in ctx.env.project.deps
compat_str = compat(ctx, dep)
push!(opt_strs, compat_line(io, dep, uuid, compat_str, longest_dep_len, indent = ""))
compat_str = Operations.compat(ctx, dep)
push!(opt_strs, Operations.compat_line(io, dep, uuid, compat_str, longest_dep_len, indent = ""))
push!(opt_pkgs, dep)
end
compat_str = compat(ctx, "julia")
push!(opt_strs, compat_line(io, "julia", nothing, compat_str, longest_dep_len, indent = ""))
push!(opt_pkgs, "julia")
menu = TerminalMenus.RadioMenu(opt_strs, pagesize=length(opt_strs))
choice = try
TerminalMenus.request(" Select an entry to edit:", menu)
Expand All @@ -1676,7 +1641,7 @@ function compat!(ctx::Context; list = false, io = nothing)
end
choice == -1 && return false
dep = opt_pkgs[choice]
current_compat_str = something(compat(ctx, dep), "")
current_compat_str = something(Operations.compat(ctx, dep), "")
resp = try
prompt = " Edit compat entry for $(dep):"
print(io, prompt)
Expand Down Expand Up @@ -1738,27 +1703,30 @@ function compat!(ctx::Context; list = false, io = nothing)
ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), stdin.handle, false)
end
new_entry = strip(resp)
compat!(ctx, dep, string(new_entry))
if isempty(new_entry)
println(io, "\n $(dep) compat entry removed")
else
println(io, "\n $(dep) compat entry set to $new_entry")
end
compat(ctx, dep, string(new_entry))
return
end
function compat!(ctx::Context, pkg::String, compat_str::Union{Nothing,String}; kwargs...)
function compat(ctx::Context, pkg::String, compat_str::Union{Nothing,String}; io = nothing, kwargs...)
io = something(io, ctx.io)
pkg = pkg == "Julia" ? "julia" : pkg
isnothing(compat_str) || (compat_str = string(strip(compat_str, '"')))
if haskey(ctx.env.project.deps, pkg) || pkg == "julia"
success = Operations.set_compat(ctx.env.project, pkg, isnothing(compat_str) ? nothing : isempty(compat_str) ? nothing : compat_str)
success === false && pkgerror("invalid compat version specifier \"$(compat_str)\"")
write_env(ctx.env)
if isnothing(compat_str) || isempty(compat_str)
printpkgstyle(io, :Compat, "Entry removed for $(pkg)")
else
printpkgstyle(io, :Compat, "Entry set\n $(pkg) = $(repr(compat_str))")
end
return
else
pkgerror("No package named $pkg in current Project")
end
end
compat!(pkg::String, compat_str::Union{Nothing,String}; kwargs...) = compat!(Context(), pkg, compat_str; kwargs...)
compat!(;kwargs...) = compat!(Context(); kwargs...)
compat(pkg::String; kwargs...) = compat(pkg, nothing; kwargs...)
compat(pkg::String, compat_str::Union{Nothing,String}; kwargs...) = compat(Context(), pkg, compat_str; kwargs...)
compat(;kwargs...) = compat(Context(); kwargs...)

########
# Undo #
Expand Down
34 changes: 34 additions & 0 deletions src/Operations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,40 @@ function status(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pk
end
end

function compat_line(io, pkg, uuid, compat_str, longest_dep_len; indent = " ")
iob = IOBuffer()
ioc = IOContext(iob, :color => get(io, :color, false))
if isnothing(uuid)
print(ioc, "$indent ")
else
printstyled(ioc, "$indent[", string(uuid)[1:8], "] "; color = :light_black)
end
print(ioc, rpad(pkg, longest_dep_len))
if isnothing(compat_str)
printstyled(ioc, " none"; color = :light_black)
else
print(ioc, " ", compat_str)
end
return String(take!(iob))
end

function print_compat(ctx::Context, pkgs_in::Vector{PackageSpec} = PackageSpec[]; io = nothing)
io = something(io, ctx.io)
printpkgstyle(io, :Compat, pathrepr(ctx.env.project_file))
names = [pkg.name for pkg in pkgs_in]
pkgs = isempty(pkgs_in) ? ctx.env.project.deps : filter(pkg -> in(first(pkg), names), ctx.env.project.deps)
add_julia = isempty(pkgs_in) || any(p->p.name == "julia", pkgs_in)
longest_dep_len = isempty(pkgs) ? length("julia") : max(reduce(max, map(length, collect(keys(pkgs)))), length("julia"))
if add_julia
println(io, compat_line(io, "julia", nothing, get_compat_str(ctx.env.project, "julia"), longest_dep_len))
end
for (dep, uuid) in pkgs
println(io, compat_line(io, dep, uuid, get_compat_str(ctx.env.project, dep), longest_dep_len))
end
end
print_compat(pkg::String; kwargs...) = print_compat(Context(), pkg; kwargs...)
print_compat(; kwargs...) = print_compat(Context(); kwargs...)

function apply_force_latest_compatible_version!(ctx::Types.Context;
target_name = nothing,
allow_earlier_backwards_compatible_versions::Bool = true)
Expand Down
17 changes: 2 additions & 15 deletions src/Pkg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -431,28 +431,15 @@ const status = API.status
"""
Pkg.compat()
Print out the [compat] entries within the current Project.
Pkg.compat(pkg::String)
Return the [compat] entry for a given package within the current Project.
See [`Compatibility`](@ref) for more information on the project [compat] section.
"""
const compat = API.compat

"""
Pkg.compat!()
Interactively edit the [compat] entries within the current Project.
Pkg.compat!(pkg::String, compat::String)
Pkg.compat(pkg::String, compat::String)
Set the [compat] string for the given package within the current Project.
See [`Compatibility`](@ref) for more information on the project [compat] section.
"""
const compat! = API.compat!
const compat = API.compat

"""
Pkg.activate([s::String]; shared::Bool=false, io::IO=stderr)
Expand Down
21 changes: 12 additions & 9 deletions src/REPLMode/command_declarations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,15 @@ PSA[:name => "status",
PSA[:name => "manifest", :short_name => "m", :api => :mode => PKGMODE_MANIFEST],
PSA[:name => "diff", :short_name => "d", :api => :diff => true],
PSA[:name => "outdated", :short_name => "o", :api => :outdated => true],
PSA[:name => "compat", :short_name => "c", :api => :compat => true],
],
:completions => complete_installed_packages,
:description => "summarize contents of and changes to environment",
:help => md"""
[st|status] [-d|--diff] [-o|--outdated] [pkgs...]
[st|status] [-d|--diff] [-o|--outdated] [-p|--project] [pkgs...]
[st|status] [-d|--diff] [-o|--outdated] [-m|--manifest] [pkgs...]
[st|status] [-c|--compat] [pkgs...]
Show the status of the current environment. In `--project` mode (default), the
status of the project file is summarized. In `--manifest` mode the output also
Expand All @@ -367,6 +369,7 @@ The `--diff` option will, if the environment is in a git repository, limit
the output to the difference as compared to the last git commit.
The `--outdated` option in addition show if some packages are not at their latest version
and what packages are holding them back.
The `--compat` option alone shows project compat entries.
!!! compat "Julia 1.1"
`pkg> status` with package arguments requires at least Julia 1.1.
Expand All @@ -376,20 +379,20 @@ and what packages are holding them back.
is the default for environments in git repositories.
!!! compat "Julia 1.8"
The `--outdated` option requires at least Julia 1.8.
The `--outdated` and `--compat` options require at least Julia 1.8.
""",
],
PSA[:name => "compat",
:api => API.compat!,
:option_spec => [
PSA[:name => "list", :short_name => "l", :api => :list => true],
],
:description => "edit or list compat entries in the current Project",
:api => API.compat,
:arg_count => 0 => 2,
:completions => complete_installed_packages_and_compat,
:description => "edit compat entries in the current Project",
:help => md"""
compat [-l|--list]
compat [pkg] [compat_string]
Edit project compat entries via an interactive menu.
Or just list entries by using the `-l` or `--list` option.
Edit project [compat] entries directly, or via an interactive menu by not specifying any arguments.
When directly editing use tab to complete the package name and any existing compat entry.
Specifying a package with a blank compat entry will remove the entry.
""",
],
PSA[:name => "gc",
Expand Down
12 changes: 12 additions & 0 deletions src/REPLMode/completions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ function complete_installed_packages(options, partial)
unique!([entry.name for (uuid, entry) in env.manifest])
end

function complete_installed_packages_and_compat(options, partial)
env = try EnvCache()
catch err
err isa PkgError || rethrow()
return String[]
end
return map(vcat(collect(keys(env.project.deps)), "julia")) do d
compat_str = Operations.get_compat_str(env.project, d)
isnothing(compat_str) ? d : string(d, " ", compat_str)
end
end

function complete_add_dev(options, partial, i1, i2)
comps, idx, _ = complete_local_dir(partial, i1, i2)
if occursin(Base.Filesystem.path_separator_re, partial)
Expand Down
18 changes: 9 additions & 9 deletions test/new.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2192,25 +2192,25 @@ end
isolate(loaded_depot=true) do
Pkg.add("Example")
iob = IOBuffer()
Pkg.compat(io = iob)
Pkg.status(compat=true, io = iob)
output = String(take!(iob))
@test occursin(r"Compat `.+Project.toml`", output)
@test occursin(r"\[7876af07\] *Example *none", output)
@test occursin(r"julia *none", output)

Pkg.compat!("Example", "0.2,0.3")
@test Pkg.compat("Example") == "0.2,0.3"
Pkg.compat(io = iob)
Pkg.compat("Example", "0.2,0.3")
@test Pkg.Operations.get_compat_str(Pkg.Types.Context().env.project, "Example") == "0.2,0.3"
Pkg.status(compat=true, io = iob)
output = String(take!(iob))
@test occursin(r"Compat `.+Project.toml`", output)
@test occursin(r"\[7876af07\] *Example *0.2,0.3", output)
@test occursin(r"julia *none", output)

Pkg.compat!("Example", nothing)
Pkg.compat!("julia", "1.8")
@test Pkg.compat("Example") == nothing
@test Pkg.compat("julia") == "1.8"
Pkg.compat(io = iob)
Pkg.compat("Example", nothing)
Pkg.compat("julia", "1.8")
@test Pkg.Operations.get_compat_str(Pkg.Types.Context().env.project, "Example") == nothing
@test Pkg.Operations.get_compat_str(Pkg.Types.Context().env.project, "julia") == "1.8"
Pkg.status(compat=true, io = iob)
output = String(take!(iob))
@test occursin(r"Compat `.+Project.toml`", output)
@test occursin(r"\[7876af07\] *Example *none", output)
Expand Down

0 comments on commit 6940b68

Please sign in to comment.