Skip to content

Commit

Permalink
Add custom Template method
Browse files Browse the repository at this point in the history
This one is not that bad!!!
  • Loading branch information
christopher-dG committed Apr 24, 2020
1 parent 4ed2c09 commit 7620123
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 55 deletions.
56 changes: 3 additions & 53 deletions src/interactive.jl
Original file line number Diff line number Diff line change
Expand Up @@ -134,55 +134,9 @@ function prompt(P::Type, ::Type{T}, ::Val{name}) where {T, name}
end
end

function prompt(::Type{Template}, ::Type, ::Val{:julia})
versions = map(format_version, [VERSION; map(v -> VersionNumber(1, v), 0:5)])
push!(sort!(unique!(versions)), "Other")
menu = RadioMenu(map(string, versions))
println("Select minimum Julia version:")
idx = request(menu)
return if idx == lastindex(versions)
fallback_prompt(VersionNumber, :julia)
else
VersionNumber(versions[idx])
end
end

function prompt(::Type{Template}, ::Type, ::Val{:host})
hosts = ["github.com", "gitlab.com", "bitbucket.org", "Other"]
menu = RadioMenu(hosts)
println("Select Git repository hosting service:")
idx = request(menu)
return if idx == lastindex(hosts)
fallback_prompt(String, :host)
else
hosts[idx]
end
end

function prompt(::Type{Template}, ::Type, ::Val{:plugins})
options = concretes(Plugin)
menu = MultiSelectMenu(map(string, options))
println("Select plugins:")
types = sort!(collect(request(menu)))
return map(interactive, options[types])
end

function prompt(::Type{Template}, ::Type, ::Val{:disable_defaults})
options = map(typeof, default_plugins())
menu = MultiSelectMenu(map(string, options))
println("Select default plugins to disable:")
types = sort!(collect(request(menu)))
return options[types]
end

# Call the default prompt method even if a specialized one exists.
function fallback_prompt(::Type{T}, name::Symbol) where T
return invoke(
prompt,
Tuple{Type{Plugin}, Type{T}, Val{name}},
Plugin, T, Val(name),
)
end
# Compute all the concrete subtypes of T.
concretes_rec(T::Type) = isabstracttype(T) ? vcat(map(concretes_rec, subtypes(T))...) : Any[T]
concretes(T::Type) = sort!(concretes_rec(T); by=nameof)

# Compute name => type pairs for T's interactive options.
function interactive_pairs(::Type{T}) where T <: TemplateOrPlugin
Expand All @@ -197,10 +151,6 @@ function interactive_pairs(::Type{T}) where T <: TemplateOrPlugin
return Vector{Pair{Symbol, Type}}(pairs)
end

# Compute all the concrete subtypes of T.
concretes_rec(T::Type) = isabstracttype(T) ? vcat(map(concretes_rec, subtypes(T))...) : Any[T]
concretes(T::Type) = sort!(concretes_rec(T); by=nameof)

# unique!(f, xs) added here: https://github.com/JuliaLang/julia/pull/30141
if VERSION >= v"1.1"
const uniqueby! = unique!
Expand Down
87 changes: 85 additions & 2 deletions src/template.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ struct Template
end

Template(; interactive::Bool=false, kwargs...) = Template(Val(interactive); kwargs...)
Template(::Val{true}) = interactive(Template)
Template(::Val{true}; kwargs...) = interactive(Template; kwargs...)

function Template(::Val{false}; kwargs...)
kwargs = Dict(kwargs)
Expand Down Expand Up @@ -167,11 +167,94 @@ getkw!(kwargs, k) = pop!(kwargs, k, defaultkw(Template, k))
# Default Template keyword values.
defaultkw(::Type{T}, s::Symbol) where T = defaultkw(T, Val(s))
defaultkw(::Type{Template}, ::Val{:authors}) = default_authors()
defaultkw(::Type{Template}, ::Val{:dir}) = Pkg.devdir()
defaultkw(::Type{Template}, ::Val{:dir}) = contractuser(Pkg.devdir())
defaultkw(::Type{Template}, ::Val{:disable_defaults}) = DataType[]
defaultkw(::Type{Template}, ::Val{:host}) = "github.com"
defaultkw(::Type{Template}, ::Val{:julia}) = default_version()
defaultkw(::Type{Template}, ::Val{:plugins}) = Plugin[]
defaultkw(::Type{Template}, ::Val{:user}) = default_user()

customizable(::Type{Template}) = (:disable_defaults => Vector{DataType},)

function interactive(::Type{Template}; kwargs...)
# If the user supplied any keywords themselves, don't prompt for them.
kwargs = Dict{Symbol, Any}(kwargs)
options = [:user, :authors, :dir, :host, :julia, :plugins]
customizable = setdiff(options, keys(kwargs))

# Make sure we don't try to show a menu with < 2 options.
isempty(customizable) && return Template(; kwargs...)
just_one = length(customizable) == 1
just_one && push(customizable, "None")

println("Template keywords to customize:")
menu = MultiSelectMenu(map(string, customizable))
customize = customizable[sort!(collect(request(menu)))]
just_one && lastindex(customizable) in customize && return Template(; kwargs...)

# Prompt for each keyword.
foreach(k -> kwargs[k] = prompt(Template, fieldtype(Template, k), k), customize)

# We didn't include :disable_defaults above.
# Instead, the :plugins prompt pre-selected default plugins,
# so any default plugins that were explicitly excluded from the user's selection
# should be disabled.
if :plugins in customize && !haskey(kwargs, :disable_defaults)
plugin_types = map(typeof, kwargs[:plugins])
kwargs[:disable_defaults] = DataType[]
foreach(map(typeof, default_plugins())) do T
T in plugin_types || push!(kwargs[:disable_defaults], T)
end
end
return Template(; kwargs...)
end

function prompt(::Type{Template}, ::Type, ::Val{:julia})
versions = map(format_version, [VERSION; map(v -> VersionNumber(1, v), 0:5)])
push!(sort!(unique!(versions)), "Other")
menu = RadioMenu(map(string, versions))
println("Select minimum Julia version:")
idx = request(menu)
return if idx == lastindex(versions)
fallback_prompt(VersionNumber, :julia)
else
VersionNumber(versions[idx])
end
end

function prompt(::Type{Template}, ::Type, ::Val{:host})
hosts = ["github.com", "gitlab.com", "bitbucket.org", "Other"]
menu = RadioMenu(hosts)
println("Select Git repository hosting service:")
idx = request(menu)
return if idx == lastindex(hosts)
fallback_prompt(String, :host)
else
hosts[idx]
end
end

const CRLF = "\r\n"
const DOWN = "\eOB"

function prompt(::Type{Template}, ::Type, ::Val{:plugins})
defaults = map(typeof, default_plugins())
ndefaults = length(defaults)
# Put the defaults first.
options = unique!([defaults; concretes(Plugin)])
menu = MultiSelectMenu(map(T -> string(nameof(T)), options); pagesize=length(options))
println("Select plugins:")
# Pre-select the default plugins and move the cursor to the first non-default.
print(stdin.buffer, (CRLF * DOWN)^ndefaults)
types = sort!(collect(request(menu)))
return map(interactive, options[types])
end

# Call the default prompt method even if a specialized one exists.
function fallback_prompt(::Type{T}, name::Symbol) where T
return invoke(
prompt,
Tuple{Type{Plugin}, Type{T}, Val{name}},
Plugin, T, Val(name),
)
end

0 comments on commit 7620123

Please sign in to comment.