Skip to content

Commit d3d695b

Browse files
00vareladavidKristofferC
authored andcommitted
Refactor some REPL issues (#552)
* Refactor: APIOptions should be a dictionary * Fix `do_activate!` interface * Update tests: APIOptions is a dictionary * Allow more flexibility for REPL `do_<>` functions
1 parent e039738 commit d3d695b

File tree

3 files changed

+68
-77
lines changed

3 files changed

+68
-77
lines changed

stdlib/Pkg/src/REPLMode.jl

+53-64
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ end
1919
###########
2020
# Options #
2121
###########
22-
#TODO should this opt be removed: ("name", :cmd, :temp => false)
2322
struct OptionSpec
2423
name::String
2524
short_name::Union{Nothing,String}
@@ -101,15 +100,15 @@ struct ArgSpec
101100
end
102101
const CommandDeclaration = Tuple{CommandKind,
103102
Vector{String}, # names
104-
Function, # handler
103+
Union{Nothing,Function}, # handler
105104
Tuple{ArgClass, Vector{Int}}, # argument count
106105
Vector{OptionDeclaration}, # options
107106
Union{Nothing, Markdown.MD}, #help
108107
}
109108
struct CommandSpec
110109
kind::CommandKind
111110
names::Vector{String}
112-
handler::Function
111+
handler::Union{Nothing,Function}
113112
argument_spec::ArgSpec # note: just use range operator for max/min
114113
option_specs::Dict{String, OptionSpec}
115114
help::Union{Nothing, Markdown.MD}
@@ -311,7 +310,6 @@ end
311310
##############
312311
const Token = Union{String, VersionRange, Rev}
313312
const PkgArguments = Union{Vector{String}, Vector{PackageSpec}}
314-
#TODO embed spec in PkgCommand?
315313
struct PkgCommand
316314
meta_options::Vector{Option}
317315
spec::CommandSpec
@@ -321,14 +319,14 @@ struct PkgCommand
321319
PkgCommand(meta_opts, cmd_name, opts, args) = new(meta_opts, cmd_name, opts, args)
322320
end
323321

324-
const APIOption = Pair{Symbol, Any}
325-
APIOptions(command::PkgCommand)::Vector{APIOption} =
322+
const APIOptions = Dict{Symbol, Any}
323+
APIOptions(command::PkgCommand)::Dict{Symbol, Any} =
326324
APIOptions(command.options, command.spec.option_specs)
327325

328326
function APIOptions(options::Vector{Option},
329327
specs::Dict{String, OptionSpec},
330-
)::Vector{APIOption}
331-
return map(options) do opt
328+
)::Dict{Symbol, Any}
329+
keyword_vec = map(options) do opt
332330
spec = specs[opt.val]
333331
# opt is switch
334332
spec.is_switch && return spec.api
@@ -337,18 +335,9 @@ function APIOptions(options::Vector{Option},
337335
# given opt wrapper
338336
return spec.api.first => spec.api.second(opt.argument)
339337
end
338+
return Dict(keyword_vec)
340339
end
341340

342-
function key_api(key::Symbol, api_opts::Vector{APIOption})
343-
index = findfirst(x->x.first == key, api_opts)
344-
if index !== nothing
345-
return api_opts[index].second
346-
end
347-
end
348-
349-
set_default!(opt, api_opts::Vector{APIOption}) =
350-
key_api(opt.first, api_opts) === nothing && push!(api_opts, opt)
351-
352341
function enforce_argument_order(args::Vector{Token})
353342
prev_arg = nothing
354343
function check_prev_arg(valid_type::DataType, error_message::AbstractString)
@@ -496,6 +485,8 @@ function PkgCommand(statement::Statement)::PkgCommand
496485
return PkgCommand(meta_opts, statement.command, opts, args)
497486
end
498487

488+
Context!(ctx::APIOptions)::Context = Types.Context!(collect(ctx))
489+
499490
#############
500491
# Execution #
501492
#############
@@ -519,15 +510,14 @@ function do_cmd(repl::REPL.AbstractREPL, input::String; do_rethrow=false)
519510
end
520511

521512
function do_cmd!(command::PkgCommand, repl)
522-
meta_opts = APIOptions(command.meta_options, meta_option_specs)
523-
ctx = Context(meta_opts...)
513+
context = APIOptions(command.meta_options, meta_option_specs)
524514
spec = command.spec
525515

526516
# REPL specific commands
527517
if spec.kind == CMD_HELP
528-
return Base.invokelatest(do_help!, ctx, command, repl)
518+
return Base.invokelatest(do_help!, command, repl)
529519
elseif spec.kind == CMD_PREVIEW
530-
ctx.preview = true
520+
context[:preview] = true
531521
cmd = command.arguments[1]
532522
cmd_spec = get(command_specs, cmd, nothing)
533523
cmd_spec === nothing &&
@@ -538,10 +528,15 @@ function do_cmd!(command::PkgCommand, repl)
538528

539529
# API commands
540530
# TODO is invokelatest still needed?
541-
Base.invokelatest(spec.handler, ctx, command.arguments, APIOptions(command))
531+
api_opts = APIOptions(command)
532+
if applicable(spec.handler, context, command.arguments, api_opts)
533+
Base.invokelatest(spec.handler, context, command.arguments, api_opts)
534+
else
535+
Base.invokelatest(spec.handler, command.arguments, api_opts)
536+
end
542537
end
543538

544-
function do_help!(ctk::Context, command::PkgCommand, repl::REPL.AbstractREPL)
539+
function do_help!(command::PkgCommand, repl::REPL.AbstractREPL)
545540
disp = REPL.REPLDisplay(repl)
546541
if isempty(command.arguments)
547542
Base.display(disp, help)
@@ -562,82 +557,76 @@ function do_help!(ctk::Context, command::PkgCommand, repl::REPL.AbstractREPL)
562557
end
563558

564559
# TODO set default Display.status keyword: mode = PKGMODE_COMBINED
565-
function do_status!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
566-
set_default!(:mode => PKGMODE_COMBINED, api_opts)
567-
Display.status(ctx, key_api(:mode, api_opts))
568-
end
569-
570-
# TODO remove the need to specify a handler function (not needed for REPL commands)
571-
do_preview!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) = nothing
560+
do_status!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
561+
Display.status(Context!(ctx), get(api_opts, :mode, PKGMODE_COMBINED))
572562

573563
# TODO , test recursive dependencies as on option.
574-
function do_test!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
564+
function do_test!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
575565
foreach(arg -> arg.mode = PKGMODE_MANIFEST, args)
576-
API.test(ctx, args; api_opts...)
566+
API.test(Context!(ctx), args; collect(api_opts)...)
577567
end
578568

579-
function do_registry_add!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
569+
function do_registry_add!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
580570
println("This is a dummy function for now")
581571
println("My args are:")
582572
for arg in args
583573
println("- $arg")
584574
end
585575
end
586576

587-
do_precompile!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
588-
API.precompile(ctx)
577+
do_precompile!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
578+
API.precompile(Context!(ctx))
589579

590-
do_resolve!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
591-
API.resolve(ctx)
580+
do_resolve!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
581+
API.resolve(Context!(ctx))
592582

593-
do_gc!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
594-
API.gc(ctx; api_opts...)
583+
do_gc!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
584+
API.gc(Context!(ctx); collect(api_opts)...)
595585

596-
do_instantiate!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
597-
API.instantiate(ctx; api_opts...)
586+
do_instantiate!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
587+
API.instantiate(Context!(ctx); collect(api_opts)...)
598588

599-
do_generate!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
600-
API.generate(ctx, args[1])
589+
do_generate!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
590+
API.generate(Context!(ctx), args[1])
601591

602-
do_build!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
603-
API.build(ctx, args; api_opts...)
592+
do_build!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
593+
API.build(Context!(ctx), args; collect(api_opts)...)
604594

605-
do_rm!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
606-
API.rm(ctx, args; api_opts...)
595+
do_rm!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
596+
API.rm(Context!(ctx), args; collect(api_opts)...)
607597

608-
do_free!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
609-
API.free(ctx, args; api_opts...)
598+
do_free!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
599+
API.free(Context!(ctx), args; collect(api_opts)...)
610600

611-
do_up!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption}) =
612-
API.up(ctx, args; api_opts...)
601+
do_up!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
602+
API.up(Context!(ctx), args; collect(api_opts)...)
613603

614-
function do_activate!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
615-
# TODO: Remove the ctx argument to this function.
604+
function do_activate!(args::PkgArguments, api_opts::APIOptions)
616605
if isempty(args)
617606
return API.activate(nothing)
618607
else
619608
return API.activate(args[1])
620609
end
621610
end
622611

623-
function do_pin!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
612+
function do_pin!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
624613
for arg in args
625614
# TODO not sure this is correct
626615
if arg.version.ranges[1].lower != arg.version.ranges[1].upper
627616
cmderror("pinning a package requires a single version, not a versionrange")
628617
end
629618
end
630-
API.pin(ctx, args; api_opts...)
619+
API.pin(Context!(ctx), args; collect(api_opts)...)
631620
end
632621

633-
function do_add!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
634-
push!(api_opts, :mode => :add)
635-
API.add_or_develop(ctx, args; api_opts...)
622+
function do_add!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
623+
api_opts[:mode] = :add
624+
API.add_or_develop(Context!(ctx), args; collect(api_opts)...)
636625
end
637626

638-
function do_develop!(ctx::Context, args::PkgArguments, api_opts::Vector{APIOption})
639-
push!(api_opts, :mode => :develop)
640-
API.add_or_develop(ctx, args; api_opts...)
627+
function do_develop!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
628+
api_opts[:mode] = :develop
629+
API.add_or_develop(Context!(ctx), args; collect(api_opts)...)
641630
end
642631

643632
######################
@@ -950,7 +939,7 @@ julia is started with `--startup-file=yes`.
950939
""",
951940
),( CMD_HELP,
952941
["help", "?"],
953-
do_help!,
942+
nothing,
954943
(ARG_RAW, []),
955944
[],
956945
md"""
@@ -1201,7 +1190,7 @@ Deletes packages that cannot be reached from any existing environment.
12011190
""",
12021191
),( CMD_PREVIEW,
12031192
["preview"],
1204-
do_preview!,
1193+
nothing,
12051194
(ARG_RAW, [1]),
12061195
[],
12071196
md"""

stdlib/Pkg/src/Types.jl

+8
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,14 @@ Base.@kwdef mutable struct Context
358358
old_pkg2_clone_name::String = ""
359359
end
360360

361+
function Context!(kw_context::Vector{Pair{Symbol,Any}})::Context
362+
ctx = Context()
363+
for (k, v) in kw_context
364+
setfield!(ctx, k, v)
365+
end
366+
return ctx
367+
end
368+
361369
function Context!(ctx::Context; kwargs...)
362370
for (k, v) in kwargs
363371
setfield!(ctx, k, v)

stdlib/Pkg/test/repl.jl

+7-13
Original file line numberDiff line numberDiff line change
@@ -785,26 +785,20 @@ end
785785
Pkg.REPLMode.Option("rawnum", "5"),
786786
], specs)
787787

788-
@test Pkg.REPLMode.key_api(:foo, api_opts) === nothing
789-
@test Pkg.REPLMode.key_api(:mode, api_opts) == Pkg.Types.PKGMODE_MANIFEST
790-
@test Pkg.REPLMode.key_api(:level, api_opts) == Pkg.Types.UPLEVEL_PATCH
791-
@test Pkg.REPLMode.key_api(:num, api_opts) == "5"
788+
@test get(api_opts,:foo,nothing) === nothing
789+
@test get(api_opts,:mode,nothing) == Pkg.Types.PKGMODE_MANIFEST
790+
@test get(api_opts,:level,nothing) == Pkg.Types.UPLEVEL_PATCH
791+
@test get(api_opts,:num,nothing) == "5"
792792

793793
api_opts = Pkg.REPLMode.APIOptions([
794794
Pkg.REPLMode.Option("project"),
795795
Pkg.REPLMode.Option("patch"),
796796
Pkg.REPLMode.Option("plus", "5"),
797797
], specs)
798798

799-
@test Pkg.REPLMode.key_api(:mode, api_opts) == Pkg.Types.PKGMODE_PROJECT
800-
@test Pkg.REPLMode.key_api(:level, api_opts) == Pkg.Types.UPLEVEL_PATCH
801-
@test Pkg.REPLMode.key_api(:num, api_opts) == 6
802-
803-
@test Pkg.REPLMode.key_api(:foo, api_opts) === nothing
804-
Pkg.REPLMode.set_default!(:foo => "bar", api_opts)
805-
@test Pkg.REPLMode.key_api(:foo, api_opts) == "bar"
806-
Pkg.REPLMode.set_default!(:level => "bar", api_opts)
807-
@test Pkg.REPLMode.key_api(:level, api_opts) == Pkg.Types.UPLEVEL_PATCH
799+
@test get(api_opts,:mode,nothing) == Pkg.Types.PKGMODE_PROJECT
800+
@test get(api_opts,:level,nothing) == Pkg.Types.UPLEVEL_PATCH
801+
@test get(api_opts,:num,nothing) == 6
808802
end
809803

810804
@testset "meta option errors" begin

0 commit comments

Comments
 (0)