diff --git a/.gitignore b/.gitignore index 723069de6c..56eead5f18 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ docs/build /Manifest.toml /docs/Manifest.toml +/docs/src/repl.md .DS_Store /test/registries/* diff --git a/Project.toml b/Project.toml index c4793ef282..c03db0d34e 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ desc = "The next-generation Julia package manager." keywords = ["package", "management"] license = "MIT" name = "Pkg" -version = "1.1.2" +version = "1.1.3" uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" [deps] diff --git a/docs/generate.jl b/docs/generate.jl new file mode 100644 index 0000000000..c34a8f3968 --- /dev/null +++ b/docs/generate.jl @@ -0,0 +1,49 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## generate repl-docs ## + +function generate(io, command) + println(io, """ + ```@raw html +
+
+ + $(replace(command, "-" => " ")) + + — + REPL command + . +
+ ``` + ```@eval + using Pkg + Pkg.REPLMode.CommandSpec("$(command)").help + ``` + ```@raw html +
+ ``` + """) +end +function generate() + io = IOBuffer() + println(io, """ + # [**9.** REPL Mode Reference](@id REPL-Mode-Reference) + + This section describes available commands in the Pkg REPL. + The REPL mode is mostly meant for interactive use, + and for non-interactive use it is recommended to use the + "API mode", see [API Reference](@ref API-Reference). + """) + # list commands + println(io, "## `package` commands") + foreach(command -> generate(io, command), ["add", "build", "develop", "free", "generate", "pin", "remove", "test", "update"]) + println(io, "## `registry` commands") + foreach(command -> generate(io, command), ["registry-add", "registry-remove", "registry-status", "registry-update"]) + println(io, "## Other commands") + foreach(command -> generate(io, command), ["activate", "gc", "help", "instantiate", "precompile", "resolve", "status"]) + # write to file + write(joinpath(@__DIR__, "src", "repl.md"), seekstart(io)) + return +end + +generate() diff --git a/docs/make.jl b/docs/make.jl index b80466b96f..26f34dcb48 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,6 +3,8 @@ using Documenter using Pkg +include("generate.jl") + makedocs( modules = [Pkg], sitename = "Pkg.jl", @@ -16,12 +18,13 @@ makedocs( "registries.md", # "faq.md", "glossary.md", + "repl.md", "api.md" ], - versions = ["v#.#", "dev" => "dev"], assets = ["assets/custom.css"], ) deploydocs( repo = "github.com/JuliaLang/Pkg.jl", + versions = ["v#.#", "dev" => "dev"], ) diff --git a/docs/src/creating-packages.md b/docs/src/creating-packages.md index b7a646fc0d..150deaabca 100644 --- a/docs/src/creating-packages.md +++ b/docs/src/creating-packages.md @@ -1,6 +1,6 @@ # **5.** Creating Packages -A package is a project with a `name`, `uuid` and `version` entry in the `Project.toml` file `src/PackageName.jl` file that defines the module `PackageName`. +A package is a project with a `name`, `uuid` and `version` entry in the `Project.toml` file, and a `src/PackageName.jl` file that defines the module `PackageName`. This file is executed when the package is loaded. ### Generating files for a package diff --git a/docs/src/glossary.md b/docs/src/glossary.md index 157a743b46..92d8bbcafb 100644 --- a/docs/src/glossary.md +++ b/docs/src/glossary.md @@ -1,4 +1,4 @@ -# **9.** Glossary +# **8.** Glossary **Project:** a source tree with a standard layout, including a `src` directory for the main body of Julia code, a `test` directory for testing the project, diff --git a/ext/TOML/src/print.jl b/ext/TOML/src/print.jl index a5438f8bca..49e8bf29e2 100644 --- a/ext/TOML/src/print.jl +++ b/ext/TOML/src/print.jl @@ -35,7 +35,7 @@ end printvalue(io::IO, value::AbstractDict; sorted=false) = _print(io, value, sorted=sorted) printvalue(io::IO, value::DateTime; sorted=false) = - Base.print(io, Dates.format(value, "YYYY-mm-ddTHH:MM:SS.sssZ")) + Base.print(io, Dates.format(value, dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) printvalue(io::IO, value::Bool; sorted=false) = Base.print(io, value ? "true" : "false") printvalue(io::IO, value; sorted=false) = diff --git a/src/API.jl b/src/API.jl index e51886260e..c9f65451a4 100644 --- a/src/API.jl +++ b/src/API.jl @@ -18,16 +18,21 @@ preview_info() = printstyled("───── Preview mode ─────\n"; c include("generate.jl") -function check_package_name(x::AbstractString) +function check_package_name(x::AbstractString, mode=nothing) if !(occursin(Pkg.REPLMode.name_re, x)) - pkgerror("$x is not a valid packagename") + message = "$x is not a valid packagename." + if mode !== nothing && any(occursin.(['\\','/'], x)) # maybe a url or a path + message *= "\nThe argument appears to be a URL or path, perhaps you meant " * + "`Pkg.$mode(PackageSpec(url=\"...\"))` or `Pkg.$mode(PackageSpec(path=\"...\"))`." + end + pkgerror(message) end return PackageSpec(x) end add_or_develop(pkg::Union{AbstractString, PackageSpec}; kwargs...) = add_or_develop([pkg]; kwargs...) -add_or_develop(pkgs::Vector{<:AbstractString}; kwargs...) = - add_or_develop([check_package_name(pkg) for pkg in pkgs]; kwargs...) +add_or_develop(pkgs::Vector{<:AbstractString}; mode::Symbol, kwargs...) = + add_or_develop([check_package_name(pkg, mode) for pkg in pkgs]; mode = mode, kwargs...) add_or_develop(pkgs::Vector{PackageSpec}; kwargs...) = add_or_develop(Context(), pkgs; kwargs...) function add_or_develop(ctx::Context, pkgs::Vector{PackageSpec}; mode::Symbol, shared::Bool=true, kwargs...) diff --git a/src/Operations.jl b/src/Operations.jl index f27d86f013..0358c2e459 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -24,7 +24,7 @@ function find_installed(name::String, uuid::UUID, sha1::SHA1) end function load_versions(path::String; include_yanked = false) - toml = parse_toml(path, "Versions.toml") + toml = parse_toml(path, "Versions.toml"; fakeit=true) d = Dict{VersionNumber, SHA1}( VersionNumber(ver) => SHA1(info["git-tree-sha1"]) for (ver, info) in toml if !get(info, "yanked", false) || include_yanked) @@ -380,7 +380,7 @@ function resolve_versions!( for (uuid, ver) in vers uuid in uuids && continue name = registered_name(ctx.env, uuid) - push!(pkgs, PackageSpec(name, uuid, ver)) + push!(pkgs, PackageSpec(;name=name, uuid=uuid, version=ver)) end return vers end @@ -1139,7 +1139,8 @@ function rm(ctx::Context, pkgs::Vector{PackageSpec}) @info "No changes" return end - deps_names = collect(keys(ctx.env.project.deps)) + deps_names = append!(collect(keys(ctx.env.project.deps)), + collect(keys(ctx.env.project.extras))) filter!(ctx.env.project.targets) do (target, deps) !isempty(filter!(in(deps_names), deps)) end diff --git a/src/Pkg.jl b/src/Pkg.jl index e5a3fdcd22..0663c67556 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -88,8 +88,8 @@ a package, also inside that package. ```julia Pkg.add("Example") # Add a package from registry Pkg.add(PackageSpec(name="Example", version="0.3")) # Specify version -Pkg.add(PackageSpec(url="https://github.com/JuliaLang/Example.jl", rev="master")) # From url -Pkg.add(PackageSpec(url="/remote/mycompany/juliapackages/OurPackage"))` # From path (has to be a gitrepo) +Pkg.add(PackageSpec(url="https://github.com/JuliaLang/Example.jl", rev="master")) # From url to remote gitrepo +Pkg.add(PackageSpec(url="/remote/mycompany/juliapackages/OurPackage"))` # From path to local gitrepo ``` See also [`PackageSpec`](@ref). @@ -229,7 +229,7 @@ Pkg.develop("Example") Pkg.develop(PackageSpec(url="https://github.com/JuliaLang/Compat.jl")) # By path -Pkg.develop(PackageSpec(path="MyJuliaPackages/Package.jl") +Pkg.develop(PackageSpec(path="MyJuliaPackages/Package.jl")) ``` See also [`PackageSpec`](@ref) diff --git a/src/REPLMode.jl b/src/REPLMode.jl index 59f0a0ab0e..e274a1b35e 100644 --- a/src/REPLMode.jl +++ b/src/REPLMode.jl @@ -246,7 +246,7 @@ end is_opt(word::AbstractString) = first(word) == '-' -function core_parse(words) +function core_parse(words; only_cmd=false) # prelude statement = Statement() word = nothing @@ -280,6 +280,8 @@ function core_parse(words) end statement.spec = command + only_cmd && return statement, word # hack to hook in `help` command + next_word!() || return statement, word while is_opt(word) @@ -578,6 +580,14 @@ function CommandSpec(command_name::String)::Union{Nothing,CommandSpec} return get(super, m.captures[2], nothing) end +function parse_command(words::Vector{String}) + statement, word = core_parse(words; only_cmd=true) + if statement.super === nothing && statement.spec === nothing + pkgerror("invalid input: `$word` is not a command") + end + return statement.spec === nothing ? statement.super : statement.spec +end + function do_help!(command::PkgCommand, repl::REPL.AbstractREPL) disp = REPL.REPLDisplay(repl) if isempty(command.arguments) @@ -585,17 +595,20 @@ function do_help!(command::PkgCommand, repl::REPL.AbstractREPL) return end help_md = md"" - for arg in command.arguments - spec = CommandSpec(arg) - if spec === nothing - pkgerror("'$arg' does not name a command") + + cmd = parse_command(command.arguments) + if cmd isa String + # gather all helps for super spec `cmd` + all_specs = sort!(unique(values(super_specs[cmd])); + by=(spec->spec.canonical_name)) + for spec in all_specs + isempty(help_md.content) || push!(help_md.content, md"---") + push!(help_md.content, spec.help) end - spec.help === nothing && - pkgerror("Sorry, I don't have any help for the `$arg` command.") - isempty(help_md.content) || - push!(help_md.content, md"---") - push!(help_md.content, spec.help) + elseif cmd isa CommandSpec + push!(help_md.content, cmd.help) end + !isempty(command.arguments) && @warn "More than one command specified, only rendering help for first" Base.display(disp, help_md) end @@ -791,6 +804,14 @@ function canonical_names() return sort!(names) end +function complete_help(options, partial) + names = String[] + for cmds in values(super_specs) + append!(names, [spec.canonical_name for spec in values(cmds)]) + end + return sort!(unique!(append!(names, collect(keys(super_specs))))) +end + function complete_installed_packages(options, partial) env = try EnvCache() catch err @@ -1051,19 +1072,18 @@ julia is started with `--startup-file=yes`. :name => "help", :short_name => "?", :arg_count => 0 => Inf, - :completions => ((opt, partial) -> canonical_names()), + :completions => complete_help, :description => "show this message", :help => md""" help -Display this message. - - help cmd ... +List available commands along with short descriptions. -Display usage information for commands listed. + help cmd -Available commands: `help`, `status`, `add`, `rm`, `up`, `preview`, `gc`, `test`, `build`, `free`, `pin`, `develop`. +If `cmd` is a partial command, display help for all subcommands. +If `cmd` is a full command, display help for `cmd`. """, ],[ :kind => CMD_INSTANTIATE, :name => "instantiate", @@ -1467,7 +1487,7 @@ Multiple commands can be given on the same line by interleaving a `;` between th for command in canonical_names() spec = CommandSpec(command) - push!(help.content, Markdown.parse("`$command`: $(spec.description)")) + push!(help.content, Markdown.parse("`$(replace(command, "-" => " "))`: $(spec.description)")) end end #module diff --git a/src/Types.jl b/src/Types.jl index 4b699fe361..3d4f0b0192 100644 --- a/src/Types.jl +++ b/src/Types.jl @@ -160,7 +160,7 @@ has_uuid(pkg::PackageSpec) = pkg.uuid !== nothing function Base.show(io::IO, pkg::PackageSpec) vstr = repr(pkg.version) - f = ["name" => pkg.name, "uuid" => has_uuid(pkg) ? pkg.uuid : "", "v" => (vstr == "VersionSpec(\"*\")" ? "" : vstr)] + f = ["name" => has_name(pkg) ? pkg.name : "", "uuid" => has_uuid(pkg) ? pkg.uuid : "", "v" => (vstr == "VersionSpec(\"*\")" ? "" : vstr)] if pkg.repo !== nothing if pkg.repo.url !== nothing push!(f, "url/path" => string("\"", pkg.repo.url, "\"")) @@ -1384,9 +1384,9 @@ function registered_uuid(env::EnvCache, name::String)::Union{Nothing,UUID} end # Determine current name for a given package UUID -function registered_name(env::EnvCache, uuid::UUID)::String +function registered_name(env::EnvCache, uuid::UUID)::Union{Nothing,String} names = registered_names(env, uuid) - length(names) == 0 && return "" + length(names) == 0 && return nothing length(names) == 1 && return names[1] values = registered_info(env, uuid, "name") name = nothing diff --git a/test/pkg.jl b/test/pkg.jl index 3482191d01..38e3e0ca4d 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -612,6 +612,18 @@ end end end +#issue #876 +@testset "targets should survive add/rm" begin + temp_pkg_dir() do project_path; cd_tempdir() do tmpdir + cp(joinpath(@__DIR__, "project", "good", "pkg.toml"), "Project.toml") + targets = deepcopy(Pkg.Types.read_project("Project.toml").targets) + Pkg.activate(".") + Pkg.add("Example") + Pkg.rm("Example") + @test targets == Pkg.Types.read_project("Project.toml").targets + end end +end + include("repl.jl") include("api.jl") include("registry.jl")