Skip to content

Commit

Permalink
Introduce special REPL syntax for shared environments
Browse files Browse the repository at this point in the history
  • Loading branch information
00vareladavid committed Dec 10, 2019
1 parent d69f6d7 commit 37a5413
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 31 deletions.
45 changes: 30 additions & 15 deletions src/REPLMode/REPLMode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct CommandSpec
help::Union{Nothing,Markdown.MD}
end

default_parser(xs, options) = unwrap(xs)
function CommandSpec(;name::Union{Nothing,String} = nothing,
short_name::Union{Nothing,String} = nothing,
api::Union{Nothing,Function} = nothing,
Expand All @@ -87,7 +88,7 @@ function CommandSpec(;name::Union{Nothing,String} = nothing,
description::Union{Nothing,String} = nothing,
completions::Union{Nothing,Function} = nothing,
arg_count::Pair = (0=>0),
arg_parser::Function = unwrap,
arg_parser::Function = default_parser,
)::CommandSpec
@assert name !== nothing "Supply a canonical name"
@assert description !== nothing "Supply a description"
Expand Down Expand Up @@ -357,16 +358,16 @@ Final parsing (and checking) step.
This step is distinct from `parse` in that it relies on the command specifications.
"""
function Command(statement::Statement)::Command
# options
opt_spec = statement.spec.option_specs
enforce_option(statement.options, opt_spec)
options = APIOptions(statement.options, opt_spec)
# arguments
arg_spec = statement.spec.argument_spec
arguments = arg_spec.parser(statement.arguments)
arguments = arg_spec.parser(statement.arguments, options)
if !(arg_spec.count.first <= length(arguments) <= arg_spec.count.second)
pkgerror("Wrong number of arguments")
end
# options
opt_spec = statement.spec.option_specs
enforce_option(statement.options, opt_spec)
options = APIOptions(statement.options, opt_spec)
return Command(statement.spec, options, arguments)
end

Expand Down Expand Up @@ -476,6 +477,26 @@ prev_project_file = nothing
prev_project_timestamp = nothing
prev_prefix = ""

function projname(project_file::String)
project = try
Types.read_project(project_file)
catch
nothing
end
if project === nothing || project.name === nothing
name = basename(dirname(project_file))
else
name = project.name
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

function promptf()
global prev_project_timestamp, prev_prefix, prev_project_file
project_file = try
Expand All @@ -488,15 +509,9 @@ function promptf()
if prev_project_file == project_file && prev_project_timestamp == mtime(project_file)
prefix = prev_prefix
else
project = try
Types.read_project(project_file)
catch
nothing
end
if project !== nothing
projname = project.name
name = projname !== nothing ? projname : basename(dirname(project_file))
prefix = string("(", name, ") ")
project_name = projname(project_file)
if project_name !== nothing
prefix = string("(", project_name, ") ")
prev_prefix = prefix
prev_project_timestamp = mtime(project_file)
prev_project_file = project_file
Expand Down
22 changes: 18 additions & 4 deletions src/REPLMode/argument_parsers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ..isdir_windows_workaround
"""
Parser for PackageSpec objects.
"""
function parse_package(args::Vector{QString}; add_or_dev=false)::Vector{PackageSpec}
function parse_package(args::Vector{QString}, options; add_or_dev=false)::Vector{PackageSpec}
args::Vector{PackageToken} = map(PackageToken, package_lex(args))
return parse_package_args(args; add_or_dev=add_or_dev)
end
Expand Down Expand Up @@ -95,7 +95,7 @@ end
################
# RegistrySpec #
################
function parse_registry(raw_args::Vector{QString}; add=false)
function parse_registry(raw_args::Vector{QString}, options; add=false)
regs = RegistrySpec[]
foreach(x -> push!(regs, parse_registry(x; add=add)), unwrap(raw_args))
return regs
Expand Down Expand Up @@ -132,8 +132,22 @@ end
#
# # Other
#
function parse_activate(args::Vector{QString})::Vector{String}
return [(x.isquoted ? x.raw : expanduser(x.raw)) for x in args]
function parse_activate(args::Vector{QString}, options)
isempty(args) && return [] # nothing to do
if length(args) == 1
x = first(args)
if x.isquoted
return [x.raw]
end
x = x.raw
if first(x) == '@'
options[:shared] = true
return [x[2:end]]
else
return [expanduser(x)]
end
end
return args # this is currently invalid input for "activate"
end

#
Expand Down
10 changes: 5 additions & 5 deletions src/REPLMode/command_declarations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ julia is started with `--startup-file=yes`.
:short_name => "?",
:api => identity, # dummy API function
:arg_count => 0 => Inf,
:arg_parser => identity,
:arg_parser => ((x,y) -> x),
:completions => complete_help,
:description => "show this message",
:help => md"""
Expand Down Expand Up @@ -86,7 +86,7 @@ as any no-longer-necessary manifest packages due to project package removals.
:api => API.add,
:should_splat => false,
:arg_count => 1 => Inf,
:arg_parser => (x -> parse_package(x; add_or_dev=true)),
:arg_parser => ((x,y) -> parse_package(x,y; add_or_dev=true)),
:option_spec => OptionDeclaration[
[:name => "preserve", :takes_arg => true, :api => :preserve => do_preserve],
],
Expand Down Expand Up @@ -135,7 +135,7 @@ pkg> add Example=7876af07-990d-54b4-ab0e-23690620f79a
:api => API.develop,
:should_splat => false,
:arg_count => 1 => Inf,
:arg_parser => (x -> parse_package(x; add_or_dev=true)),
:arg_parser => ((x,y) -> parse_package(x,y; add_or_dev=true)),
:option_spec => OptionDeclaration[
[:name => "strict", :api => :strict => true],
[:name => "local", :api => :shared => false],
Expand Down Expand Up @@ -274,7 +274,7 @@ packages will not be upgraded at all.
],[ :name => "generate",
:api => API.generate,
:arg_count => 1 => 1,
:arg_parser => x -> map(expanduser, unwrap(x)),
:arg_parser => ((x,y) -> map(expanduser, unwrap(x))),
:description => "generate files for a new project",
:help => md"""
generate pkgname
Expand Down Expand Up @@ -356,7 +356,7 @@ Redoes the changes from the latest [`undo`](@ref).
:api => Registry.add,
:should_splat => false,
:arg_count => 1 => Inf,
:arg_parser => (x -> parse_registry(x; add = true)),
:arg_parser => ((x,y) -> parse_registry(x,y; add = true)),
:description => "add package registries",
:help => md"""
registry add reg...
Expand Down
20 changes: 13 additions & 7 deletions src/REPLMode/completions.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
########################
# Completion Functions #
########################
function complete_activate(options, partial, i1, i2)
function _shared_envs()
possible = String[]
for depot in Base.DEPOT_PATH
envdir = joinpath(depot, "environments")
isdir(envdir) || continue
append!(possible, readdir(envdir))
end
return possible
end

function complete_activate(options, partial, i1, i2)
shared = get(options, :shared, false)
if shared
for depot in Base.DEPOT_PATH
envdir = joinpath(depot, "environments")
isdir(envdir) || continue
append!(possible, readdir(envdir))
end
return possible
return _shared_envs()
elseif !isempty(partial) && first(partial) == '@'
return "@" .* _shared_envs()
else
return complete_local_dir(partial, i1, i2)
end
Expand Down
28 changes: 28 additions & 0 deletions test/new.jl
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,34 @@ end
end end
end

#
# # Activate
#
@testset "activate: repl" begin
isolate(loaded_depot=true) do
Pkg.REPLMode.TEST_MODE[] = true
# - activate shared env
api, args, opts = first(Pkg.pkg"activate --shared Foo")
@test api == Pkg.activate
@test args == "Foo"
@test opts == Dict(:shared => true)
# - activate shared env using special syntax
api, args, opts = first(Pkg.pkg"activate @Foo")
@test api == Pkg.activate
@test args == "Foo"
@test opts == Dict(:shared => true)
# - no arg activate
api, opts = first(Pkg.pkg"activate")
@test api == Pkg.activate
@test isempty(opts)
# - regular activate
api, args, opts = first(Pkg.pkg"activate FooBar")
@test api == Pkg.activate
@test args == "FooBar"
@test isempty(opts)
end
end

#
# # Add
#
Expand Down

0 comments on commit 37a5413

Please sign in to comment.