From 6940b681e26070ba659f6226cdfdea48fae19971 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 18 Aug 2021 23:23:13 -0400 Subject: [PATCH] add --compat to status and tab completion --- src/API.jl | 84 +++++++++------------------- src/Operations.jl | 34 +++++++++++ src/Pkg.jl | 17 +----- src/REPLMode/command_declarations.jl | 21 ++++--- src/REPLMode/completions.jl | 12 ++++ test/new.jl | 18 +++--- 6 files changed, 95 insertions(+), 91 deletions(-) diff --git a/src/API.jl b/src/API.jl index f527a80c36..d4da639ad2 100644 --- a/src/API.jl +++ b/src/API.jl @@ -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 @@ -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) @@ -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) @@ -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 # diff --git a/src/Operations.jl b/src/Operations.jl index cdbcec8eec..29b1b9678a 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -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) diff --git a/src/Pkg.jl b/src/Pkg.jl index 417d760101..8876a674a4 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -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) diff --git a/src/REPLMode/command_declarations.jl b/src/REPLMode/command_declarations.jl index d00d40dc2c..575d4d13bd 100644 --- a/src/REPLMode/command_declarations.jl +++ b/src/REPLMode/command_declarations.jl @@ -351,6 +351,7 @@ 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", @@ -358,6 +359,7 @@ PSA[:name => "status", [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 @@ -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. @@ -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", diff --git a/src/REPLMode/completions.jl b/src/REPLMode/completions.jl index 11248a6a2d..62e5bdb91b 100644 --- a/src/REPLMode/completions.jl +++ b/src/REPLMode/completions.jl @@ -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) diff --git a/test/new.jl b/test/new.jl index a66659c063..f68ff920aa 100644 --- a/test/new.jl +++ b/test/new.jl @@ -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)