diff --git a/src/args.jl b/src/args.jl index 82ca09d8..cebc5344 100644 --- a/src/args.jl +++ b/src/args.jl @@ -460,6 +460,29 @@ is_axis_attr_noletter(k) = haskey(_axis_defaults, k) RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k) +const _internal_args = + [:plot_object, :series_plotindex, :markershape_to_add, :letter, :idxfilter] +const _magic_axis_args = [:axis, :tickfont, :guidefont] +const _magic_subplot_args = [:titlefont, :legendfont, :legendtitlefont, ] +const _magic_series_args = [:line, :marker, :fill] + +function is_noletter_attribute(k) + is_axis_attr_noletter(k) && return true + k in _magic_axis_args && return true + return false +end + +function is_default_attribute(k) + k in _internal_args && return true + k in _all_args && return true + is_axis_attr(k) && return true + is_noletter_attribute(k) && return true + k in _magic_subplot_args && return true + k in _magic_series_args && return true + Symbol(chop(string(k); head = 1, tail = 0)) in _magic_axis_args && return true + return false +end + # ----------------------------------------------------------------------------- makeplural(s::Symbol) = Symbol(string(s,"s")) @@ -1017,6 +1040,16 @@ function preprocessArgs!(plotattributes::AKW) end end end + # handle axes guides + if haskey(plotattributes, :guide) + guide = pop!(plotattributes, :guide) + for letter in (:x, :y, :z) + guide_sym = Symbol(letter, :guide) + if !is_explicit(plotattributes, guide_sym) + plotattributes[guide_sym] = guide + end + end + end # handle line args for arg in wraptuple(pop_kw!(plotattributes, :line, ())) diff --git a/src/pipeline.jl b/src/pipeline.jl index 1e9cd991..ef846dd1 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -1,4 +1,26 @@ +# Error for aliases used in recipes +function warn_on_recipe_aliases!(plotattributes, recipe_type, args...) + for k in keys(plotattributes) + if !is_default_attribute(k) + dk = get(_keyAliases, k, k) + if k !== dk + @warn "Attribute alias `$k` detected in the $recipe_type recipe defined for the signature $(signature_string(Val{recipe_type}, args...)). To ensure expected behavior it is recommended to use the default attribute `$dk`." + end + plotattributes[dk] = pop_kw!(plotattributes, k) + end + end +end +warn_on_recipe_aliases!(v::AbstractVector, recipe_type, args) = + foreach(x -> warn_on_recipe_aliases!(x, recipe_type, args), v) +warn_on_recipe_aliases!(rd::RecipeData, recipe_type, args) = + warn_on_recipe_aliases!(rd.plotattributes, recipe_type, args) +function signature_string(::Type{Val{:user}}, args...) + return string("(::", join(string.(typeof.(args)), ", ::"), ")") +end +signature_string(::Type{Val{:type}}, T) = "(::Type{$T}, ::$T)" +signature_string(::Type{Val{:plot}}, st) = "(::Type{Val{:$st}}, ::AbstractPlot)" +signature_string(::Type{Val{:series}}, st) = "(::Type{Val{:$st}}, x, y, z)" # ------------------------------------------------------------------ # preprocessing @@ -82,7 +104,11 @@ function _process_userrecipes(plt::Plot, plotattributes::AKW, args) if isempty(next_series.args) _process_userrecipe(plt, kw_list, next_series) else - rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...) + rd_list = RecipesBase.apply_recipe( + next_series.plotattributes, + next_series.args... + ) + warn_on_recipe_aliases!(rd_list, :user, next_series.args) prepend!(still_to_process,rd_list) end end @@ -184,6 +210,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p st = kw[:seriestype] st = kw[:seriestype] = get(_typeAliases, st, st) datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) + warn_on_recipe_aliases!(datalist, :plot, st) for data in datalist preprocessArgs!(data.plotattributes) if data.plotattributes[:seriestype] == st @@ -408,7 +435,9 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW) else # get a sub list of series for this seriestype - datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z]) + x, y, z = plotattributes[:x], plotattributes[:y], plotattributes[:z] + datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, x, y, z) + warn_on_recipe_aliases!(datalist, :series, st) # assuming there was no error, recursively apply the series recipes for data in datalist diff --git a/src/recipes.jl b/src/recipes.jl index dc94c1a9..79b4176d 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -1261,8 +1261,8 @@ end yflip := true aspect_ratio := 1 rs, cs, zs = findnz(z.surf) - xlim := ignorenan_extrema(cs) - ylim := ignorenan_extrema(rs) + xlims := ignorenan_extrema(cs) + ylims := ignorenan_extrema(rs) if plotattributes[:markershape] == :none markershape := :circle end diff --git a/src/series.jl b/src/series.jl index 725d2f8b..bbb49469 100644 --- a/src/series.jl +++ b/src/series.jl @@ -174,6 +174,7 @@ end function _apply_type_recipe(plotattributes, v, letter) _preprocess_axis_args!(plotattributes, letter) rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v) + warn_on_recipe_aliases!(plotattributes, :type, typeof(v)) _postprocess_axis_args!(plotattributes, letter) return rdvec[1].args[1] end @@ -185,11 +186,13 @@ function _apply_type_recipe(plotattributes, v::AbstractArray, letter) _preprocess_axis_args!(plotattributes, letter) # First we try to apply an array type recipe. w = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1] + warn_on_recipe_aliases!(plotattributes, :type, typeof(v)) # If the type did not change try it element-wise if typeof(v) == typeof(w) isempty(skipmissing(v)) && return Float64[] x = first(skipmissing(v)) args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args + warn_on_recipe_aliases!(plotattributes, :type, typeof(x)) _postprocess_axis_args!(plotattributes, letter) if length(args) == 2 && all(arg -> arg isa Function, args) numfunc, formatter = args @@ -223,9 +226,8 @@ _apply_type_recipe(plotattributes, v::AbstractArray{<:DataPoint}, letter) = v # axis args before type recipes should still be mapped to all axes function _preprocess_axis_args!(plotattributes) - replaceAliases!(plotattributes, _keyAliases) for (k, v) in plotattributes - if haskey(_axis_defaults, k) + if is_noletter_attribute(k) pop!(plotattributes, k) for l in (:x, :y, :z) lk = Symbol(l, k) @@ -243,9 +245,8 @@ end function _postprocess_axis_args!(plotattributes, letter) pop!(plotattributes, :letter) if letter in (:x, :y, :z) - replaceAliases!(plotattributes, _keyAliases) for (k, v) in plotattributes - if haskey(_axis_defaults, k) + if is_noletter_attribute(k) pop!(plotattributes, k) lk = Symbol(letter, k) haskey(plotattributes, lk) || (plotattributes[lk] = v) diff --git a/src/types.jl b/src/types.jl index 4ee8e9e0..a866e59f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -26,8 +26,9 @@ struct Attr <: AbstractDict{Symbol,Any} defaults::KW end -Base.getindex(attr::Attr, k) = haskey(attr.explicit,k) ? - attr.explicit[k] : attr.defaults[k] +function Base.getindex(attr::Attr, k) + return haskey(attr.explicit, k) ? attr.explicit[k] : attr.defaults[k] +end Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(attr.defaults,k) Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default function Base.get!(attr::Attr, k, default) @@ -41,7 +42,7 @@ end function Base.delete!(attr::Attr, k) haskey(attr.explicit, k) && delete!(attr.explicit, k) haskey(attr.defaults, k) && delete!(attr.defaults, k) -end +end Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults))) function Base.iterate(attr::Attr) exp_keys = keys(attr.explicit)