Skip to content

Commit

Permalink
Make merge and merge! accept more than 2 arguments. (#184)
Browse files Browse the repository at this point in the history
* Make merge and merge! accept more than 2 arguments.

Incidentally,

- add unit tests for `merge` and `copy` for `Options`
- allow `merge!` for options
- add `merge` example to docs

* Cleanup of options part of the manual.

Also add examples for #183.
  • Loading branch information
tpapp authored Aug 31, 2020
1 parent 7709e39 commit f689a00
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 19 deletions.
75 changes: 62 additions & 13 deletions docs/src/man/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Values should be valid Julia expressions, as they are evaluated, so you cannot u

In addition to replacing underscores in keys, the following transformations of values are done when the options are written in `.tex` style:

* A list as a value is written as "comma joined" e.g. `[1, 2, 3] -> "1, 2, 3"`.
* A list as a value is written as comma joined e.g. `[1, 2, 3] -> "1, 2, 3"`.

* A tuple as a value is written with braces delimiting the elements e.g. `(60, 30) -> {60}{30}`

Expand Down Expand Up @@ -142,21 +142,17 @@ julia> print_tex(p)
;
```

You can also merge in options that have been created separately, using `merge!`:
## Working with options

```jldoctest
julia> a = Axis();
Collections of options are first-class objects: they can be used independently of `Plot`, `Axis`, and similar, copied, modified, and merged.

julia> @pgf opts = {xmin = 0, ymax = 1, ybar};
This allows a disciplined approach to working with complex plots: for example, you can create a set of default options for some purpose (eg plots in a research paper, with a style imposed by a journal), and then modify this as needed for individual plots. It is then easy to apply, for example, a “theme” to an axis where the theme is a set of options already saved.

julia> merge!(a, opts);
Another use case is creating orthogonal sets of options, eg one for axis annotations and another one for legends, and merging these as necessary.

julia> print_tex(a)
\begin{axis}[xmin={0}, ymax={1}, ybar]
\end{axis}
```
### Extending and combining options

An alternative to using `merge!` is using `...` to splice an option into another one, e.g.
Use `...` to splice an option into another one, e.g.

```jldoctest
julia> theme = @pgf {xmajorgrids, ymajorgrids};
Expand All @@ -168,13 +164,66 @@ julia> a = Axis(
julia> print_tex(a)
\begin{axis}[xmajorgrids, ymajorgrids, title={Foo}]
\end{axis}
julia> print_tex(theme) # original is not modified
[xmajorgrids, ymajorgrids]
```

You can also `merge` sets of options:
```jldoctest
julia> O1 = @pgf { color = "red" };
julia> O2 = @pgf { dashed };
julia> O3 = @pgf { no_marks };
julia> print_tex(Plot(merge(O1, O2, O3), Table(1:2, 1:2)))
\addplot[color={red}, dashed, no marks]
table[row sep={\\}]
{
\\
1 1 \\
2 2 \\
}
;
```
Again, the value of original options is unchanged above.


### Modifying options

You can modify existing options with `push!`, `append!`, and `merge!`. The first two expect pairs of a string and a value (may be `nothing` for options like `"red"`), and are mostly useful when you are generating options using a function. `merge!` of course accepts options.

It is then easy to apply, for example, a “theme” to an axis where the theme is a set of options already saved.
```jldoctest
julia> opt = @pgf {};
julia> push!(opt, :color => "red", :mark => "x");
julia> append!(opt, [:style => "thick", :mark_options => @pgf { scale = 0.4 }]);
julia> merge!(opt, @pgf { "error bars/y dir=both", "error bars/y explicit" });
julia> print_tex(opt)
[color={red}, mark={x}, style={thick}, mark options={scale={0.4}}, error bars/y dir=both, error bars/y explicit]
```

All containers with options also support using `merge!` directly.

```jldoctest
julia> a = Axis();
julia> @pgf opts = {xmin = 0, ymax = 1, ybar};
julia> merge!(a, opts);
julia> print_tex(a)
\begin{axis}[xmin={0}, ymax={1}, ybar]
\end{axis}
```

## Empty options

Empty options are not printed by default, but printing `[]` can be useful in some cases, eg when combined with global settings `\pgfplotsset{every axis plot/.append style={...}}` in LaTeX code. In order to force printing empty options, it is recommended to use `{}` in expressions like
Empty options are not emitted by default, but using in LaTeX code `[]` can be useful in some cases, eg when combined with global settings `\pgfplotsset{every axis plot/.append style={...}}`. In order to force printing empty options, it is recommended to use `{}` in expressions like

```julia
@pgf Plot({}, ...)
Expand Down
17 changes: 11 additions & 6 deletions src/options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ Base.haskey(o::Options, args...; kwargs...) = haskey(o.dict, args...; kwargs...)

Base.copy(options::Options) = deepcopy(options)

Base.merge(a::Options, b::Options) =
Options(merge(a.dict, b.dict), a.print_empty || b.print_empty)
function Base.merge(options::Options, others::Options...)
args = (options, others...)
Options(mapreduce(opts -> opts.dict, merge, args),
mapreduce(opts -> opts.print_empty, |, args))
end

function prockey(key)
if isa(key, Symbol) || isa(key, String)
Expand Down Expand Up @@ -143,11 +146,13 @@ Base.delete!(o::OptionType, args...; kwargs...) = (delete!(o.options, args...; k

Base.copy(a::OptionType) = deepcopy(a)

function Base.merge!(a::OptionType, options::Options)
for (k, v) in options.dict
a[k] = v
function Base.merge!(options::Union{Options,OptionType}, others::Options...)
for other in others
for (k, v) in other.dict
options[k] = v
end
end
return a
options
end

"""
Expand Down
22 changes: 22 additions & 0 deletions test/test_options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ end
"\\addplot[]\ntable[row sep={\\\\}]\n{\nx \\\\\n1 \\\\\n2 \\\\\n3 \\\\\n}\n;" # note []
end

@testset "operations on options" begin
O1 = @pgf { a = 1 }

# copy
O2 = copy(O1)
@test O1 O2

# merge
@test merge(O1, @pgf { b = 2 }) @pgf { a = 1, b = 2 }
O3 = @pgf { a = 1, b = 2, c = 3 }
@test merge(O1, @pgf({ b = 2 }), @pgf({ c = 3 })) O3

# merge!
@test merge!(O1, @pgf({ b = 2 }), @pgf({ c = 3 })) O1
@test O1 O3

# merge! for OptionType
P = Plot(O2, Table([1], [1])); # don't print, nonsensical
@test merge!(P, @pgf({ b = 2 }), @pgf({ c = 3 })) P
@test P.options O3
end

@testset "options push! and append!" begin
opt1 = "color" => "red"
opt2 = "dashed"
Expand Down

0 comments on commit f689a00

Please sign in to comment.